Push down aggregation on text field without .keyword sub-field#5646
Push down aggregation on text field without .keyword sub-field#5646penghuo wants to merge 6 commits into
Conversation
For a text-typed group key or metric argument with no .keyword sub-field, NamedFieldExpression.getReferenceForTermQuery() returned null and CompositeValuesSourceBuilder/ValueCountAggregationBuilder rejected the null field, so pushDownAggregate silently fell back to a full _source scan and client-side aggregation. Route those bare RexInputRefs through a Calcite script that reads the value from _source, matching TermQuery/LikeQuery/RexStandardizer for filter and script fields. Composite terms buckets and metric aggregations that accept a script (notably count(FIELD)) now push down. Signed-off-by: Peng Huo <penghuo@amazon.com> Signed-off-by: Peng Huo <penghuo@gmail.com>
PR Reviewer Guide 🔍(Review updated until commit 99d01c3)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 99d01c3 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit c409ddf
Suggestions up to commit 91204e9
Suggestions up to commit 70c263d
Suggestions up to commit 1760521
Suggestions up to commit bcd0f07
|
Signed-off-by: Peng Huo <penghuo@amazon.com> Signed-off-by: Peng Huo <penghuo@gmail.com>
|
Persistent review updated to latest commit 1760521 |
Four CalciteExplainIT plans encoded the pre-fix behavior where dedup / chart / timechart on a text field with no .keyword sub-field silently fell back to a client-side aggregation over an unbounded scan. With the AggregateAnalyzer fix those queries now push down as composite terms (script over _source), so the pinned physical plans are stale. - Rename testDedupTextTypeNotPushdown -> testDedupTextTypePushdown and update explain_dedup_text_type_push.yaml to the composite terms + top_hits DSL. - Refresh chart_null_str.yaml (chart limit=10 ... over gender by age span=10) to the composite terms(script) + histogram plan. - Refresh explain_timechart.yaml and explain_timechart_count.yaml (timechart span=1m ... by host) to the composite terms(script) + date_histogram plan. Add DedupCommandIT.testDedupOnTextField to verify behavioral equivalence: the result set for `source=bank | dedup email` matches the fixture's set of distinct emails, running both under the V2 path (base class) and Calcite pushdown path (CalciteDedupCommandIT). Signed-off-by: Peng Huo <penghuo@amazon.com> Signed-off-by: Peng Huo <penghuo@gmail.com>
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit 70c263d.
The table above displays the top 10 most important findings. Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
|
Persistent review updated to latest commit 70c263d |
Widen the assertion beyond the dedup key to also verify the associated projected columns per row (firstname, balance), so the top_hits round-trip in the pushed-down dedup DSL is checked end-to-end. Signed-off-by: Peng Huo <penghuo@amazon.com> Signed-off-by: Peng Huo <penghuo@gmail.com>
|
Persistent review updated to latest commit 91204e9 |
Assert row-level results for `source=events | timechart span=1m avg(cpu_usage) by host` on the events fixture, where `host` is a text field with no .keyword sub-field. Golden values were collected on upstream/main (unpushed) before applying the fix, so the assertion pins behavioral equivalence between the V2 client-side plan and the pushed composite terms(script)+date_histogram plan. Signed-off-by: Peng Huo <penghuo@amazon.com> Signed-off-by: Peng Huo <penghuo@gmail.com>
|
Persistent review updated to latest commit c409ddf |
Signed-off-by: Peng Huo <penghuo@amazon.com> Signed-off-by: Peng Huo <penghuo@gmail.com>
| EnumerableCalc(expr#0..2=[{inputs}], expr#3=[10], expr#4=[null:NULL], expr#5=[SPAN($t2, $t3, $t4)], gender=[$t1], balance=[$t0], age0=[$t5]) | ||
| CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank_with_null_values]], PushDownContext=[[PROJECT->[balance, gender, age], FILTER->AND(IS NOT NULL($1), IS NOT NULL($0))], OpenSearchRequestBuilder(sourceBuilder={"from":0,"timeout":"1m","query":{"bool":{"must":[{"exists":{"field":"gender","boost":1.0}},{"exists":{"field":"balance","boost":1.0}}],"adjust_pure_negative":true,"boost":1.0}},"_source":{"includes":["balance","gender","age"]}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)]) | ||
| EnumerableCalc(expr#0..2=[{inputs}], expr#3=[SAFE_CAST($t1)], gender=[$t0], age=[$t3], avg(balance)=[$t2]) | ||
| CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_bank_with_null_values]], PushDownContext=[[FILTER->AND(IS NOT NULL($1), IS NOT NULL($0)), AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={0, 2},avg(balance)=AVG($1))], OpenSearchRequestBuilder(sourceBuilder={"from":0,"size":0,"timeout":"1m","query":{"bool":{"must":[{"exists":{"field":"gender","boost":1.0}},{"exists":{"field":"balance","boost":1.0}}],"adjust_pure_negative":true,"boost":1.0}},"aggregations":{"composite_buckets":{"composite":{"size":1000,"sources":[{"gender":{"terms":{"script":{"source":"{\"langType\":\"calcite\",\"script\":\"rO0ABXQAaXsKICAiZHluYW1pY1BhcmFtIjogMCwKICAidHlwZSI6IHsKICAgICJ0eXBlIjogIlZBUkNIQVIiLAogICAgIm51bGxhYmxlIjogdHJ1ZSwKICAgICJwcmVjaXNpb24iOiAtMQogIH0KfQ==\"}","lang":"opensearch_compounded_script","params":{"utcTimestamp": 0,"SOURCES":[1],"DIGESTS":["gender"]}},"missing_bucket":true,"missing_order":"first","order":"asc"}}},{"age0":{"histogram":{"field":"age","missing_bucket":true,"missing_order":"first","order":"asc","interval":10.0}}}]},"aggregations":{"avg(balance)":{"avg":{"field":"balance"}}}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)]) |
There was a problem hiding this comment.
EnumerableAggregate is pushed down as script.
|
Persistent review updated to latest commit 99d01c3 |
Description
When a text field has no
.keywordsub-field, PPL queries that group by or aggregate over that field silently fall back to a full_sourcescan with client-side aggregation, even when Calcite pushdown is enabled.Fix. When
getReferenceForTermQuery()returnsnullfor a bareRexInputRef, route throughScriptQueryExpression(rexNode, ...)to build a Calcite script that reads the field from_source. This reuses the exactSOURCEpath already established byTermQuery/LikeQuery/RexStandardizer.visitInputRefand consumed at runtime byCalciteScriptEngine.ScriptDataContext.getFromSource→SourceLookup.get(...). The change is inAggregateAnalyzer.AggregateBuilderHelper.build— one branch added.Related Issues
Resolves #5634
Check List
AggregateAnalyzerTest:analyze_aggCall_TextWithoutKeyword_countPushesDownAsScript,analyze_groupBy_TextWithoutKeyword(asserts scriptedvalue_count/termsDSL andSOURCES/DIGESTSscript params).text_agg_pushdown.yml(10 cases) coveringtop,stats … by,count(field)— including result correctness and pushed-down DSL assertions — plus baselines for text+keyword and multi-index queries.--signoffor-s.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.