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_STATE_COUNTRY ;
910import static org .opensearch .sql .legacy .TestsConstants .TEST_INDEX_TIME_DATA ;
1011import static org .opensearch .sql .util .MatcherUtils .rows ;
1112import static org .opensearch .sql .util .MatcherUtils .schema ;
@@ -26,6 +27,7 @@ public void init() throws Exception {
2627 disallowCalciteFallback ();
2728 loadIndex (Index .BANK );
2829 loadIndex (Index .TIME_TEST_DATA );
30+ loadIndex (Index .STATE_COUNTRY );
2931 }
3032
3133 @ Test
@@ -224,4 +226,113 @@ public void testReverseWithTimestampAndExplicitSort() throws IOException {
224226 // Should reverse the value sort, giving us the highest values
225227 verifyDataRowsInOrder (result , rows (9521 , "B" ), rows (9367 , "A" ), rows (9321 , "A" ));
226228 }
229+
230+ @ Test
231+ public void testStreamstatsWithReverse () throws IOException {
232+ // Test that reverse is ignored when used directly after streamstats
233+ // streamstats maintains order via __stream_seq__, but this field is projected out
234+ // and doesn't create a detectable collation, so reverse is ignored (no-op)
235+ JSONObject result =
236+ executeQuery (
237+ String .format (
238+ "source=%s | streamstats count() as cnt, avg(age) as avg | reverse" ,
239+ TEST_INDEX_STATE_COUNTRY ));
240+ verifySchema (
241+ result ,
242+ schema ("name" , "string" ),
243+ schema ("country" , "string" ),
244+ schema ("state" , "string" ),
245+ schema ("month" , "int" ),
246+ schema ("year" , "int" ),
247+ schema ("age" , "int" ),
248+ schema ("cnt" , "bigint" ),
249+ schema ("avg" , "double" ));
250+ // Reverse is ignored, so data remains in original streamstats order
251+ verifyDataRowsInOrder (
252+ result ,
253+ rows ("Jake" , "USA" , "California" , 4 , 2023 , 70 , 1 , 70 ),
254+ rows ("Hello" , "USA" , "New York" , 4 , 2023 , 30 , 2 , 50 ),
255+ rows ("John" , "Canada" , "Ontario" , 4 , 2023 , 25 , 3 , 41.666666666666664 ),
256+ rows ("Jane" , "Canada" , "Quebec" , 4 , 2023 , 20 , 4 , 36.25 ));
257+ }
258+
259+ @ Test
260+ public void testStreamstatsWindowWithReverse () throws IOException {
261+ // Test that reverse is ignored after streamstats with window
262+ JSONObject result =
263+ executeQuery (
264+ String .format (
265+ "source=%s | streamstats window=2 avg(age) as avg | reverse" ,
266+ TEST_INDEX_STATE_COUNTRY ));
267+ verifySchema (
268+ result ,
269+ schema ("name" , "string" ),
270+ schema ("country" , "string" ),
271+ schema ("state" , "string" ),
272+ schema ("month" , "int" ),
273+ schema ("year" , "int" ),
274+ schema ("age" , "int" ),
275+ schema ("avg" , "double" ));
276+ // Reverse is ignored, data remains in original order
277+ // Window=2 means average of current and previous row (sliding window of size 2)
278+ verifyDataRowsInOrder (
279+ result ,
280+ rows ("Jake" , "USA" , "California" , 4 , 2023 , 70 , 70 ),
281+ rows ("Hello" , "USA" , "New York" , 4 , 2023 , 30 , 50 ),
282+ rows ("John" , "Canada" , "Ontario" , 4 , 2023 , 25 , 27.5 ),
283+ rows ("Jane" , "Canada" , "Quebec" , 4 , 2023 , 20 , 22.5 ));
284+ }
285+
286+ @ Test
287+ public void testStreamstatsByWithReverse () throws IOException {
288+ // Test that reverse is ignored after streamstats with partitioning (by clause)
289+ JSONObject result =
290+ executeQuery (
291+ String .format (
292+ "source=%s | streamstats count() as cnt, avg(age) as avg by country | reverse" ,
293+ TEST_INDEX_STATE_COUNTRY ));
294+ verifySchema (
295+ result ,
296+ schema ("name" , "string" ),
297+ schema ("country" , "string" ),
298+ schema ("state" , "string" ),
299+ schema ("month" , "int" ),
300+ schema ("year" , "int" ),
301+ schema ("age" , "int" ),
302+ schema ("cnt" , "bigint" ),
303+ schema ("avg" , "double" ));
304+ // Reverse is ignored, data remains in original order
305+ verifyDataRowsInOrder (
306+ result ,
307+ rows ("Jake" , "USA" , "California" , 4 , 2023 , 70 , 1 , 70 ),
308+ rows ("Hello" , "USA" , "New York" , 4 , 2023 , 30 , 2 , 50 ),
309+ rows ("John" , "Canada" , "Ontario" , 4 , 2023 , 25 , 1 , 25 ),
310+ rows ("Jane" , "Canada" , "Quebec" , 4 , 2023 , 20 , 2 , 22.5 ));
311+ }
312+
313+ @ Test
314+ public void testStreamstatsWithSortThenReverse () throws IOException {
315+ // Test that reverse works when there's an explicit sort after streamstats
316+ // The explicit sort creates a collation that reverse can detect and reverse
317+ JSONObject result =
318+ executeQuery (
319+ String .format (
320+ "source=%s | streamstats count() as cnt | sort age | reverse | head 3" ,
321+ TEST_INDEX_STATE_COUNTRY ));
322+ verifySchema (
323+ result ,
324+ schema ("name" , "string" ),
325+ schema ("country" , "string" ),
326+ schema ("state" , "string" ),
327+ schema ("month" , "int" ),
328+ schema ("year" , "int" ),
329+ schema ("age" , "int" ),
330+ schema ("cnt" , "bigint" ));
331+ // With explicit sort and reverse, data is in descending age order
332+ verifyDataRowsInOrder (
333+ result ,
334+ rows ("Jake" , "USA" , "California" , 4 , 2023 , 70 , 1 ),
335+ rows ("Hello" , "USA" , "New York" , 4 , 2023 , 30 , 2 ),
336+ rows ("John" , "Canada" , "Ontario" , 4 , 2023 , 25 , 3 ));
337+ }
227338}
0 commit comments