-
Notifications
You must be signed in to change notification settings - Fork 176
Add non-numeric field support for max/min functions #4281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
db99822
2201a96
87e5b6a
2580bcd
de51eb1
987809b
864e379
83b5f50
5aea131
6dbf487
1fbd3fb
67d23df
a648c2e
c3080d8
16edabe
d2200e8
e692b9d
27fe823
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"calcite": { | ||
"logical": "LogicalSystemLimit(fetch=[10000], type=[QUERY_SIZE_LIMIT])\n LogicalAggregate(group=[{}], max(firstname)=[MAX($0)])\n LogicalProject(firstname=[$1])\n CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n", | ||
"physical": "EnumerableLimit(fetch=[10000])\n CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={},max(firstname)=MAX($0))], OpenSearchRequestBuilder(sourceBuilder={\"from\":0,\"size\":0,\"timeout\":\"1m\",\"aggregations\":{\"max(firstname)\":{\"top_hits\":{\"from\":0,\"size\":1,\"version\":false,\"seq_no_primary_term\":false,\"explain\":false,\"_source\":{\"includes\":[\"firstname\"],\"excludes\":[]},\"sort\":[{\"firstname.keyword\":{\"order\":\"desc\"}}]}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])\n" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"calcite": { | ||
"logical": "LogicalSystemLimit(fetch=[10000], type=[QUERY_SIZE_LIMIT])\n LogicalAggregate(group=[{}], min(firstname)=[MIN($0)])\n LogicalProject(firstname=[$1])\n CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n", | ||
"physical": "EnumerableLimit(fetch=[10000])\n CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={},min(firstname)=MIN($0))], OpenSearchRequestBuilder(sourceBuilder={\"from\":0,\"size\":0,\"timeout\":\"1m\",\"aggregations\":{\"min(firstname)\":{\"top_hits\":{\"from\":0,\"size\":1,\"version\":false,\"seq_no_primary_term\":false,\"explain\":false,\"_source\":{\"includes\":[\"firstname\"],\"excludes\":[]},\"sort\":[{\"firstname.keyword\":{\"order\":\"asc\"}}]}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])\n" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"calcite": { | ||
"logical": "LogicalSystemLimit(fetch=[10000], type=[QUERY_SIZE_LIMIT])\n LogicalAggregate(group=[{}], max(firstname)=[MAX($0)])\n LogicalProject(firstname=[$1])\n CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n", | ||
"physical": "EnumerableLimit(fetch=[10000])\n EnumerableAggregate(group=[{}], max(firstname)=[MAX($1)])\n CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"calcite": { | ||
"logical": "LogicalSystemLimit(fetch=[10000], type=[QUERY_SIZE_LIMIT])\n LogicalAggregate(group=[{}], min(firstname)=[MIN($0)])\n LogicalProject(firstname=[$1])\n CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n", | ||
"physical": "EnumerableLimit(fetch=[10000])\n EnumerableAggregate(group=[{}], min(firstname)=[MIN($1)])\n CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,8 +69,10 @@ | |
import org.opensearch.sql.ast.expression.Argument; | ||
import org.opensearch.sql.ast.expression.SpanUnit; | ||
import org.opensearch.sql.calcite.utils.OpenSearchTypeFactory; | ||
import org.opensearch.sql.data.type.ExprCoreType; | ||
import org.opensearch.sql.data.type.ExprType; | ||
import org.opensearch.sql.expression.function.BuiltinFunctionName; | ||
import org.opensearch.sql.opensearch.data.type.OpenSearchDataType; | ||
import org.opensearch.sql.opensearch.request.PredicateAnalyzer.NamedFieldExpression; | ||
import org.opensearch.sql.opensearch.response.agg.ArgMaxMinParser; | ||
import org.opensearch.sql.opensearch.response.agg.BucketAggregationParser; | ||
|
@@ -298,12 +300,46 @@ private static Pair<AggregationBuilder, MetricParser> createRegularAggregation( | |
helper.build( | ||
!args.isEmpty() ? args.getFirst() : null, AggregationBuilders.count(aggFieldName)), | ||
new SingleValueParser(aggFieldName)); | ||
case MIN -> Pair.of( | ||
helper.build(args.getFirst(), AggregationBuilders.min(aggFieldName)), | ||
new SingleValueParser(aggFieldName)); | ||
case MAX -> Pair.of( | ||
helper.build(args.getFirst(), AggregationBuilders.max(aggFieldName)), | ||
new SingleValueParser(aggFieldName)); | ||
case MIN -> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MIN and MAX logic seems same other than AggregationBuilders.min/max and ASC/DESC. Can we extract as a method for maintainability? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup makes sense will add this and using |
||
String fieldName = helper.inferNamedField(args.getFirst()).getRootName(); | ||
ExprType fieldType = helper.fieldTypes.get(fieldName); | ||
|
||
if (supportsMaxMinAggregation(fieldType)) { | ||
yield Pair.of( | ||
helper.build(args.getFirst(), AggregationBuilders.min(aggFieldName)), | ||
new SingleValueParser(aggFieldName)); | ||
} else { | ||
yield Pair.of( | ||
AggregationBuilders.topHits(aggFieldName) | ||
.fetchSource(helper.inferNamedField(args.getFirst()).getRootName(), null) | ||
.size(1) | ||
.from(0) | ||
.sort( | ||
helper.inferNamedField(args.getFirst()).getReferenceForTermQuery(), | ||
SortOrder.ASC), | ||
new TopHitsParser(aggFieldName, true)); | ||
} | ||
} | ||
case MAX -> { | ||
String fieldName = helper.inferNamedField(args.getFirst()).getRootName(); | ||
ExprType fieldType = helper.fieldTypes.get(fieldName); | ||
|
||
if (supportsMaxMinAggregation(fieldType)) { | ||
yield Pair.of( | ||
helper.build(args.getFirst(), AggregationBuilders.max(aggFieldName)), | ||
new SingleValueParser(aggFieldName)); | ||
} else { | ||
yield Pair.of( | ||
AggregationBuilders.topHits(aggFieldName) | ||
.fetchSource(helper.inferNamedField(args.getFirst()).getRootName(), null) | ||
.size(1) | ||
.from(0) | ||
.sort( | ||
helper.inferNamedField(args.getFirst()).getReferenceForTermQuery(), | ||
SortOrder.DESC), | ||
new TopHitsParser(aggFieldName, true)); | ||
} | ||
} | ||
case VAR_SAMP -> Pair.of( | ||
helper.build(args.getFirst(), AggregationBuilders.extendedStats(aggFieldName)), | ||
new StatsParser(ExtendedStats::getVarianceSampling, aggFieldName)); | ||
|
@@ -383,6 +419,18 @@ yield switch (functionName) { | |
}; | ||
} | ||
|
||
private static boolean supportsMaxMinAggregation(ExprType fieldType) { | ||
ExprType coreType = | ||
(fieldType instanceof OpenSearchDataType) | ||
? ((OpenSearchDataType) fieldType).getExprType() | ||
: fieldType; | ||
|
||
return ExprCoreType.numberTypes().contains(coreType) | ||
|| coreType == ExprCoreType.DATE | ||
|| coreType == ExprCoreType.TIME | ||
|| coreType == ExprCoreType.TIMESTAMP; | ||
} | ||
|
||
private static ValuesSourceAggregationBuilder<?> createBucketAggregation( | ||
Integer group, Project project, AggregateAnalyzer.AggregateBuilderHelper helper) { | ||
return createBucket(group, project, helper); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we can use assertYamlEqualsJsonIgnoreId (refer #4274) Calcite plan readability.