3838import java .util .Objects ;
3939import java .util .Optional ;
4040import java .util .Set ;
41+ import java .util .function .UnaryOperator ;
4142import java .util .stream .Collectors ;
4243import java .util .stream .Stream ;
4344import org .apache .calcite .plan .RelOptTable ;
8081import org .opensearch .sql .ast .expression .AllFieldsExcludeMeta ;
8182import org .opensearch .sql .ast .expression .Argument ;
8283import org .opensearch .sql .ast .expression .Argument .ArgumentMap ;
83- import org .opensearch .sql .ast .expression .Cast ;
8484import org .opensearch .sql .ast .expression .Field ;
8585import org .opensearch .sql .ast .expression .Function ;
8686import org .opensearch .sql .ast .expression .Let ;
@@ -1900,7 +1900,6 @@ public RelNode visitRareTopN(RareTopN node, CalcitePlanContext context) {
19001900 // 1. group the group-by list + field list and add a count() aggregation
19011901 List <UnresolvedExpression > groupExprList = new ArrayList <>();
19021902 node .getGroupExprList ().forEach (exp -> groupExprList .add (exp ));
1903- // need alias for dynamic fields
19041903 node .getFields ().forEach (field -> groupExprList .add (field ));
19051904 groupExprList .forEach (expr -> projectDynamicField (expr , context ));
19061905 List <UnresolvedExpression > aggExprList =
@@ -2050,9 +2049,8 @@ public RelNode visitTimechart(
20502049 org .opensearch .sql .ast .tree .Timechart node , CalcitePlanContext context ) {
20512050 visitChildren (node , context );
20522051
2053- projectDynamicFieldAsString (
2054- node .getBinExpression (), context ); // spanExpr would become static field.
2055- projectDynamicFieldAsString (node .getByField (), context ); // byField would become static field.
2052+ projectDynamicFieldAsString (node .getBinExpression (), context );
2053+ projectDynamicFieldAsString (node .getByField (), context );
20562054
20572055 // Extract parameters
20582056 UnresolvedExpression spanExpr = node .getBinExpression ();
@@ -2095,8 +2093,7 @@ public RelNode visitTimechart(
20952093
20962094 // Extract parameters for byField case
20972095 UnresolvedExpression byField = node .getByField ();
2098-
2099- String byFieldName = ((Field ) node .getByField ()).getField ().toString ();
2096+ String byFieldName = ((Field ) byField ).getField ().toString ();
21002097 String valueFunctionName = getValueFunctionName (node .getAggregateFunction ());
21012098
21022099 int limit = Optional .ofNullable (node .getLimit ()).orElse (10 );
@@ -2150,25 +2147,34 @@ public RelNode visitTimechart(
21502147 }
21512148 }
21522149
2150+ /**
2151+ * Project dynamic field to static field and cast to string to make it easier to handle. It does
2152+ * nothing if exp does not refer dynamic field.
2153+ */
21532154 private void projectDynamicFieldAsString (UnresolvedExpression exp , CalcitePlanContext context ) {
2154- projectDynamicField (exp , true , context );
2155+ projectDynamicField (exp , context , node -> context . rexBuilder . castToString ( node ) );
21552156 }
21562157
2158+ /**
2159+ * Project dynamic field to static field to make it easier to handle. It does nothing if exp does
2160+ * not refer dynamic field.
2161+ */
21572162 private void projectDynamicField (UnresolvedExpression exp , CalcitePlanContext context ) {
2158- projectDynamicField (exp , false , context );
2163+ UnaryOperator <RexNode > noWrap = node -> node ;
2164+ projectDynamicField (exp , context , noWrap );
21592165 }
21602166
21612167 private void projectDynamicField (
2162- UnresolvedExpression exp , boolean castNeeded , CalcitePlanContext context ) {
2168+ UnresolvedExpression exp , CalcitePlanContext context , UnaryOperator < RexNode > nodeWrapper ) {
21632169 if (exp != null ) {
21642170 exp .accept (
21652171 new AbstractNodeVisitor <Void , CalcitePlanContext >() {
21662172 @ Override
21672173 public Void visitField (Field field , CalcitePlanContext context ) {
21682174 RexNode node = rexVisitor .analyze (field , context );
21692175 if (node .isA (SqlKind .ITEM )) {
2170- RexNode casted = castNeeded ? castToString ( node , context ) : node ;
2171- RexNode alias = context .relBuilder .alias (casted , field .getField ().toString ());
2176+ RexNode alias =
2177+ context .relBuilder .alias (nodeWrapper . apply ( node ) , field .getField ().toString ());
21722178 context .relBuilder .projectPlus (alias );
21732179 }
21742180 return null ;
@@ -2178,36 +2184,6 @@ public Void visitField(Field field, CalcitePlanContext context) {
21782184 }
21792185 }
21802186
2181- private RexNode castToString (RexNode node , CalcitePlanContext context ) {
2182- RelDataType stringType = context .rexBuilder .getTypeFactory ().createSqlType (SqlTypeName .VARCHAR );
2183- RelDataType nullableStringType =
2184- context .rexBuilder .getTypeFactory ().createTypeWithNullability (stringType , true );
2185- return context .rexBuilder .makeCast (nullableStringType , node , true , true );
2186- }
2187-
2188- /** Add cast and alias to Field. Needed for when the field is resolved as dynamic field access. */
2189- private UnresolvedExpression castAndAliasToFieldAccess (UnresolvedExpression exp ) {
2190- if (exp instanceof Field ) {
2191- Field f = (Field ) exp ;
2192- return AstDSL .alias (f .getField ().toString (), castToString (f ));
2193- } else {
2194- return castToString (exp );
2195- }
2196- }
2197-
2198- private UnresolvedExpression castToString (UnresolvedExpression exp ) {
2199- return new Cast (exp , AstDSL .stringLiteral ("STRING" ));
2200- }
2201-
2202- /** Add alias to Field. Needed for when the field is resolved as dynamic field access. */
2203- private UnresolvedExpression addAliasToFieldAccess (UnresolvedExpression exp ) {
2204- if (exp instanceof Field f ) {
2205- return AstDSL .alias (f .getField ().toString (), f );
2206- } else {
2207- return exp ;
2208- }
2209- }
2210-
22112187 /** Build top categories query - simpler approach that works better with OTHER handling */
22122188 private RelNode buildTopCategoriesQuery (
22132189 RelNode completeResults , int limit , CalcitePlanContext context ) {
0 commit comments