|
3 | 3 | // |
4 | 4 | package io.deephaven.engine.table.impl.by; |
5 | 5 |
|
6 | | -import io.deephaven.api.ColumnName; |
7 | | -import io.deephaven.api.Pair; |
8 | | -import io.deephaven.api.SortColumn; |
| 6 | +import io.deephaven.api.*; |
9 | 7 | import io.deephaven.api.agg.*; |
10 | 8 | import io.deephaven.api.agg.spec.AggSpec; |
11 | 9 | import io.deephaven.api.agg.spec.AggSpecAbsSum; |
|
93 | 91 | import io.deephaven.engine.table.impl.by.ssmcountdistinct.unique.ShortRollupUniqueOperator; |
94 | 92 | import io.deephaven.engine.table.impl.by.ssmminmax.SsmChunkedMinMaxOperator; |
95 | 93 | import io.deephaven.engine.table.impl.by.ssmpercentile.SsmChunkedPercentileOperator; |
| 94 | +import io.deephaven.engine.table.impl.select.SelectColumn; |
96 | 95 | import io.deephaven.engine.table.impl.sources.ReinterpretUtils; |
97 | 96 | import io.deephaven.engine.table.impl.ssms.SegmentedSortedMultiSet; |
98 | 97 | import io.deephaven.engine.table.impl.util.freezeby.FreezeByCountOperator; |
99 | 98 | import io.deephaven.engine.table.impl.util.freezeby.FreezeByOperator; |
100 | 99 | import io.deephaven.time.DateTimeUtils; |
101 | 100 | import io.deephaven.util.annotations.FinalDefault; |
102 | 101 | import io.deephaven.util.type.ArrayTypeUtils; |
| 102 | +import io.deephaven.vector.VectorFactory; |
103 | 103 | import org.apache.commons.lang3.mutable.MutableBoolean; |
104 | 104 | import org.jetbrains.annotations.NotNull; |
105 | 105 | import org.jetbrains.annotations.Nullable; |
|
113 | 113 | import java.util.Collections; |
114 | 114 | import java.util.List; |
115 | 115 | import java.util.Map; |
| 116 | +import java.util.Set; |
116 | 117 | import java.util.function.BiFunction; |
117 | 118 | import java.util.function.Function; |
118 | 119 | import java.util.function.Supplier; |
@@ -153,6 +154,13 @@ private enum Type { |
153 | 154 | private final Collection<? extends Aggregation> aggregations; |
154 | 155 | private final Type type; |
155 | 156 |
|
| 157 | + /** |
| 158 | + * For {@link Formula formula} aggregations we need a representation of the table definition with the column data |
| 159 | + * types converted to {@link io.deephaven.vector.Vector vectors}. This can be computed once and re-used across all |
| 160 | + * formula aggregations. |
| 161 | + */ |
| 162 | + private Map<String, ColumnDefinition<?>> vectorColumnDefinitions; |
| 163 | + |
156 | 164 | /** |
157 | 165 | * Convert a collection of {@link Aggregation aggregations} to an {@link AggregationContextFactory}. |
158 | 166 | * |
@@ -707,6 +715,50 @@ public void visit(@NotNull final Partition partition) { |
707 | 715 | groupByColumnNames)); |
708 | 716 | } |
709 | 717 |
|
| 718 | + @Override |
| 719 | + public void visit(@NotNull final Formula formula) { |
| 720 | + final SelectColumn selectColumn = SelectColumn.of(formula.selectable()); |
| 721 | + |
| 722 | + // Get or create a column definition map composed of vectors of the original column types (or scalars when |
| 723 | + // part of the key columns). |
| 724 | + final Set<String> groupByColumnSet = Set.of(groupByColumnNames); |
| 725 | + if (vectorColumnDefinitions == null) { |
| 726 | + vectorColumnDefinitions = table.getDefinition().getColumnStream().collect(Collectors.toMap( |
| 727 | + ColumnDefinition::getName, |
| 728 | + (final ColumnDefinition<?> cd) -> groupByColumnSet.contains(cd.getName()) |
| 729 | + ? cd |
| 730 | + : ColumnDefinition.fromGenericType( |
| 731 | + cd.getName(), |
| 732 | + VectorFactory.forElementType(cd.getDataType()).vectorType(), |
| 733 | + cd.getDataType()))); |
| 734 | + } |
| 735 | + |
| 736 | + // Get the input column names from the formula and provide them to the groupBy operator |
| 737 | + final String[] allInputColumns = |
| 738 | + selectColumn.initDef(vectorColumnDefinitions, compilationProcessor).toArray(String[]::new); |
| 739 | + |
| 740 | + final Map<Boolean, List<String>> partitioned = Arrays.stream(allInputColumns) |
| 741 | + .collect(Collectors.partitioningBy(groupByColumnSet::contains)); |
| 742 | + final String[] inputKeyColumns = partitioned.get(true).toArray(String[]::new); |
| 743 | + final String[] inputNonKeyColumns = partitioned.get(false).toArray(String[]::new); |
| 744 | + |
| 745 | + if (!selectColumn.getColumnArrays().isEmpty()) { |
| 746 | + throw new IllegalArgumentException("AggFormula does not support column arrays (" |
| 747 | + + selectColumn.getColumnArrays() + ")"); |
| 748 | + } |
| 749 | + if (selectColumn.hasVirtualRowVariables()) { |
| 750 | + throw new IllegalArgumentException("AggFormula does not support virtual row variables"); |
| 751 | + } |
| 752 | + // TODO: re-use shared groupBy operators (https://github.com/deephaven/deephaven-core/issues/6363) |
| 753 | + final GroupByChunkedOperator groupByChunkedOperator = new GroupByChunkedOperator(table, false, null, |
| 754 | + Arrays.stream(inputNonKeyColumns).map(col -> MatchPair.of(Pair.parse(col))) |
| 755 | + .toArray(MatchPair[]::new)); |
| 756 | + |
| 757 | + final FormulaMultiColumnChunkedOperator op = new FormulaMultiColumnChunkedOperator(table, |
| 758 | + groupByChunkedOperator, true, selectColumn, inputKeyColumns); |
| 759 | + addNoInputOperator(op); |
| 760 | + } |
| 761 | + |
710 | 762 | // ------------------------------------------------------------------------------------------------------------- |
711 | 763 | // AggSpec.Visitor |
712 | 764 | // ------------------------------------------------------------------------------------------------------------- |
@@ -745,6 +797,7 @@ public void visit(@NotNull final AggSpecFirst first) { |
745 | 797 | @Override |
746 | 798 | public void visit(@NotNull final AggSpecFormula formula) { |
747 | 799 | unsupportedForBlinkTables("Formula"); |
| 800 | + // TODO: re-use shared groupBy operators (https://github.com/deephaven/deephaven-core/issues/6363) |
748 | 801 | final GroupByChunkedOperator groupByChunkedOperator = new GroupByChunkedOperator(table, false, null, |
749 | 802 | resultPairs.stream().map(pair -> MatchPair.of((Pair) pair.input())).toArray(MatchPair[]::new)); |
750 | 803 | final FormulaChunkedOperator formulaChunkedOperator = new FormulaChunkedOperator(groupByChunkedOperator, |
@@ -860,6 +913,12 @@ default void visit(@NotNull final LastRowKey lastRowKey) { |
860 | 913 | rollupUnsupported("LastRowKey"); |
861 | 914 | } |
862 | 915 |
|
| 916 | + @Override |
| 917 | + @FinalDefault |
| 918 | + default void visit(@NotNull final Formula formula) { |
| 919 | + rollupUnsupported("Formula"); |
| 920 | + } |
| 921 | + |
863 | 922 | // ------------------------------------------------------------------------------------------------------------- |
864 | 923 | // AggSpec.Visitor for unsupported column aggregation specs |
865 | 924 | // ------------------------------------------------------------------------------------------------------------- |
|
0 commit comments