66package org .opensearch .sql .calcite .remote ;
77
88import static org .opensearch .sql .legacy .TestsConstants .TEST_INDEX_BANK ;
9+ import static org .opensearch .sql .legacy .TestsConstants .TEST_INDEX_TIME_DATA ;
910import static org .opensearch .sql .util .MatcherUtils .rows ;
1011import static org .opensearch .sql .util .MatcherUtils .schema ;
1112import static org .opensearch .sql .util .MatcherUtils .verifyDataRowsInOrder ;
@@ -24,12 +25,16 @@ public void init() throws Exception {
2425 enableCalcite ();
2526 disallowCalciteFallback ();
2627 loadIndex (Index .BANK );
28+ loadIndex (Index .TIME_TEST_DATA );
2729 }
2830
2931 @ Test
3032 public void testReverse () throws IOException {
3133 JSONObject result =
32- executeQuery (String .format ("source=%s | fields account_number | reverse" , TEST_INDEX_BANK ));
34+ executeQuery (
35+ String .format (
36+ "source=%s | fields account_number | sort account_number | reverse" ,
37+ TEST_INDEX_BANK ));
3338 verifySchema (result , schema ("account_number" , "bigint" ));
3439 verifyDataRowsInOrder (
3540 result , rows (32 ), rows (25 ), rows (20 ), rows (18 ), rows (13 ), rows (6 ), rows (1 ));
@@ -40,7 +45,8 @@ public void testReverseWithFields() throws IOException {
4045 JSONObject result =
4146 executeQuery (
4247 String .format (
43- "source=%s | fields account_number, firstname | reverse" , TEST_INDEX_BANK ));
48+ "source=%s | fields account_number, firstname | sort account_number | reverse" ,
49+ TEST_INDEX_BANK ));
4450 verifySchema (result , schema ("account_number" , "bigint" ), schema ("firstname" , "string" ));
4551 verifyDataRowsInOrder (
4652 result ,
@@ -70,7 +76,8 @@ public void testDoubleReverse() throws IOException {
7076 JSONObject result =
7177 executeQuery (
7278 String .format (
73- "source=%s | fields account_number | reverse | reverse" , TEST_INDEX_BANK ));
79+ "source=%s | fields account_number | sort account_number | reverse | reverse" ,
80+ TEST_INDEX_BANK ));
7481 verifySchema (result , schema ("account_number" , "bigint" ));
7582 verifyDataRowsInOrder (
7683 result , rows (1 ), rows (6 ), rows (13 ), rows (18 ), rows (20 ), rows (25 ), rows (32 ));
@@ -80,7 +87,9 @@ public void testDoubleReverse() throws IOException {
8087 public void testReverseWithHead () throws IOException {
8188 JSONObject result =
8289 executeQuery (
83- String .format ("source=%s | fields account_number | reverse | head 3" , TEST_INDEX_BANK ));
90+ String .format (
91+ "source=%s | fields account_number | sort account_number | reverse | head 3" ,
92+ TEST_INDEX_BANK ));
8493 verifySchema (result , schema ("account_number" , "bigint" ));
8594 verifyDataRowsInOrder (result , rows (32 ), rows (25 ), rows (20 ));
8695 }
@@ -90,7 +99,8 @@ public void testReverseWithComplexPipeline() throws IOException {
9099 JSONObject result =
91100 executeQuery (
92101 String .format (
93- "source=%s | where account_number > 18 | fields account_number | reverse | head 2" ,
102+ "source=%s | where account_number > 18 | fields account_number | sort"
103+ + " account_number | reverse | head 2" ,
94104 TEST_INDEX_BANK ));
95105 verifySchema (result , schema ("account_number" , "bigint" ));
96106 verifyDataRowsInOrder (result , rows (32 ), rows (25 ));
@@ -163,4 +173,55 @@ public void testDoubleReverseWithMixedSortDirections() throws IOException {
163173 rows (6 , "Hattie" ),
164174 rows (1 , "Amber JOHnny" ));
165175 }
176+
177+ @ Test
178+ public void testReverseIgnoredWithoutSortOrTimestamp () throws IOException {
179+ // Test that reverse is ignored when there's no explicit sort and no @timestamp field
180+ // BANK index doesn't have @timestamp, so reverse should be ignored
181+ JSONObject result =
182+ executeQuery (
183+ String .format ("source=%s | fields account_number | reverse | head 3" , TEST_INDEX_BANK ));
184+ verifySchema (result , schema ("account_number" , "bigint" ));
185+ // Without sort or @timestamp, reverse is ignored, so data comes in natural order
186+ // The first 3 documents in natural order (ascending by account_number)
187+ verifyDataRowsInOrder (result , rows (1 ), rows (6 ), rows (13 ));
188+ }
189+
190+ @ Test
191+ public void testReverseWithTimestampField () throws IOException {
192+ // Test that reverse with @timestamp field sorts by @timestamp DESC
193+ // TIME_TEST_DATA index has @timestamp field
194+ JSONObject result =
195+ executeQuery (
196+ String .format (
197+ "source=%s | fields value, category, `@timestamp` | reverse | head 5" ,
198+ TEST_INDEX_TIME_DATA ));
199+ verifySchema (
200+ result ,
201+ schema ("value" , "int" ),
202+ schema ("category" , "string" ),
203+ schema ("@timestamp" , "timestamp" ));
204+ // Should return the latest 5 records (highest @timestamp values) in descending order
205+ // Based on the test data, these are IDs 100, 99, 98, 97, 96
206+ verifyDataRowsInOrder (
207+ result ,
208+ rows (8762 , "A" , "2025-08-01 03:47:41" ),
209+ rows (7348 , "C" , "2025-08-01 02:00:56" ),
210+ rows (9015 , "B" , "2025-08-01 01:14:11" ),
211+ rows (6489 , "D" , "2025-08-01 00:27:26" ),
212+ rows (8676 , "A" , "2025-07-31 23:40:33" ));
213+ }
214+
215+ @ Test
216+ public void testReverseWithTimestampAndExplicitSort () throws IOException {
217+ // Test that explicit sort takes precedence over @timestamp
218+ JSONObject result =
219+ executeQuery (
220+ String .format (
221+ "source=%s | fields value, category | sort value | reverse | head 3" ,
222+ TEST_INDEX_TIME_DATA ));
223+ verifySchema (result , schema ("value" , "int" ), schema ("category" , "string" ));
224+ // Should reverse the value sort, giving us the highest values
225+ verifyDataRowsInOrder (result , rows (9521 , "B" ), rows (9367 , "A" ), rows (9321 , "A" ));
226+ }
166227}
0 commit comments