This is an automated email from the ASF dual-hosted git repository.

yqm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new b85b1cd6a73 Update ordering direction requirement on projection match 
(#18539)
b85b1cd6a73 is described below

commit b85b1cd6a73acb49626bc68cacaf635720cc25d6
Author: Cece Mei <[email protected]>
AuthorDate: Tue Sep 23 13:57:29 2025 -0700

    Update ordering direction requirement on projection match (#18539)
    
    * proj-sorting
    
    * Update 
processing/src/test/java/org/apache/druid/segment/CursorFactoryProjectionTest.java
    
    Co-authored-by: Copilot <[email protected]>
    
    * Update 
processing/src/test/java/org/apache/druid/segment/CursorFactoryProjectionTest.java
    
    Co-authored-by: Copilot <[email protected]>
    
    * format
    
    * test
    
    * ordering
    
    * build
    
    * format
    
    * reverse
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
---
 .../main/java/org/apache/druid/query/OrderBy.java  |  19 +
 .../org/apache/druid/segment/CursorBuildSpec.java  |  24 +-
 .../apache/druid/segment/CursorBuildSpecTest.java  |  12 +
 .../druid/segment/CursorFactoryProjectionTest.java | 382 +++++++++------------
 4 files changed, 211 insertions(+), 226 deletions(-)

diff --git a/processing/src/main/java/org/apache/druid/query/OrderBy.java 
b/processing/src/main/java/org/apache/druid/query/OrderBy.java
index 5cdda9bb46e..8a5ea0d1bc5 100644
--- a/processing/src/main/java/org/apache/druid/query/OrderBy.java
+++ b/processing/src/main/java/org/apache/druid/query/OrderBy.java
@@ -68,6 +68,25 @@ public class OrderBy
     return order;
   }
 
+  /**
+   * Returns true if the given {@link OrderBy} is the exact reverse, meaning 
they have the same column name
+   * in revrersed order.
+   */
+  public boolean isExactReverse(OrderBy that)
+  {
+    if (!columnName.equals(that.columnName)) {
+      return false;
+    }
+    switch (order) {
+      case ASCENDING:
+        return Order.DESCENDING.equals(that.order);
+      case DESCENDING:
+        return Order.ASCENDING.equals(that.order);
+      default:
+        throw new IAE("No order[%s] for column[%s]", order, columnName);
+    }
+  }
+
   @Override
   public boolean equals(Object o)
   {
diff --git 
a/processing/src/main/java/org/apache/druid/segment/CursorBuildSpec.java 
b/processing/src/main/java/org/apache/druid/segment/CursorBuildSpec.java
index 001805ca288..1d7d6b0be1d 100644
--- a/processing/src/main/java/org/apache/druid/segment/CursorBuildSpec.java
+++ b/processing/src/main/java/org/apache/druid/segment/CursorBuildSpec.java
@@ -220,9 +220,12 @@ public class CursorBuildSpec
   }
 
   /**
-   * Returns true if the supplied ordering matches {@link 
#getPreferredOrdering()}, meaning that the supplied ordering
-   * has everything which is in the preferred ordering in the same direction 
and order. The supplied ordering may have
-   * additional columns beyond the preferred ordering and still satisify this 
method.
+   * Returns true if the given ordering is compatible with {@link 
#getPreferredOrdering()}. This means that, for every
+   * column in the preferred ordering, the supplied ordering must either:
+   * <li> use the same direction, or
+   * <li> use the exact opposite direction.
+   * <p>
+   * The supplied ordering may also include extra columns beyond those in the 
preferred ordering and still satisfy this condition.
    */
   public boolean isCompatibleOrdering(List<OrderBy> ordering)
   {
@@ -234,8 +237,18 @@ public class CursorBuildSpec
     if (ordering.size() < preferredOrdering.size()) {
       return false;
     }
-    for (int i = 0; i < preferredOrdering.size(); i++) {
-      if (!ordering.get(i).equals(preferredOrdering.get(i))) {
+
+    boolean exactMatch = ordering.get(0).equals(preferredOrdering.get(0));
+    if (!exactMatch && 
!ordering.get(0).isExactReverse(preferredOrdering.get(0))) {
+      // not exact match or reverse match on first column, fail fast
+      return false;
+    }
+    for (int i = 1; i < preferredOrdering.size(); i++) {
+      if (exactMatch && ordering.get(i).equals(preferredOrdering.get(i))) {
+        // exact match, continue
+      } else if (!exactMatch && 
ordering.get(i).isExactReverse(preferredOrdering.get(i))) {
+        // match in opposite direction, continue
+      } else {
         return false;
       }
     }
@@ -498,7 +511,6 @@ public class CursorBuildSpec
     }
 
 
-
     /**
      * Adds a {@link Filter} to the builder, if {@link #filter} is already 
set, the existing and new filters will be
      * combined with an {@link org.apache.druid.segment.filter.AndFilter}. If 
{@link #physicalColumns} is set,
diff --git 
a/processing/src/test/java/org/apache/druid/segment/CursorBuildSpecTest.java 
b/processing/src/test/java/org/apache/druid/segment/CursorBuildSpecTest.java
index 7e19858c5a3..56dd2396d0d 100644
--- a/processing/src/test/java/org/apache/druid/segment/CursorBuildSpecTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/CursorBuildSpecTest.java
@@ -63,6 +63,12 @@ public class CursorBuildSpecTest
             ImmutableList.of(OrderBy.ascending(ColumnHolder.TIME_COLUMN_NAME), 
OrderBy.ascending("x"))
         )
     );
+    // pass if the cursor ordering is exactly the reverse
+    Assert.assertTrue(
+        spec1.isCompatibleOrdering(
+            
ImmutableList.of(OrderBy.descending(ColumnHolder.TIME_COLUMN_NAME), 
OrderBy.descending("x"))
+        )
+    );
     // pass if the cursor ordering includes additional ordering not specified 
by the spec preferred ordering
     Assert.assertTrue(
         spec1.isCompatibleOrdering(
@@ -83,6 +89,12 @@ public class CursorBuildSpecTest
             )
         )
     );
+    // fail if the cursor ordering is different
+    Assert.assertFalse(
+        spec1.isCompatibleOrdering(
+            ImmutableList.of(OrderBy.ascending(ColumnHolder.TIME_COLUMN_NAME), 
OrderBy.descending("x"))
+        )
+    );
 
     // test no specified preferred ordering by the reader
     CursorBuildSpec spec2 = CursorBuildSpec.builder()
diff --git 
a/processing/src/test/java/org/apache/druid/segment/CursorFactoryProjectionTest.java
 
b/processing/src/test/java/org/apache/druid/segment/CursorFactoryProjectionTest.java
index 7299cbb7fef..8c306b71089 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/CursorFactoryProjectionTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/CursorFactoryProjectionTest.java
@@ -20,9 +20,6 @@
 package org.apache.druid.segment;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import it.unimi.dsi.fastutil.Hash;
-import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
 import org.apache.druid.collections.CloseableDefaultBlockingPool;
 import org.apache.druid.collections.CloseableStupidPool;
 import org.apache.druid.collections.NonBlockingPool;
@@ -46,10 +43,14 @@ import org.apache.druid.java.util.common.guava.Sequence;
 import org.apache.druid.java.util.common.io.Closer;
 import org.apache.druid.math.expr.ExprMacroTable;
 import org.apache.druid.query.DefaultQueryMetrics;
+import org.apache.druid.query.DirectQueryProcessingPool;
 import org.apache.druid.query.DruidProcessingConfig;
 import org.apache.druid.query.Druids;
 import org.apache.druid.query.Query;
 import org.apache.druid.query.QueryContexts;
+import org.apache.druid.query.QueryPlus;
+import org.apache.druid.query.QueryResourceId;
+import org.apache.druid.query.QueryRunner;
 import org.apache.druid.query.Result;
 import org.apache.druid.query.aggregation.AggregatorFactory;
 import org.apache.druid.query.aggregation.CountAggregatorFactory;
@@ -252,20 +253,12 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                  new StringDimensionSchema("b")
                              )
                              .build(),
-      AggregateProjectionSpec.builder("abfoo")
-                             .virtualColumns(
-                                 new ExpressionVirtualColumn(
-                                     "bfoo",
-                                     "concat(b, 'foo')",
-                                     ColumnType.STRING,
-                                     TestExprMacroTable.INSTANCE
-                                 )
-                             )
-                             .groupingColumns(
-                                 new StringDimensionSchema("a"),
-                                 new StringDimensionSchema("bfoo")
-                             )
-                             .build(),
+      AggregateProjectionSpec
+          .builder("abfoo")
+          .virtualColumns(
+              new ExpressionVirtualColumn("bfoo", "concat(b, 'foo')", 
ColumnType.STRING, TestExprMacroTable.INSTANCE))
+          .groupingColumns(new StringDimensionSchema("a"), new 
StringDimensionSchema("bfoo"))
+          .build(),
       AggregateProjectionSpec.builder("c_sum_daily")
                              
.virtualColumns(Granularities.toVirtualColumn(Granularities.DAY, "__gran"))
                              .groupingColumns(new 
LongDimensionSchema("__gran"))
@@ -285,10 +278,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
       AggregateProjectionSpec.builder("a_filter_b_aaonly_hourly_cd_sum")
                              
.virtualColumns(Granularities.toVirtualColumn(Granularities.HOUR, "__gran"))
                              .filter(new EqualityFilter("b", 
ColumnType.STRING, "aa", null))
-                             .groupingColumns(
-                                 new StringDimensionSchema("a"),
-                                 new LongDimensionSchema("__gran")
-                             )
+                             .groupingColumns(new StringDimensionSchema("a"), 
new LongDimensionSchema("__gran"))
                              .aggregators(
                                  new LongSumAggregatorFactory("_c_sum", "c"),
                                  new DoubleSumAggregatorFactory("d", "d")
@@ -361,32 +351,25 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
   );
 
   private static final List<AggregateProjectionSpec> ROLLUP_PROJECTIONS = 
Arrays.asList(
-      AggregateProjectionSpec.builder("a_hourly_c_sum_with_count")
-                             
.virtualColumns(Granularities.toVirtualColumn(Granularities.HOUR, "__gran"))
-                             .groupingColumns(
-                                 new LongDimensionSchema("__gran"),
-                                 new StringDimensionSchema("a")
-                             )
-                             .aggregators(
-                                 new CountAggregatorFactory("chocula"),
-                                 new LongSumAggregatorFactory("sum_c", "sum_c")
-                             )
-                             .build(),
-      AggregateProjectionSpec.builder("afoo")
-                             .virtualColumns(
-                                 new ExpressionVirtualColumn(
-                                     "afoo",
-                                     "concat(a, 'foo')",
-                                     ColumnType.STRING,
-                                     TestExprMacroTable.INSTANCE
-                                 )
-                             )
-                             .groupingColumns(new 
StringDimensionSchema("afoo"))
-                             .aggregators(
-                                 new LongSumAggregatorFactory("sum_c", 
"sum_c"),
-                                 new LongMaxAggregatorFactory("max_c", "max_c")
-                             )
-                             .build()
+      AggregateProjectionSpec
+          .builder("a_hourly_c_sum_with_count")
+          .virtualColumns(Granularities.toVirtualColumn(Granularities.HOUR, 
"__gran"))
+          .groupingColumns(new LongDimensionSchema("__gran"), new 
StringDimensionSchema("a"))
+          .aggregators(
+              new CountAggregatorFactory("chocula"),
+              new LongSumAggregatorFactory("sum_c", "sum_c")
+          )
+          .build(),
+      AggregateProjectionSpec
+          .builder("afoo")
+          .virtualColumns(
+              new ExpressionVirtualColumn("afoo", "concat(a, 'foo')", 
ColumnType.STRING, TestExprMacroTable.INSTANCE))
+          .groupingColumns(new StringDimensionSchema("afoo"))
+          .aggregators(
+              new LongSumAggregatorFactory("sum_c", "sum_c"),
+              new LongMaxAggregatorFactory("max_c", "max_c")
+          )
+          .build()
   );
 
   private static final List<AggregateProjectionSpec> AUTO_PROJECTIONS =
@@ -551,6 +534,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
   public final TimeBoundaryInspector rollupProjectionsTimeBoundaryInspector;
 
   private final GroupingEngine groupingEngine;
+  private final GroupByResourcesReservationPool resourcesReservationPool;
   private final TimeseriesQueryEngine timeseriesEngine;
 
   private final NonBlockingPool<ByteBuffer> nonBlockingPool;
@@ -583,18 +567,19 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
             () -> ByteBuffer.allocate(50000)
         )
     );
+    this.resourcesReservationPool = new GroupByResourcesReservationPool(
+        closer.closeLater(
+            new CloseableDefaultBlockingPool<>(
+                () -> ByteBuffer.allocate(50000),
+                5
+            )
+        ),
+        new GroupByQueryConfig()
+    );
     this.groupingEngine = new GroupingEngine(
         new DruidProcessingConfig(),
         GroupByQueryConfig::new,
-        new GroupByResourcesReservationPool(
-            closer.closeLater(
-                new CloseableDefaultBlockingPool<>(
-                    () -> ByteBuffer.allocate(50000),
-                    5
-                )
-            ),
-            new GroupByQueryConfig()
-        ),
+        resourcesReservationPool,
         TestHelper.makeJsonMapper(),
         TestHelper.makeSmileMapper(),
         (query, future) -> {
@@ -614,6 +599,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     .setInterval(Intervals.ETERNITY)
                     .addDimension("a")
                     .addDimension("b")
+                    .addOrderByColumn("a", Direction.DESCENDING)
+                    .addOrderByColumn("b", Direction.DESCENDING)
+                    .setLimit(10)
                     .build();
 
     final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy("ab");
@@ -625,10 +613,12 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{"b", "bb"},
-            new Object[]{"a", "dd"},
             new Object[]{"b", "aa"},
+            new Object[]{"a", "dd"},
             new Object[]{"a", "cc"},
             new Object[]{"a", "bb"},
             new Object[]{"a", "aa"}
@@ -658,12 +648,12 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                         new DefaultLimitSpec(
                             Arrays.asList(
                                 new OrderByColumnSpec("a", 
Direction.ASCENDING, StringComparators.LEXICOGRAPHIC),
-                                new OrderByColumnSpec("v0", 
Direction.ASCENDING, StringComparators.LEXICOGRAPHIC)
+                                new OrderByColumnSpec("v0", 
Direction.DESCENDING, StringComparators.LEXICOGRAPHIC)
                             ),
                             10
                         )
                     )
-                    .setContext(ImmutableMap.of(QueryContexts.USE_PROJECTION, 
"abfoo"))
+                    .setContext(Map.of(QueryContexts.USE_PROJECTION, "abfoo"))
                     .build();
 
     final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy("abfoo");
@@ -674,13 +664,15 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
-            new Object[]{"b", "bbfoo"},
+        false,
+        true,
+        List.of(
             new Object[]{"a", "ddfoo"},
-            new Object[]{"b", "aafoo"},
             new Object[]{"a", "ccfoo"},
             new Object[]{"a", "bbfoo"},
-            new Object[]{"a", "aafoo"}
+            new Object[]{"a", "aafoo"},
+            new Object[]{"b", "bbfoo"},
+            new Object[]{"b", "aafoo"}
         )
     );
   }
@@ -696,6 +688,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     .setInterval(Intervals.ETERNITY)
                     .addDimension("a")
                     .addDimension("b")
+                    .addOrderByColumn("a", Direction.DESCENDING)
+                    .addOrderByColumn("b", Direction.DESCENDING)
+                    .setLimit(10)
                     .addAggregator(new CountAggregatorFactory("count"))
                     .build();
 
@@ -707,12 +702,14 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
+            new Object[]{"b", "bb", 1L},
             new Object[]{"b", "aa", 2L},
+            new Object[]{"a", "dd", 1L},
             new Object[]{"a", "cc", 1L},
             new Object[]{"a", "bb", 1L},
-            new Object[]{"b", "bb", 1L},
-            new Object[]{"a", "dd", 1L},
             new Object[]{"a", "aa", 2L}
         )
     );
@@ -730,7 +727,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     .addDimension("a")
                     .addDimension("b")
                     .addAggregator(new CountAggregatorFactory("count"))
-                    
.setContext(ImmutableMap.of(QueryContexts.FORCE_PROJECTION, true))
+                    .setContext(Map.of(QueryContexts.FORCE_PROJECTION, true))
                     .build();
 
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, null);
@@ -754,7 +751,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     .addDimension("a")
                     .addAggregator(new LongSumAggregatorFactory("c_sum", "c"))
                     .addAggregator(new LongLastAggregatorFactory("c_last", 
"c", null))
-                    .setContext(ImmutableMap.of(QueryContexts.NO_PROJECTIONS, 
true))
+                    .setContext(Map.of(QueryContexts.NO_PROJECTIONS, true))
                     .build();
     final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy(null);
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, queryMetrics);
@@ -1090,17 +1087,14 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, queryMetrics);
 
     assertCursorNoProjection(buildSpec, queryMetrics);
-
-    Set<Object[]> resultsInNoParticularOrder = makeArrayResultSet();
-    resultsInNoParticularOrder.addAll(
-        ROWS.stream()
-            .map(x -> new Object[]{x.getTimestamp().getMillis(), 
x.getRaw("a"), x.getRaw("c")})
-            .collect(Collectors.toList())
-    );
     testGroupBy(
         query,
         queryMetrics,
-        resultsInNoParticularOrder
+        false,
+        true,
+        ROWS.stream()
+            .map(x -> new Object[]{x.getTimestamp().getMillis(), 
x.getRaw("a"), x.getRaw("c")})
+            .collect(Collectors.toList())
     );
   }
 
@@ -1133,7 +1127,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{UTC_MIDNIGHT.getMillis(), "a", 4L},
             new Object[]{UTC_MIDNIGHT.getMillis(), "b", 12L},
             new Object[]{UTC_01H.getMillis(), "a", 3L}
@@ -1170,7 +1166,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{UTC_MIDNIGHT.getMillis(), "aa", 8L},
             new Object[]{UTC_MIDNIGHT.getMillis(), "bb", 6L},
             new Object[]{UTC_MIDNIGHT.getMillis(), "cc", 2L},
@@ -1218,7 +1216,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{UTC_MIDNIGHT.getMillis(), "a", 4L},
             new Object[]{UTC_MIDNIGHT.getMillis(), "b", 12L},
             new Object[]{UTC_01H.getMillis(), "a", 3L}
@@ -1263,7 +1263,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{UTC_MIDNIGHT.minusMinutes(30).getMillis(), "a", 4L},
             new Object[]{UTC_MIDNIGHT.minusMinutes(30).getMillis(), "b", 12L},
             new Object[]{UTC_01H.minusMinutes(30).getMillis(), "a", 1L},
@@ -1301,7 +1303,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{UTC_MIDNIGHT.getMillis(), "a", 7L},
             new Object[]{UTC_MIDNIGHT.getMillis(), "b", 12L}
         )
@@ -1402,7 +1406,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         
.intervals(ImmutableList.of(Intervals.ETERNITY))
                                         .granularity(Granularities.ALL)
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(QueryContexts.USE_PROJECTION, "b_c_sum"))
+                                        
.context(Map.of(QueryContexts.USE_PROJECTION, "b_c_sum"))
                                         .build();
 
     final ExpectedProjectionTimeseries queryMetrics =
@@ -1430,7 +1434,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         
.intervals(ImmutableList.of(Intervals.ETERNITY))
                                         .granularity(Granularities.ALL)
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(QueryContexts.NO_PROJECTIONS, true))
+                                        
.context(Map.of(QueryContexts.NO_PROJECTIONS, true))
                                         .build();
 
     final ExpectedProjectionTimeseries queryMetrics =
@@ -1458,7 +1462,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         
.intervals(ImmutableList.of(Intervals.ETERNITY))
                                         .granularity(Granularities.DAY)
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(QueryContexts.USE_PROJECTION, "b_c_sum"))
+                                        
.context(Map.of(QueryContexts.USE_PROJECTION, "b_c_sum"))
                                         .build();
 
     final CursorBuildSpec buildSpec = 
TimeseriesQueryEngine.makeCursorBuildSpec(query, null);
@@ -1504,7 +1508,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         
.intervals(ImmutableList.of(Intervals.ETERNITY))
                                         .granularity(Granularities.ALL)
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(QueryContexts.USE_PROJECTION, "c_sum_daily"))
+                                        
.context(Map.of(QueryContexts.USE_PROJECTION, "c_sum_daily"))
                                         .build();
 
     final ExpectedProjectionTimeseries queryMetrics =
@@ -1530,7 +1534,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         
.intervals(ImmutableList.of(Intervals.ETERNITY))
                                         .granularity(Granularities.ALL)
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(QueryContexts.USE_PROJECTION, "c_sum"))
+                                        
.context(Map.of(QueryContexts.USE_PROJECTION, "c_sum"))
                                         .build();
 
     final ExpectedProjectionTimeseries queryMetrics =
@@ -1558,7 +1562,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         
.intervals(ImmutableList.of(Intervals.ETERNITY))
                                         .granularity(Granularities.MINUTE)
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(TimeseriesQuery.SKIP_EMPTY_BUCKETS, true))
+                                        
.context(Map.of(TimeseriesQuery.SKIP_EMPTY_BUCKETS, true))
                                         .build();
 
     final ExpectedProjectionTimeseries queryMetrics =
@@ -1595,17 +1599,16 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     .addAggregator(new LongSumAggregatorFactory("c_sum", 
"sum_c"))
                     .build();
 
-    final ExpectedProjectionGroupBy queryMetrics =
-        new ExpectedProjectionGroupBy("a_hourly_c_sum_with_count");
+    final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy("a_hourly_c_sum_with_count");
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, queryMetrics);
 
     assertCursorProjection(rollupProjectionsCursorFactory, buildSpec, 
queryMetrics, 3);
 
     testGroupBy(
-        rollupProjectionsCursorFactory,
-        rollupProjectionsTimeBoundaryInspector,
         query,
         queryMetrics,
+        true,
+        false,
         List.of(
             new Object[]{"a", 7L},
             new Object[]{"b", 12L}
@@ -1639,11 +1642,11 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     assertCursorProjection(rollupProjectionsCursorFactory, buildSpec, 
queryMetrics, 2);
 
     testGroupBy(
-        rollupProjectionsCursorFactory,
-        rollupProjectionsTimeBoundaryInspector,
         query,
         queryMetrics,
-        makeArrayResultSet(
+        true,
+        false,
+        List.of(
             new Object[]{"afoo", 7L, 2L},
             new Object[]{"bfoo", 12L, 5L}
         )
@@ -1753,13 +1756,14 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{"aaa", null},
             new Object[]{"aaa", 2L},
             new Object[]{"abb", 2L},
             new Object[]{"acc", 4L},
             new Object[]{"add", 4L},
-            new Object[]{"baa", 8L},
             new Object[]{"baa", 6L},
             new Object[]{"baa", 8L},
             new Object[]{"bbb", 11L}
@@ -1781,19 +1785,14 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
 
     final boolean isRealtime = projectionsCursorFactory instanceof 
IncrementalIndexCursorFactory;
     // realtime projections don't have row count, so abfoo is chosen because 
of how projection sorting happens
-    final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy(isRealtime ? "abfoo" : 
"a_hourly_c_sum_filter_a_to_a");
+    final ExpectedProjectionGroupBy queryMetrics =
+        new ExpectedProjectionGroupBy(isRealtime ? "abfoo" : 
"a_hourly_c_sum_filter_a_to_a");
 
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, queryMetrics);
 
     assertCursorProjection(buildSpec, queryMetrics, isRealtime ? 4 : 2);
-
-    testGroupBy(
-        query,
-        queryMetrics,
-        makeArrayResultSet(
-            new Object[]{"a"}
-        )
-    );
+    // List.of automatically unpacks Object[] to Object, so use 
ImmutableList.of
+    testGroupBy(query, queryMetrics, ImmutableList.of(new Object[]{"a"}));
   }
 
   @Test
@@ -1809,19 +1808,14 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     .build();
 
     final boolean isRealtime = projectionsCursorFactory instanceof 
IncrementalIndexCursorFactory;
-    final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy(isRealtime ? "abfoo" : 
"a_hourly_c_sum_with_count_latest");
+    final ExpectedProjectionGroupBy queryMetrics =
+        new ExpectedProjectionGroupBy(isRealtime ? "abfoo" : 
"a_hourly_c_sum_with_count_latest");
 
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, queryMetrics);
 
     assertCursorProjection(buildSpec, queryMetrics, isRealtime ? 2 : 1);
-
-    testGroupBy(
-        query,
-        queryMetrics,
-        makeArrayResultSet(
-            new Object[]{"b"}
-        )
-    );
+    // List.of automatically unpacks Object[] to Object, so use 
ImmutableList.of
+    testGroupBy(query, queryMetrics, ImmutableList.of(new Object[]{"b"}));
   }
 
   @Test
@@ -1843,11 +1837,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
 
     assertCursorProjection(buildSpec, queryMetrics, 0);
 
-    testGroupBy(
-        query,
-        queryMetrics,
-        makeArrayResultSet()
-    );
+    testGroupBy(query, queryMetrics, List.of());
   }
 
   @Test
@@ -1859,7 +1849,10 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                                         .granularity(Granularities.ALL)
                                         .filters(new EqualityFilter("a", 
ColumnType.STRING, "nomatch", null))
                                         .aggregators(new 
LongSumAggregatorFactory("c_sum", "c"))
-                                        
.context(ImmutableMap.of(QueryContexts.USE_PROJECTION, 
"a_hourly_c_sum_filter_a_to_empty"))
+                                        .context(Map.of(
+                                            QueryContexts.USE_PROJECTION,
+                                            "a_hourly_c_sum_filter_a_to_empty"
+                                        ))
                                         .build();
 
     final ExpectedProjectionTimeseries queryMetrics =
@@ -1937,8 +1930,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                     )
                     .build();
 
-    final ExpectedProjectionGroupBy queryMetrics =
-        new ExpectedProjectionGroupBy("time_and_a");
+    final ExpectedProjectionGroupBy queryMetrics = new 
ExpectedProjectionGroupBy("time_and_a");
     final CursorBuildSpec buildSpec = 
GroupingEngine.makeCursorBuildSpec(query, queryMetrics);
 
     assertCursorProjection(buildSpec, queryMetrics, 8);
@@ -1946,7 +1938,9 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(
+        false,
+        true,
+        List.of(
             new Object[]{UTC_MIDNIGHT.getMillis(), "aaaa"},
             new Object[]{UTC_MIDNIGHT.plusMinutes(2).getMillis(), "aaaa"},
             new Object[]{UTC_MIDNIGHT.plusMinutes(4).getMillis(), "aaaa"},
@@ -1990,93 +1984,66 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
     testGroupBy(
         query,
         queryMetrics,
-        makeArrayResultSet(new Object[]{6L})
+        ImmutableList.of(new Object[]{6L})
     );
   }
 
-
-  private void testGroupBy(GroupByQuery query, ExpectedProjectionGroupBy 
queryMetrics, List<Object[]> expectedResults)
-  {
-    testGroupBy(projectionsCursorFactory, projectionsTimeBoundaryInspector, 
query, queryMetrics, expectedResults);
-  }
-
   private void testGroupBy(
-      CursorFactory cursorFactory,
-      TimeBoundaryInspector timeBoundaryInspector,
       GroupByQuery query,
       ExpectedProjectionGroupBy queryMetrics,
       List<Object[]> expectedResults
   )
   {
-    final Sequence<ResultRow> resultRows = groupingEngine.process(
-        query,
-        cursorFactory,
-        timeBoundaryInspector,
-        nonBlockingPool,
-        queryMetrics
-    );
-
-    queryMetrics.assertProjection();
-
-    final List<ResultRow> results = resultRows.toList();
-    assertGroupByResults(expectedResults, results);
-
-    final Sequence<ResultRow> resultRowsNoProjection = groupingEngine.process(
-        query.withOverriddenContext(Map.of(QueryContexts.NO_PROJECTIONS, 
true)),
-        cursorFactory,
-        timeBoundaryInspector,
-        nonBlockingPool,
-        null
-    );
-
-    final List<ResultRow> resultsNoProjection = 
resultRowsNoProjection.toList();
-    assertGroupByResults(expectedResults, resultsNoProjection);
+    testGroupBy(query, queryMetrics, false, false, expectedResults);
   }
 
-  private void testGroupBy(GroupByQuery query, ExpectedProjectionGroupBy 
queryMetrics, Set<Object[]> expectedResults)
+  private void testGroupBy(
+      GroupByQuery query,
+      ExpectedProjectionGroupBy queryMetrics,
+      boolean rollup,
+      boolean withMerge,
+      List<Object[]> expectedResults
+  )
   {
-    testGroupBy(projectionsCursorFactory, projectionsTimeBoundaryInspector, 
query, queryMetrics, expectedResults);
+    // test query with projections (sometimes projections are not used due to 
query shape)
+    testGroupByQuery(query, queryMetrics, rollup, withMerge, expectedResults);
+    // test query without projections
+    GroupByQuery queryNoProjections = 
query.withOverriddenContext(Map.of(QueryContexts.NO_PROJECTIONS, true));
+    testGroupByQuery(queryNoProjections, new ExpectedProjectionGroupBy(null), 
rollup, withMerge, expectedResults);
   }
 
-  private void testGroupBy(
-      CursorFactory cursorFactory,
-      TimeBoundaryInspector timeBoundaryInspector,
+  private void testGroupByQuery(
       GroupByQuery query,
       ExpectedProjectionGroupBy queryMetrics,
-      Set<Object[]> expectedResults
+      boolean rollup,
+      boolean withMerge,
+      List<Object[]> expectedResults
   )
   {
-    final Sequence<ResultRow> resultRows = groupingEngine.process(
-        query,
-        cursorFactory,
-        timeBoundaryInspector,
+    GroupByQuery finalQuery = withMerge ? query.withOverriddenContext(Map.of(
+        QueryContexts.QUERY_RESOURCE_ID,
+        String.valueOf(query.hashCode())
+    )) : query;
+    QueryRunner<ResultRow> runner = (unused1, unused2) -> 
groupingEngine.process(
+        finalQuery,
+        rollup ? rollupProjectionsCursorFactory : projectionsCursorFactory,
+        rollup ? rollupProjectionsTimeBoundaryInspector : 
projectionsTimeBoundaryInspector,
         nonBlockingPool,
         queryMetrics
     );
+    if (withMerge) {
+      resourcesReservationPool.reserve(
+          new QueryResourceId(String.valueOf(query.hashCode())),
+          finalQuery,
+          true,
+          new GroupByStatsProvider.PerQueryStats()
+      );
+      runner = groupingEngine.mergeRunners(DirectQueryProcessingPool.INSTANCE, 
List.of(runner));
+    }
+    final List<ResultRow> results = 
runner.run(QueryPlus.wrap(finalQuery)).toList();
 
     queryMetrics.assertProjection();
-
-    final Object[] results = 
resultRows.toList().stream().map(ResultRow::getArray).map(Arrays::toString).toArray();
-    Arrays.sort(results);
-
-    final Object[] expectedResultsArray = 
expectedResults.stream().map(Arrays::toString).toArray();
-    Arrays.sort(expectedResultsArray);
-    // print a full diff of all differing elements.
-    Assertions.assertEquals(Arrays.toString(expectedResultsArray), 
Arrays.toString(results));
-
-    final Sequence<ResultRow> resultRowsNoProjection = groupingEngine.process(
-        query.withOverriddenContext(Map.of(QueryContexts.NO_PROJECTIONS, 
true)),
-        cursorFactory,
-        timeBoundaryInspector,
-        nonBlockingPool,
-        null
-    );
-
-    final Object[] resultsNoProjection = 
resultRowsNoProjection.toList().stream().map(ResultRow::getArray).map(Arrays::toString).toArray();
-    Arrays.sort(resultsNoProjection);
-    // print a full diff of all differing elements.
-    Assertions.assertEquals(Arrays.toString(expectedResultsArray), 
Arrays.toString(resultsNoProjection));
-
+    assertGroupByResults(expectedResults, results);
   }
 
   private void testTimeseries(
@@ -2128,9 +2095,12 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
   private void assertGroupByResults(List<Object[]> expected, List<ResultRow> 
actual)
   {
     Assertions.assertEquals(expected.size(), actual.size());
-    for (int i = 0; i < expected.size(); i++) {
-      Assertions.assertArrayEquals(expected.get(i), actual.get(i).getArray());
-    }
+    Object[] actualArray = 
actual.stream().map(ResultRow::getArray).map(Arrays::toString).toArray();
+    // print a full diff of all differing elements.
+    Assertions.assertEquals(
+        Arrays.toString(expected.stream().map(Arrays::toString).toArray()),
+        Arrays.toString(actualArray)
+    );
   }
 
   private void assertTimeseriesResults(
@@ -2179,7 +2149,7 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
       Assert.assertEquals(expectedRowCount, rowCount);
     }
   }
-  
+
   private static AutoTypeColumnSchema toAutoColumn(DimensionSchema x)
   {
     if (PROJECTION_TIME_COLUMNS.contains(x.getName())) {
@@ -2230,34 +2200,6 @@ public class CursorFactoryProjectionTest extends 
InitializedNullHandlingTest
                        .rows(ROLLUP_ROWS);
   }
 
-  private static Set<Object[]> makeArrayResultSet()
-  {
-    Set<Object[]> resultsInNoParticularOrder = new ObjectOpenCustomHashSet<>(
-        new Hash.Strategy<>()
-        {
-          @Override
-          public int hashCode(Object[] o)
-          {
-            return Arrays.deepHashCode(o);
-          }
-
-          @Override
-          public boolean equals(Object[] a, Object[] b)
-          {
-            return Arrays.deepEquals(a, b);
-          }
-        }
-    );
-    return resultsInNoParticularOrder;
-  }
-
-  private static Set<Object[]> makeArrayResultSet(Object[]... values)
-  {
-    Set<Object[]> resultsInNoParticularOrder = makeArrayResultSet();
-    resultsInNoParticularOrder.addAll(Arrays.asList(values));
-    return resultsInNoParticularOrder;
-  }
-
   private static Object[] getResultArray(Result<TimeseriesResultValue> result, 
RowSignature rowSignature)
   {
     final Object[] rowArray = new Object[rowSignature.size()];


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to