Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Support composite aggregation paginating - #4884

Merged
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836
Dec 8, 2025
Merged

Support composite aggregation paginating#4884
qianheng-aws merged 14 commits into
opensearch-project:mainfrom
LantaoJin:pr/issues/4836

Conversation

@LantaoJin

@LantaoJinLantaoJin commented Nov 28, 2025

Copy link
Copy Markdown
Member

Description

Support composite aggregation paginating

This PR fix the correctness of following clickbench queries:
Q28, Q29: aggregate with having clause
Q42: offset exceeds the bucket size
and other queries such aggregations within join

Q28:

SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c
FROM hits
WHERE URL <>''GROUP BY CounterID HAVINGCOUNT(*) >100000ORDER BY l DESCLIMIT25;

In PPL query, where c > 100000 actually is a HAVING clause:

source=hits
source=hits
| where URL != ''
| stats bucket_nullable=false avg(length(URL)) as l, count() as c by CounterID
| where c > 100000
| sort - l
| head 25

Q42:

SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews
FROM hits WHERE CounterID =62AND EventDate >='2013-07-01'AND EventDate <='2013-07-31'AND IsRefresh =0AND DontCountHits =0AND URLHash =2868770270353813622GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESCLIMIT10 OFFSET 10000;

In PPL query, the from 10000 exceeds the default bucket size

source=hits
| where CounterID = 62 and EventDate >= '2013-07-01 00:00:00' and EventDate <= '2013-07-31 00:00:00' and IsRefresh = 0 and DontCountHits = 0 and URLHash = 2868770270353813622
| stats bucket_nullable=false count() as PageViews by WindowClientWidth, WindowClientHeight
| sort - PageViews
| head 10 from 10000

Query aggregations within join:
SQL

SELECT*FROM (
SELECTCOUNT(*) cnt, DEPTNO FROM EMP
GROUP BY DEPTNO
) t1
INNER JOIN (
SELECTCOUNT(*) cnt, DEPTNO
FROM DEPT
GROUP BY DEPTNO
) t2 ONt1.DEPTNO=t2.DEPTNO

PPL

source=EMP
| stats count() as cnt by DEPTNO | join type=inner DEPTNO [
source=DEPT | stats count() as cnt by DEPTNO
]

Related Issues

Resolves#4836

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for HAVING clause optimization in aggregation queries to improve query performance.
    • Implemented paginating aggregations for enhanced memory efficiency with large datasets.
  • Tests

    • Expanded test coverage for aggregation queries with HAVING conditions and complex filtering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitaiBot commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

81 files out of 189 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces support for pushing down HAVING clauses in Calcite query optimization and implements composite aggregation pagination. It adds a new HavingPushdownRule, modifies request/response handling to support paginating aggregations with afterKey state management, and includes comprehensive test coverage with expected output files for HAVING aggregation scenarios.

Changes

Cohort / File(s)Summary
HAVING Pushdown Rule Implementation
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/HavingPushdownRule.java
New rule class to match LogicalFilter → LogicalProject → CalciteLogicalIndexScan pattern and push down HAVING clauses via pushDownHavingClauseFlag. Includes Config interface with DEFAULT configuration.
Rule Registration
opensearch/src/main/java/org/opensearch/sql/opensearch/planner/rules/OpenSearchIndexRules.java
Registered new HAVING_PUSHDOWN rule in OPEN_SEARCH_INDEX_SCAN_RULES list.
CalciteLogicalIndexScan HAVING Support
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteLogicalIndexScan.java
Added public method pushDownHavingClauseFlag(LogicalFilter filter) to check conditions and push down HAVING clauses on composite aggregations.
PushDownContext & PushDownType Updates
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownContext.java, opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/PushDownType.java
Added HAVING enum constant to PushDownType; added isHavingFlagPushed state flag to PushDownContext.
AggPushDownAction HAVING Flag
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/context/AggPushDownAction.java
Added hasHavingClause flag with setHavingClauseFlag() setter; updated apply() to pass boolean hasHavingClause to pushDownAggregation().
AbstractCalciteIndexScan Enhancements
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/AbstractCalciteIndexScan.java
Added public methods getAggMeasureNameList() and isHavingFlagPushed(); expanded cost/row-count estimation to include HAVING alongside FILTER.
OpenSearchRequestBuilder Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java
Added paginatingAgg flag; extended build() pathway to handle paginating-agg flows with composite afterKey support; updated pushDownAggregation() signature to accept hasHavingClause boolean; added toString() override with paginatingAgg state.
OpenSearchQueryRequest Paginating Aggregation
opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequest.java
Added static factory methods of(...); new constructor with paginatingAgg parameter; refactored search() to support paginating-agg flow with afterKey collection and continuation; updated cleanup paths to reset afterKey.
OpenSearchIndex & OpenSearchResponse
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/OpenSearchIndex.java, opensearch/src/main/java/org/opensearch/sql/opensearch/response/OpenSearchResponse.java
Added buildRequest(OpenSearchRequestBuilder) public API method to OpenSearchIndex; added noCompositeAfterKey() method to OpenSearchResponse for checking composite aggregation continuation.
OpenSearchIndexEnumerator Logic
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexEnumerator.java
Narrowed aggregation stop condition to check noCompositeAfterKey(); adjusted hits-size condition to require non-zero hits below maxResultWindow.
OpenSearchIndexScanAggregationBuilder
opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanAggregationBuilder.java
Updated pushDownAggregation() call to pass false as second argument for hasHavingClause.
Integration Test: HAVING Explain Plans
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteExplainIT.java
Added testHaving() method executing three explain queries with HAVING conditions on stats aggregations.
Integration Test: HAVING Stats Command
integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStatsCommandIT.java
Added testStatsHaving() method testing PPL stats query with HAVING filter; added @Override to init() method.
Expected Output: ClickBench Q28 & Q29
integ-test/src/test/resources/expectedOutput/calcite/clickbench/q28.yaml, integ-test/src/test/resources/expectedOutput/calcite/clickbench/q29.yaml
Updated physical plans to include HAVING filters in CalciteEnumerableIndexScan PushDownContext.
Expected Output: HAVING Aggregation Plans
integ-test/src/test/resources/expectedOutput/calcite/explain_agg_having{1,2,3}.yaml
New YAML files containing complete logical and physical Calcite explain plans for three HAVING aggregation scenarios with composite bucket pagination.
Expected Output: Explain Output
integ-test/src/test/resources/expectedOutput/calcite/explain_output.yaml
Updated physical plan with revised projections [avg_age, state], added HAVING IS NOT NULL($0), adjusted Sort and pagination, set paginatingAgg=true.
Unit Tests: Request/Builder Updates
opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchQueryRequestTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilderTest.java, opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/OpenSearchIndexScanOptimizationTest.java
Replaced direct constructor calls with OpenSearchQueryRequest.of(...); updated pushDownAggregation() calls to pass false as second argument.
PPL Aggregation Tests
ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLAggregationTest.java
Added three new test cases (testHaving1, testHaving2, testHaving3) covering HAVING semantics, bucket_nullable behavior, and complex filter expressions on aggregations.

Sequence Diagram

sequenceDiagram
participant Optimizer
participant HavingPushdown as HavingPushdownRule
participant Scan as CalciteLogicalIndexScan
participant Builder as OpenSearchRequestBuilder
participant Query as OpenSearchQueryRequest
participant Response as OpenSearchResponse
participant Enumerator as OpenSearchIndexEnumerator
Optimizer->>HavingPushdown: onMatch(LogicalFilter → Project → IndexScan)
HavingPushdown->>Scan: pushDownHavingClauseFlag(filter)
alt Conditions Met
Scan->>Scan: Add HAVING to PushDownContext
Scan-->>HavingPushdown: Return new scan with HAVING flag
HavingPushdown-->>Optimizer: Transform plan with HAVING
else Conditions Not Met
Scan-->>HavingPushdown: Return null
end
Optimizer->>Builder: build()
alt Has Aggregate with HAVING
Builder->>Builder: Set paginatingAgg = true
Builder->>Query: Create with paginatingAgg=true
else Regular Path
Builder->>Query: Create with paginatingAgg=false
end
Query->>Query: Execute search()
alt paginatingAgg Flow
Query->>Query: searchPaginatingAgg()
Query->>Response: Capture CompositeAggregation.afterKey
Response-->>Query: Return response with afterKey
alt No More Results
Response->>Response: noCompositeAfterKey()
Response-->>Enumerator: true
Enumerator->>Enumerator: Stop fetching batches
else More Results
Query->>Query: Set afterKey in CompositeAggregationBuilder
Query->>Query: Execute next search
end
else Regular Flow
Query->>Query: Single-page or PIT search
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • HavingPushdownRule.java: New rule implementation with pattern matching logic and scan transformation; validate correctness of filter extraction and HAVING flag application
  • OpenSearchRequestBuilder & OpenSearchQueryRequest: Significant refactoring introducing paginatingAgg flag and searchPaginatingAgg() flow; verify afterKey state management, cleanup paths, and interaction with existing PIT logic
  • OpenSearchIndexEnumerator: Modified stop conditions for aggregations; ensure noCompositeAfterKey() check correctly handles pagination and doesn't premature exit batches
  • CalciteLogicalIndexScan.pushDownHavingClauseFlag(): Conditional HAVING pushdown requires validation that aggregate field checks and filter condition extraction are correct
  • PushDownContext/AggPushDownAction coordination: Verify the HAVING flag threading through multiple layers (Rule → Scan → Context → Builder → Request)

Possibly related PRs

  • #4867 — Modifies aggregation push-down logic in AggPushDownAction and request builder, directly impacted by this PR's changes to the push-down API signature.

Suggested labels

pushdown

Suggested reviewers

  • penghuo
  • ps48
  • kavithacm
  • derek-ho
  • joshuali925
  • noCharger
  • dai-chen
  • anirudha
  • Yury-Fridlyand
  • MaxKsyunz
  • vamsimanohar
  • acarbonetto
  • seankao-az
  • yuancu
  • Swiddis

Poem

🐰 A HAVING clause hops through the scan,
Paginating buckets as fast as we can—
Composite afterKeys lead the way,
With filters pushed down every day,
Optimization dreams come true, hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 20.63% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check❓ InconclusiveThe title 'Support composite aggregation paginating' is partially related to the changeset but lacks clarity and doesn't fully capture the main objective of supporting HAVING clause pushdown with composite aggregation pagination.Consider revising the title to be more specific and clear, such as 'Support composite aggregation pagination with HAVING clause pushdown' or 'Add HAVING pushdown rule for paginated composite aggregations'.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR implements composite aggregation pagination and HAVING-clause pushdown to fix incorrect query results in Q29 and Q42, directly addressing the requirements in issue #4836.
Out of Scope Changes check✅ PassedAll changes are directly related to implementing composite aggregation pagination and HAVING-clause support. No unrelated or extraneous modifications were detected.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

{
"name": "OpenSearchIndexScan",
"description": {
"request": "OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":10000,\"timeout\":\"1m\",\"query\":{\"bool\":{\"filter\":[{\"range\":{\"age\":{\"from\":null,\"to\":20,\"include_lower\":true,\"include_upper\":false,\"boost\":1.0}}},{\"range\":{\"age\":{\"from\":10,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"_source\":{\"includes\":[\"age\"],\"excludes\":[]}}, searchDone=false)"

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all needClean and searchDone in plan.


import org.junit.After;

public class CalcitePPLAggregationPaginatingIT extends CalcitePPLAggregationIT {

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add CalcitePPLAggregationPaginatingIT and CalcitePPLTpchPaginatingIT to verifiy the results have no changes with paginating feature.

CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]])
physical: |
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])
CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0},avg_age=AVG($1)), PROJECT->[avg_age, age_range], LIMIT->10000], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"age_range":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQGFHsKICAib3AiOiB7CiAgICAibmFtZSI6ICJDQVNFIiwKICAgICJraW5kIjogIkNBU0UiLAogICAgInN5bnRheCI6ICJTUEVDSUFMIgogIH0sCiAgIm9wZXJhbmRzIjogWwogICAgewogICAgICAib3AiOiB7CiAgICAgICAgIm5hbWUiOiAiPCIsCiAgICAgICAgImtpbmQiOiAiTEVTU19USEFOIiwKICAgICAgICAic3ludGF4IjogIkJJTkFSWSIKICAgICAgfSwKICAgICAgIm9wZXJhbmRzIjogWwogICAgICAgIHsKICAgICAgICAgICJkeW5hbWljUGFyYW0iOiAwLAogICAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIklOVEVHRVIiLAogICAgICAgICAgICAibnVsbGFibGUiOiB0cnVlCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiZHluYW1pY1BhcmFtIjogMSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiAyLAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm9wIjogewogICAgICAgICJuYW1lIjogIlNFQVJDSCIsCiAgICAgICAgImtpbmQiOiAiU0VBUkNIIiwKICAgICAgICAic3ludGF4IjogIklOVEVSTkFMIgogICAgICB9LAogICAgICAib3BlcmFuZHMiOiBbCiAgICAgICAgewogICAgICAgICAgImR5bmFtaWNQYXJhbSI6IDMsCiAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiSU5URUdFUiIsCiAgICAgICAgICAgICJudWxsYWJsZSI6IHRydWUKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICJsaXRlcmFsIjogewogICAgICAgICAgICAicmFuZ2VTZXQiOiBbCiAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgImNsb3NlZCIsCiAgICAgICAgICAgICAgICAiMzAiLAogICAgICAgICAgICAgICAgIjQwIgogICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm51bGxBcyI6ICJVTktOT1dOIgogICAgICAgICAgfSwKICAgICAgICAgICJ0eXBlIjogewogICAgICAgICAgICAidHlwZSI6ICJJTlRFR0VSIiwKICAgICAgICAgICAgIm51bGxhYmxlIjogZmFsc2UKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJkeW5hbWljUGFyYW0iOiA0LAogICAgICAidHlwZSI6IHsKICAgICAgICAidHlwZSI6ICJWQVJDSEFSIiwKICAgICAgICAibnVsbGFibGUiOiBmYWxzZSwKICAgICAgICAicHJlY2lzaW9uIjogLTEKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgImR5bmFtaWNQYXJhbSI6IDUsCiAgICAgICJ0eXBlIjogewogICAgICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgICAgICJudWxsYWJsZSI6IGZhbHNlLAogICAgICAgICJwcmVjaXNpb24iOiAtMQogICAgICB9CiAgICB9CiAgXQp9\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[0,2,2,0,2,2],"DIGESTS":["age",30,"u30","age","u40","u100"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}}]},"aggregations":{"avg_age":{"avg":{"field":"age"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])

@LantaoJinLantaoJinDec 8, 2025

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main change in yarm files is updating requestedTotalSize to limited value for composite aggregation.

@qianheng-aws
qianheng-aws merged commit 9930665 into opensearch-project:mainDec 8, 2025
44 of 45 checks passed
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Signed-off-by: Asif Bashar <asif.bashar@gmail.com>
asifabashar pushed a commit to asifabashar/sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
@opensearch-trigger-bot

Copy link
Copy Markdown
Contributor

The backport to 2.19-dev failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repositorycd$(git rev-parse --show-toplevel)# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/sql/backport-2.19-dev 2.19-dev
# Navigate to the new working treepushd ../.worktrees/sql/backport-2.19-dev
# Create a new branch
git switch --create backport/backport-4884-to-2.19-dev
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 9930665c372f433eea4aeb04b5a4cfcd51be3e9e
# Push it to GitHub
git push --set-upstream origin backport/backport-4884-to-2.19-dev
# Go back to the original working treepopd# Delete the working tree
git worktree remove ../.worktrees/sql/backport-2.19-dev

Then, create a pull request where the base branch is 2.19-dev and the compare/head branch is backport/backport-4884-to-2.19-dev.

LantaoJin added a commit to LantaoJin/search-plugins-sql that referenced this pull request Dec 10, 2025
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
@LantaoJinLantaoJin added the backport-manually Filed a PR to backport manually. label Dec 10, 2025
@LantaoJin
LantaoJin deleted the pr/issues/4836 branch December 10, 2025 10:03
LantaoJin added a commit that referenced this pull request Dec 11, 2025
…4930)
* Support composite aggregation paginating (#4884)
* Support composite aggregation paginating in HAVING clause
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* typo
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix doctest and IT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* secruity it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* revert changes in OpenSearchIndexScan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix compile error
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Fix v2 paginationIT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* optimize request total size in compoisite agg
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* fix it
Signed-off-by: Lantao Jin <ltjin@amazon.com>
* Refactor
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
(cherry picked from commit 9930665)
* Fix UT
Signed-off-by: Lantao Jin <ltjin@amazon.com>
---------
Signed-off-by: Lantao Jin <ltjin@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Result of Q29 is incorrect without composite aggregation paginating

4 participants

@LantaoJin@penghuo@yuancu@qianheng-aws