|
28 | 28 | import org.apache.calcite.plan.ViewExpanders; |
29 | 29 | import org.apache.calcite.rel.RelNode; |
30 | 30 | import org.apache.calcite.rel.core.Aggregate; |
| 31 | +import org.apache.calcite.rel.core.JoinRelType; |
31 | 32 | import org.apache.calcite.rel.type.RelDataTypeField; |
32 | 33 | import org.apache.calcite.rex.RexCall; |
33 | 34 | import org.apache.calcite.rex.RexCorrelVariable; |
@@ -164,7 +165,7 @@ public RelNode visitProject(Project node, CalcitePlanContext context) { |
164 | 165 | } |
165 | 166 |
|
166 | 167 | /** See logic in {@link org.opensearch.sql.analysis.symbol.SymbolTable#lookupAllFields} */ |
167 | | - private void tryToRemoveNestedFields(CalcitePlanContext context) { |
| 168 | + private static void tryToRemoveNestedFields(CalcitePlanContext context) { |
168 | 169 | Set<String> allFields = new HashSet<>(context.relBuilder.peek().getRowType().getFieldNames()); |
169 | 170 | List<RexNode> duplicatedNestedFields = |
170 | 171 | allFields.stream() |
@@ -824,8 +825,52 @@ public RelNode visitTrendline(Trendline node, CalcitePlanContext context) { |
824 | 825 | throw new CalciteUnsupportedException("Trendline command is unsupported in Calcite"); |
825 | 826 | } |
826 | 827 |
|
| 828 | + /** |
| 829 | + * Expand command visitor to handle array field expansion. 1. Unnest 2. Join with the original |
| 830 | + * table to get all fields |
| 831 | + * |
| 832 | + * <p>S = π_{field, other_fields}(R ⨝ UNNEST_field(R)) |
| 833 | + * |
| 834 | + * @param expand Expand command to be visited |
| 835 | + * @param context CalcitePlanContext containing the RelBuilder and other context |
| 836 | + * @return RelNode representing records with the expanded array field |
| 837 | + */ |
827 | 838 | @Override |
828 | 839 | public RelNode visitExpand(Expand expand, CalcitePlanContext context) { |
829 | | - throw new CalciteUnsupportedException("Expand command is unsupported in Calcite"); |
| 840 | + // 1. Visit Children |
| 841 | + visitChildren(expand, context); |
| 842 | + |
| 843 | + var relBuilder = context.relBuilder; |
| 844 | + |
| 845 | + // 3. Get the field to expand |
| 846 | + Field arrayField = expand.getField(); |
| 847 | + |
| 848 | + // 5. Unnest the array field |
| 849 | + // Analyze the array field to get its RexNode |
| 850 | + RexNode arrayFieldRex = rexVisitor.analyze(arrayField, context); |
| 851 | + |
| 852 | + // Push the original table to the RelBuilder stack |
| 853 | + RelNode originalTable = relBuilder.peek(); |
| 854 | + // No alias is provided in the expand command, so we remove the original array field, |
| 855 | + // then replace it with the unnest result. |
| 856 | + relBuilder.projectExcept(arrayFieldRex); |
| 857 | + relBuilder.push(originalTable); |
| 858 | + |
| 859 | + // Join on ROW_NUMBER_COLUMN_NAME |
| 860 | + Holder<RexCorrelVariable> correlVariable = Holder.empty(); |
| 861 | + relBuilder.variable(correlVariable::set); |
| 862 | + |
| 863 | + relBuilder.project(List.of(arrayFieldRex), List.of(), false, List.of(correlVariable.get().id)); |
| 864 | + // Alias is not supported in expand yet, we pass in an empty list |
| 865 | + relBuilder.uncollect(List.of(), false); |
| 866 | + |
| 867 | + List<RexNode> allFields = |
| 868 | + relBuilder.peek().getRowType().getFieldList().stream() |
| 869 | + .map(f -> (RexNode) relBuilder.field(f.getName())) |
| 870 | + .toList(); |
| 871 | + |
| 872 | + relBuilder.correlate(JoinRelType.INNER, correlVariable.get().id, relBuilder.fields()); |
| 873 | + |
| 874 | + return relBuilder.peek(); |
830 | 875 | } |
831 | 876 | } |
0 commit comments