10
10
import static org .opensearch .sql .ast .tree .Sort .NullOrder .NULL_LAST ;
11
11
import static org .opensearch .sql .ast .tree .Sort .SortOrder .ASC ;
12
12
import static org .opensearch .sql .ast .tree .Sort .SortOrder .DESC ;
13
+ import static org .opensearch .sql .data .type .ExprCoreType .DATE ;
13
14
import static org .opensearch .sql .data .type .ExprCoreType .STRUCT ;
15
+ import static org .opensearch .sql .data .type .ExprCoreType .TIME ;
16
+ import static org .opensearch .sql .data .type .ExprCoreType .TIMESTAMP ;
14
17
import static org .opensearch .sql .utils .MLCommonsConstants .RCF_ANOMALOUS ;
15
18
import static org .opensearch .sql .utils .MLCommonsConstants .RCF_ANOMALY_GRADE ;
16
19
import static org .opensearch .sql .utils .MLCommonsConstants .RCF_SCORE ;
22
25
import com .google .common .collect .ImmutableMap ;
23
26
import com .google .common .collect .ImmutableSet ;
24
27
import java .util .ArrayList ;
28
+ import java .util .Collections ;
25
29
import java .util .List ;
26
30
import java .util .Objects ;
27
31
import java .util .Optional ;
62
66
import org .opensearch .sql .ast .tree .Sort ;
63
67
import org .opensearch .sql .ast .tree .Sort .SortOption ;
64
68
import org .opensearch .sql .ast .tree .TableFunction ;
69
+ import org .opensearch .sql .ast .tree .Trendline ;
65
70
import org .opensearch .sql .ast .tree .UnresolvedPlan ;
66
71
import org .opensearch .sql .ast .tree .Values ;
67
72
import org .opensearch .sql .common .antlr .SyntaxCheckException ;
100
105
import org .opensearch .sql .planner .logical .LogicalRemove ;
101
106
import org .opensearch .sql .planner .logical .LogicalRename ;
102
107
import org .opensearch .sql .planner .logical .LogicalSort ;
108
+ import org .opensearch .sql .planner .logical .LogicalTrendline ;
103
109
import org .opensearch .sql .planner .logical .LogicalValues ;
104
110
import org .opensearch .sql .planner .physical .datasource .DataSourceTable ;
105
111
import org .opensearch .sql .storage .Table ;
@@ -469,23 +475,7 @@ public LogicalPlan visitParse(Parse node, AnalysisContext context) {
469
475
@ Override
470
476
public LogicalPlan visitSort (Sort node , AnalysisContext context ) {
471
477
LogicalPlan child = node .getChild ().get (0 ).accept (this , context );
472
- ExpressionReferenceOptimizer optimizer =
473
- new ExpressionReferenceOptimizer (expressionAnalyzer .getRepository (), child );
474
-
475
- List <Pair <SortOption , Expression >> sortList =
476
- node .getSortList ().stream ()
477
- .map (
478
- sortField -> {
479
- var analyzed = expressionAnalyzer .analyze (sortField .getField (), context );
480
- if (analyzed == null ) {
481
- throw new UnsupportedOperationException (
482
- String .format ("Invalid use of expression %s" , sortField .getField ()));
483
- }
484
- Expression expression = optimizer .optimize (analyzed , context );
485
- return ImmutablePair .of (analyzeSortOption (sortField .getFieldArgs ()), expression );
486
- })
487
- .collect (Collectors .toList ());
488
- return new LogicalSort (child , sortList );
478
+ return buildSort (child , context , node .getSortList ());
489
479
}
490
480
491
481
/** Build {@link LogicalDedupe}. */
@@ -594,6 +584,55 @@ public LogicalPlan visitML(ML node, AnalysisContext context) {
594
584
return new LogicalML (child , node .getArguments ());
595
585
}
596
586
587
+ /** Build {@link LogicalTrendline} for Trendline command. */
588
+ @ Override
589
+ public LogicalPlan visitTrendline (Trendline node , AnalysisContext context ) {
590
+ final LogicalPlan child = node .getChild ().get (0 ).accept (this , context );
591
+
592
+ final TypeEnvironment currEnv = context .peek ();
593
+ final List <Trendline .TrendlineComputation > computations = node .getComputations ();
594
+ final ImmutableList .Builder <Pair <Trendline .TrendlineComputation , ExprCoreType >>
595
+ computationsAndTypes = ImmutableList .builder ();
596
+ computations .forEach (
597
+ computation -> {
598
+ final Expression resolvedField =
599
+ expressionAnalyzer .analyze (computation .getDataField (), context );
600
+ final ExprCoreType averageType ;
601
+ // Duplicate the semantics of AvgAggregator#create():
602
+ // - All numerical types have the DOUBLE type for the moving average.
603
+ // - All datetime types have the same datetime type for the moving average.
604
+ if (ExprCoreType .numberTypes ().contains (resolvedField .type ())) {
605
+ averageType = ExprCoreType .DOUBLE ;
606
+ } else {
607
+ switch (resolvedField .type ()) {
608
+ case DATE :
609
+ case TIME :
610
+ case TIMESTAMP :
611
+ averageType = (ExprCoreType ) resolvedField .type ();
612
+ break ;
613
+ default :
614
+ throw new SemanticCheckException (
615
+ String .format (
616
+ "Invalid field used for trendline computation %s. Source field %s had type"
617
+ + " %s but must be a numerical or datetime field." ,
618
+ computation .getAlias (),
619
+ computation .getDataField ().getChild ().get (0 ),
620
+ resolvedField .type ().typeName ()));
621
+ }
622
+ }
623
+ currEnv .define (new Symbol (Namespace .FIELD_NAME , computation .getAlias ()), averageType );
624
+ computationsAndTypes .add (Pair .of (computation , averageType ));
625
+ });
626
+
627
+ if (node .getSortByField ().isEmpty ()) {
628
+ return new LogicalTrendline (child , computationsAndTypes .build ());
629
+ }
630
+
631
+ return new LogicalTrendline (
632
+ buildSort (child , context , Collections .singletonList (node .getSortByField ().get ())),
633
+ computationsAndTypes .build ());
634
+ }
635
+
597
636
@ Override
598
637
public LogicalPlan visitPaginate (Paginate paginate , AnalysisContext context ) {
599
638
LogicalPlan child = paginate .getChild ().get (0 ).accept (this , context );
@@ -612,6 +651,27 @@ public LogicalPlan visitCloseCursor(CloseCursor closeCursor, AnalysisContext con
612
651
return new LogicalCloseCursor (closeCursor .getChild ().get (0 ).accept (this , context ));
613
652
}
614
653
654
+ private LogicalSort buildSort (
655
+ LogicalPlan child , AnalysisContext context , List <Field > sortFields ) {
656
+ ExpressionReferenceOptimizer optimizer =
657
+ new ExpressionReferenceOptimizer (expressionAnalyzer .getRepository (), child );
658
+
659
+ List <Pair <SortOption , Expression >> sortList =
660
+ sortFields .stream ()
661
+ .map (
662
+ sortField -> {
663
+ var analyzed = expressionAnalyzer .analyze (sortField .getField (), context );
664
+ if (analyzed == null ) {
665
+ throw new UnsupportedOperationException (
666
+ String .format ("Invalid use of expression %s" , sortField .getField ()));
667
+ }
668
+ Expression expression = optimizer .optimize (analyzed , context );
669
+ return ImmutablePair .of (analyzeSortOption (sortField .getFieldArgs ()), expression );
670
+ })
671
+ .collect (Collectors .toList ());
672
+ return new LogicalSort (child , sortList );
673
+ }
674
+
615
675
/**
616
676
* The first argument is always "asc", others are optional. Given nullFirst argument, use its
617
677
* value. Otherwise just use DEFAULT_ASC/DESC.
0 commit comments