-
Notifications
You must be signed in to change notification settings - Fork 0
Pull/3867 #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pull/3867 #7
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package org.opensearch.sql.ast.tree; | ||
|
|
||
| import com.google.common.collect.ImmutableList; | ||
| import java.util.List; | ||
| import lombok.EqualsAndHashCode; | ||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.Setter; | ||
| import lombok.ToString; | ||
| import org.opensearch.sql.ast.AbstractNodeVisitor; | ||
|
|
||
| /** AST node represent Reverse operation. */ | ||
| @Getter | ||
| @Setter | ||
| @ToString | ||
| @EqualsAndHashCode(callSuper = false) | ||
| @RequiredArgsConstructor | ||
| public class Reverse extends UnresolvedPlan { | ||
|
|
||
| private UnresolvedPlan child; | ||
|
|
||
| @Override | ||
| public Reverse attach(UnresolvedPlan child) { | ||
| this.child = child; | ||
| return this; | ||
| } | ||
|
|
||
| @Override | ||
| public List<UnresolvedPlan> getChild() { | ||
| return this.child == null ? ImmutableList.of() : ImmutableList.of(this.child); | ||
| } | ||
|
|
||
| @Override | ||
| public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) { | ||
| return nodeVisitor.visitReverse(this, context); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| ============= | ||
| reverse | ||
| ============= | ||
|
|
||
| .. rubric:: Table of contents | ||
|
|
||
| .. contents:: | ||
| :local: | ||
| :depth: 2 | ||
|
|
||
|
|
||
| Description | ||
| ============ | ||
| | Using ``reverse`` command to reverse the display order of search results. The same results are returned, but in reverse order. | ||
|
|
||
|
|
||
| Syntax | ||
| ============ | ||
| reverse | ||
|
|
||
|
|
||
| * No parameters: The reverse command takes no arguments or options. | ||
|
|
||
|
|
||
| Example 1: Basic reverse operation | ||
| ================================== | ||
|
|
||
| The example shows reversing the order of all documents. | ||
|
|
||
| PPL query:: | ||
|
|
||
| os> source=accounts | fields account_number, age | reverse; | ||
| fetched rows / total rows = 4/4 | ||
| +----------------+-----+ | ||
| | account_number | age | | ||
| |----------------+-----| | ||
| | 6 | 36 | | ||
| | 18 | 33 | | ||
| | 1 | 32 | | ||
| | 13 | 28 | | ||
| +----------------+-----+ | ||
|
|
||
|
|
||
| Example 2: Reverse with sort | ||
| ============================ | ||
|
|
||
| The example shows reversing results after sorting by age in ascending order, effectively giving descending order. | ||
|
|
||
| PPL query:: | ||
|
|
||
| os> source=accounts | sort age | fields account_number, age | reverse; | ||
| fetched rows / total rows = 4/4 | ||
| +----------------+-----+ | ||
| | account_number | age | | ||
| |----------------+-----| | ||
| | 6 | 36 | | ||
| | 18 | 33 | | ||
| | 1 | 32 | | ||
| | 13 | 28 | | ||
| +----------------+-----+ | ||
|
|
||
|
|
||
| Example 3: Reverse with head | ||
| ============================ | ||
|
|
||
| The example shows using reverse with head to get the last 2 records from the original order. | ||
|
|
||
| PPL query:: | ||
|
|
||
| os> source=accounts | reverse | head 2 | fields account_number, age; | ||
| fetched rows / total rows = 2/2 | ||
| +----------------+-----+ | ||
| | account_number | age | | ||
| |----------------+-----| | ||
| | 6 | 36 | | ||
| | 18 | 33 | | ||
| +----------------+-----+ | ||
|
|
||
|
|
||
| Example 4: Double reverse | ||
| ========================= | ||
|
|
||
| The example shows that applying reverse twice returns to the original order. | ||
|
|
||
| PPL query:: | ||
|
|
||
| os> source=accounts | reverse | reverse | fields account_number, age; | ||
| fetched rows / total rows = 4/4 | ||
| +----------------+-----+ | ||
| | account_number | age | | ||
| |----------------+-----| | ||
| | 13 | 28 | | ||
| | 1 | 32 | | ||
| | 18 | 33 | | ||
| | 6 | 36 | | ||
| +----------------+-----+ | ||
|
|
||
|
|
||
| Example 5: Reverse with complex pipeline | ||
| ======================================= | ||
|
|
||
| The example shows reverse working with filtering and field selection. | ||
|
|
||
| PPL query:: | ||
|
|
||
| os> source=accounts | where age > 30 | fields account_number, age | reverse; | ||
| fetched rows / total rows = 3/3 | ||
| +----------------+-----+ | ||
| | account_number | age | | ||
| |----------------+-----| | ||
| | 6 | 36 | | ||
| | 18 | 33 | | ||
| | 1 | 32 | | ||
| +----------------+-----+ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package org.opensearch.sql.calcite.remote; | ||
|
|
||
| import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BANK; | ||
| import static org.opensearch.sql.util.MatcherUtils.rows; | ||
| import static org.opensearch.sql.util.MatcherUtils.schema; | ||
| import static org.opensearch.sql.util.MatcherUtils.verifyDataRowsInOrder; | ||
| import static org.opensearch.sql.util.MatcherUtils.verifySchema; | ||
|
|
||
| import java.io.IOException; | ||
| import org.json.JSONObject; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.opensearch.sql.ppl.PPLIntegTestCase; | ||
|
|
||
| public class CalciteReverseCommandIT extends PPLIntegTestCase { | ||
|
|
||
| @Override | ||
| public void init() throws Exception { | ||
| super.init(); | ||
| enableCalcite(); | ||
| disallowCalciteFallback(); | ||
| loadIndex(Index.BANK); | ||
| } | ||
|
|
||
| @Test | ||
| public void testReverse() throws IOException { | ||
| JSONObject result = executeQuery(String.format("source=%s | fields account_number | reverse", TEST_INDEX_BANK)); | ||
| verifySchema(result, schema("account_number", "bigint")); | ||
| verifyDataRowsInOrder(result, rows(32), rows(25), rows(20), rows(18), rows(13), rows(6), rows(1)); | ||
| } | ||
|
|
||
| @Test | ||
| public void testReverseWithFields() throws IOException { | ||
| JSONObject result = executeQuery(String.format("source=%s | fields account_number, firstname | reverse", TEST_INDEX_BANK)); | ||
| verifySchema(result, schema("account_number", "bigint"), schema("firstname", "string")); | ||
| verifyDataRowsInOrder( | ||
| result, | ||
| rows(32, "Dillard"), | ||
| rows(25, "Virginia"), | ||
| rows(20, "Elinor"), | ||
| rows(18, "Dale"), | ||
| rows(13, "Nanette"), | ||
| rows(6, "Hattie"), | ||
| rows(1, "Amber JOHnny")); | ||
| } | ||
|
|
||
| @Test | ||
| public void testReverseWithSort() throws IOException { | ||
| JSONObject result = executeQuery(String.format("source=%s | sort account_number | fields account_number | reverse", TEST_INDEX_BANK)); | ||
| verifySchema(result, schema("account_number", "bigint")); | ||
| verifyDataRowsInOrder(result, rows(32), rows(25), rows(20), rows(18), rows(13), rows(6), rows(1)); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDoubleReverse() throws IOException { | ||
| JSONObject result = executeQuery(String.format("source=%s | fields account_number | reverse | reverse", TEST_INDEX_BANK)); | ||
| verifySchema(result, schema("account_number", "bigint")); | ||
| verifyDataRowsInOrder(result, rows(1), rows(6), rows(13), rows(18), rows(20), rows(25), rows(32)); | ||
| } | ||
|
|
||
| @Test | ||
| public void testReverseWithHead() throws IOException { | ||
| JSONObject result = executeQuery(String.format("source=%s | fields account_number | reverse | head 3", TEST_INDEX_BANK)); | ||
| verifySchema(result, schema("account_number", "bigint")); | ||
| verifyDataRowsInOrder(result, rows(32), rows(25), rows(20)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -32,6 +32,10 @@ public void onMatch(RelOptRuleCall call) { | |||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Integer limitValue = extractLimitValue(sort.fetch); | ||||||||||||||||||||||||||||||||||||
| Integer offsetValue = extractOffsetValue(sort.offset); | ||||||||||||||||||||||||||||||||||||
| // Skip this rule if this is a reverse operation (indicated by row_number) | ||||||||||||||||||||||||||||||||||||
| if (hasRowNumberFunction(sort)) { | ||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| if (limitValue != null && offsetValue != null) { | ||||||||||||||||||||||||||||||||||||
| CalciteLogicalIndexScan newScan = scan.pushDownLimit(limitValue, offsetValue); | ||||||||||||||||||||||||||||||||||||
| if (newScan != null) { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -40,6 +44,18 @@ public void onMatch(RelOptRuleCall call) { | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Check if the LogicalSort contains a row_number function, which indicates a reverse operation. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param sort The LogicalSort to check | ||||||||||||||||||||||||||||||||||||
| * @return True if a row_number function is found, false otherwise | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private boolean hasRowNumberFunction(LogicalSort sort) { | ||||||||||||||||||||||||||||||||||||
| // Check if the sort has a row_number function in its digest | ||||||||||||||||||||||||||||||||||||
| String digest = sort.getDigest(); | ||||||||||||||||||||||||||||||||||||
| return digest != null && digest.contains("row_number"); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+54
to
+56
|
||||||||||||||||||||||||||||||||||||
| // Check if the sort has a row_number function in its digest | |
| String digest = sort.getDigest(); | |
| return digest != null && digest.contains("row_number"); | |
| // Check collation fields for row_number function | |
| if (sort.getCollation() != null) { | |
| return sort.getCollation().getFieldCollations().stream() | |
| .anyMatch(fieldCollation -> fieldCollation.getFieldName().equalsIgnoreCase("row_number")); | |
| } | |
| // Check for preceding Project node introducing __reverse_row_num__ column | |
| if (sort.getInput() instanceof LogicalProject) { | |
| LogicalProject project = (LogicalProject) sort.getInput(); | |
| return project.getProjects().stream() | |
| .anyMatch(rexNode -> rexNode.toString().contains("__reverse_row_num__")); | |
| } | |
| return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alias 'reverse_row_num' is used as a magic string. Extract this into a shared constant to improve maintainability and avoid typos when referencing it in multiple places.