adasari commented on code in PR #18334:
URL: https://github.com/apache/pinot/pull/18334#discussion_r3600301816


##########
pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java:
##########
@@ -115,17 +115,38 @@ public Operator<AggregationResultsBlock> 
buildNonFilteredAggOperator() {
 
     boolean hasNullValues = _queryContext.isNullHandlingEnabled() && 
hasNullValues(aggregationFunctions);
     if (!hasNullValues) {
+      DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+      for (int i = 0; i < aggregationFunctions.length; i++) {
+        List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
+        if (!inputExpressions.isEmpty()) {
+          String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
+          dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+        }
+      }
+
       // Priority 2: Check if non-scan based aggregation is feasible
       if (filterOperator.isResultMatchingAll() && isFitForNonScanBasedPlan()) {
-        DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+        return new NonScanBasedAggregationOperator(_queryContext, dataSources, 
numTotalDocs);
+      }
+
+      if (filterOperator.isResultMatchingAll()) {
+        boolean anyResolved = false;
+        Object[] preAggregatedResults = new 
Object[aggregationFunctions.length];
         for (int i = 0; i < aggregationFunctions.length; i++) {
-          List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
-          if (!inputExpressions.isEmpty()) {
-            String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
-            dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+          Object resolved = 
AggregationFunctionUtils.getAggregationResult(aggregationFunctions[i],
+              dataSources[i], numTotalDocs, 
NonScanBasedAggregationOperator.EXPLAIN_NAME);
+          if (resolved != null) {
+            preAggregatedResults[i] = resolved;
+            anyResolved = true;
           }
         }
-        return new NonScanBasedAggregationOperator(_queryContext, dataSources, 
numTotalDocs);
+
+        if (anyResolved) {
+          // build aggregation info for all functions (including those that 
cannot be resolved from metadata)
+          aggregationInfo = 
AggregationFunctionUtils.buildAggregationInfoWithoutStarTree(_segmentContext, 
_queryContext,

Review Comment:
   > buildAggregationInfoWithoutStarTree collects every function's columns, so 
the metadata-resolved columns still get projected/scanned - we only skip the 
per-row aggregate(), not the I/O. So the win is narrower than "avoid the scan".
   
   Yes. The I/O required to read the dictionary which cannot be avoided. 
However, aggregation over the full segment is skipped.
   
   There is still room for further optimization. Some aggregation functions, 
such as **MIN** and **MAX**, are still dictionary-based, but they can be 
replaced with metadata-driven implementations.
   
   In the future, we can also optimize the segment layout to minimize I/O. For 
example, if dictionaries are confined to a small number of pages (from mmap), 
it may be possible to avoid reading the actual index pages data altogether.
   
   > Resolution runs here at plan-build time (dictionary scans, HLL merges), 
whereas the full non-scan path does it lazily in getNextBlock(). Might be worth 
deferring to keep planning cheap and the two paths consistent.
   
   Done.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to