4141import java .util .Set ;
4242import java .util .function .Function ;
4343import java .util .stream .Collectors ;
44-
4544import lombok .RequiredArgsConstructor ;
4645import org .apache .calcite .plan .RelOptCluster ;
4746import org .apache .calcite .rel .core .Aggregate ;
6059import org .opensearch .search .aggregations .AggregatorFactories ;
6160import org .opensearch .search .aggregations .AggregatorFactories .Builder ;
6261import org .opensearch .search .aggregations .BucketOrder ;
63- import org .opensearch .search .aggregations .bucket .range .RangeAggregationBuilder ;
6462import org .opensearch .search .aggregations .bucket .composite .CompositeValuesSourceBuilder ;
6563import org .opensearch .search .aggregations .bucket .composite .TermsValuesSourceBuilder ;
6664import org .opensearch .search .aggregations .bucket .missing .MissingOrder ;
65+ import org .opensearch .search .aggregations .bucket .range .RangeAggregationBuilder ;
6766import org .opensearch .search .aggregations .bucket .terms .TermsAggregationBuilder ;
6867import org .opensearch .search .aggregations .metrics .ExtendedStats ;
6968import org .opensearch .search .aggregations .metrics .PercentilesAggregationBuilder ;
8180import org .opensearch .sql .opensearch .request .PredicateAnalyzer .NamedFieldExpression ;
8281import org .opensearch .sql .opensearch .response .agg .ArgMaxMinParser ;
8382import org .opensearch .sql .opensearch .response .agg .BucketAggregationParser ;
84- import org .opensearch .sql .opensearch .response .agg .CompositeAggregationParser ;
83+ import org .opensearch .sql .opensearch .response .agg .LeafBucketAggregationParser ;
8584import org .opensearch .sql .opensearch .response .agg .MetricParser ;
8685import org .opensearch .sql .opensearch .response .agg .NoBucketAggregationParser ;
8786import org .opensearch .sql .opensearch .response .agg .OpenSearchAggregationResponseParser ;
88- import org .opensearch .sql .opensearch .response .agg .RangeParser ;
8987import org .opensearch .sql .opensearch .response .agg .SinglePercentileParser ;
9088import org .opensearch .sql .opensearch .response .agg .SingleValueParser ;
9189import org .opensearch .sql .opensearch .response .agg .StatsParser ;
@@ -204,11 +202,19 @@ public static Pair<List<AggregationBuilder>, OpenSearchAggregationResponseParser
204202 Builder metricBuilder = builderAndParser .getLeft ();
205203 List <MetricParser > metricParsers = builderAndParser .getRight ();
206204 // Find group by fields derived from CASE functions and convert them to range queries
207- CaseRangeAnalyzer rangeAnalyzer = CaseRangeAnalyzer .create (rowType );
208- List <Pair <Integer , RangeAggregationBuilder >> groupsByCase = groupList .stream ()
209- .map (i -> Pair .of (i , project .getProjects ().get (i )))
210- .filter (p -> p .getRight () instanceof RexCall rexCall && rexCall .getKind () == SqlKind .CASE )
211- .map (p -> Pair .of (p .getLeft (), rangeAnalyzer .analyze ((RexCall ) p .getRight ())))
205+ List <Pair <Integer , RangeAggregationBuilder >> groupsByCase =
206+ groupList .stream ()
207+ .map (i -> Pair .of (i , project .getNamedProjects ().get (i )))
208+ .filter (
209+ p ->
210+ p .getRight ().getKey () instanceof RexCall rexCall
211+ && rexCall .getKind () == SqlKind .CASE )
212+ .map (
213+ p ->
214+ Pair .of (
215+ p .getLeft (),
216+ CaseRangeAnalyzer .create (p .getRight ().getValue (), rowType )
217+ .analyze ((RexCall ) p .getRight ().getKey ())))
212218 .filter (p -> p .getRight ().isPresent ())
213219 .map (p -> Pair .of (p .getLeft (), p .getRight ().get ()))
214220 .toList ();
@@ -220,28 +226,32 @@ public static Pair<List<AggregationBuilder>, OpenSearchAggregationResponseParser
220226 // Note that but a composite aggregation can not be a sub aggregation of range aggregation,
221227 // but range aggregation can be a sub aggregation of a composite aggregation.
222228 AggregationBuilder rangeAggregationBuilder = null ;
229+ BucketAggregationParser bucketAggregationParser = null ;
223230 if (!groupsByCase .isEmpty ()) {
224231 for (int i = 0 ; i < groupsByCase .size (); i ++) {
225232 Pair <Integer , RangeAggregationBuilder > pair = groupsByCase .get (i );
226233 if (i == 0 ) {
227234 rangeAggregationBuilder = pair .getRight ();
235+ bucketAggregationParser =
236+ new BucketAggregationParser (new LeafBucketAggregationParser (metricParsers ));
228237 } else {
229238 groupsByCase .get (i - 1 ).getRight ().subAggregation (pair .getRight ());
239+ bucketAggregationParser = new BucketAggregationParser (bucketAggregationParser );
230240 }
231241 }
232242 groupsByCase .getLast ().getRight ().subAggregations (metricBuilder );
233- metricParsers .add (new RangeParser ("case_range" ));
234243 }
235244
236245 // Remove groups that are converted to ranges from groupList
237246 Set <Integer > toRemove = groupsByCase .stream ().map (Pair ::getLeft ).collect (Collectors .toSet ());
238- List <Integer > filteredGroupList = groupList .stream ().filter (i -> !toRemove .contains (i )).toList ();
247+ List <Integer > filteredGroupList =
248+ groupList .stream ().filter (i -> !toRemove .contains (i )).toList ();
239249
240250 // The top-level query is a range query: stats count() by range_field
241251 // RangeAgg
242252 // Metric
243253 if (!groupsByCase .isEmpty () && filteredGroupList .isEmpty ()) {
244- return Pair .of (List .of (rangeAggregationBuilder ), new BucketAggregationParser ( metricParsers ) );
254+ return Pair .of (List .of (rangeAggregationBuilder ), bucketAggregationParser );
245255 }
246256 // No parent composite aggregation or range aggregation is attached: stats count()
247257 // Metric
@@ -250,21 +260,23 @@ else if (aggregate.getGroupSet().isEmpty() && filteredGroupList.isEmpty()) {
250260 ImmutableList .copyOf (metricBuilder .getAggregatorFactories ()),
251261 new NoBucketAggregationParser (metricParsers ));
252262 }
253- // It has both composite aggregation and range aggregation: stats count() by range_field, non_range_field
263+ // It has both composite aggregation and range aggregation: stats count() by range_field,
264+ // non_range_field
254265 // CompositeAgg
255266 // RangeAgg
256267 // Metric
257268 else if (!groupsByCase .isEmpty ()) {
258269 List <CompositeValuesSourceBuilder <?>> buckets =
259- createCompositeBuckets (filteredGroupList , project , helper );
270+ createCompositeBuckets (filteredGroupList , project , helper );
260271 return Pair .of (
261- Collections .singletonList (
262- AggregationBuilders .composite ("composite_buckets" , buckets )
263- .subAggregation (rangeAggregationBuilder )
264- .size (AGGREGATION_BUCKET_SIZE )),
265- new CompositeAggregationParser ( metricParsers ));
272+ Collections .singletonList (
273+ AggregationBuilders .composite ("composite_buckets" , buckets )
274+ .subAggregation (rangeAggregationBuilder )
275+ .size (AGGREGATION_BUCKET_SIZE )),
276+ new BucketAggregationParser ( bucketAggregationParser ));
266277 }
267- // It does not have range aggregation, but has composite aggregation: stats count() by non_range_field
278+ // It does not have range aggregation, but has composite aggregation: stats count() by
279+ // non_range_field
268280 // CompositeAgg
269281 // Metric
270282 else {
@@ -275,7 +287,7 @@ else if (!groupsByCase.isEmpty()) {
275287 AggregationBuilders .composite ("composite_buckets" , buckets )
276288 .subAggregations (metricBuilder )
277289 .size (AGGREGATION_BUCKET_SIZE )),
278- new CompositeAggregationParser ( metricParsers ));
290+ new BucketAggregationParser ( new LeafBucketAggregationParser ( metricParsers ) ));
279291 }
280292 } catch (Throwable e ) {
281293 Throwables .throwIfInstanceOf (e , UnsupportedOperationException .class );
0 commit comments