diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsRollupConfig.java b/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsRollupConfig.java index f2861b1872c..c7b6831341f 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsRollupConfig.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsRollupConfig.java @@ -7,6 +7,7 @@ import elemental2.core.JsArray; import elemental2.core.JsObject; import elemental2.core.JsString; +import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.Table_pb; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.hierarchicaltable_pb.RollupRequest; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.AggSpec; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.Aggregation; @@ -20,6 +21,7 @@ import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecLast; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecMax; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecMin; +import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecNonUniqueSentinel; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecStd; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecSum; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.aggspec.AggSpecUnique; @@ -58,7 +60,7 @@ public class JsRollupConfig { * Mapping from each aggregation name to the ordered list of columns it should be applied to in the resulting * roll-up table. */ - public JsPropertyMap> aggregations = + public JsPropertyMap> aggregations = Js.cast(JsObject.create(null)); /** * Optional parameter indicating if an extra leaf node should be added at the bottom of the hierarchy, showing the @@ -245,7 +247,11 @@ public RollupRequest buildRequest(JsArray tableColumns) { } case JsAggregationOperation.UNIQUE: { AggSpec spec = new AggSpec(); - spec.setUnique(new AggSpecUnique()); + AggSpecUnique unique = new AggSpecUnique(); + AggSpecNonUniqueSentinel sentinel = new AggSpecNonUniqueSentinel(); + sentinel.setNullValue(Table_pb.NullValue.getNULL_VALUE()); + unique.setNonUniqueSentinel(sentinel); + spec.setUnique(unique); columns = new AggregationColumns(); columns.setSpec(spec); agg.setColumns(columns); diff --git a/web/client-api/src/main/java/io/deephaven/web/public/rollup.html b/web/client-api/src/main/java/io/deephaven/web/public/rollup.html index 7256a080b92..cb531f3c197 100644 --- a/web/client-api/src/main/java/io/deephaven/web/public/rollup.html +++ b/web/client-api/src/main/java/io/deephaven/web/public/rollup.html @@ -55,14 +55,18 @@

Rollup Table Configuration

diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java b/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java index 3e244ba9cd8..ae3f63edfb9 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java @@ -3,8 +3,18 @@ // package io.deephaven.web.client.api; +import elemental2.core.JsArray; import elemental2.dom.CustomEvent; import io.deephaven.web.client.api.tree.JsTreeTable; +import elemental2.promise.Promise; +import io.deephaven.web.client.api.tree.JsRollupConfig; +import io.deephaven.web.client.api.tree.enums.JsAggregationOperation; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; public class HierarchicalTableTestGwt extends AbstractAsyncGwtTestCase { @Override @@ -13,11 +23,15 @@ public String getModuleName() { } private final TableSourceBuilder tables = new TableSourceBuilder() - .script("from deephaven import empty_table, time_table") + .script("from deephaven import empty_table, time_table, agg") .script("static_tree", "empty_table(1000).update(['ID=i', 'Parent=i == 0 ? null : (int)(i/10)']).tree('ID', 'Parent')") .script("ticking_tree", - "time_table('PT0.1s').update(['ID=i', 'Parent=i == 0 ? null : (int)(i/10)']).tree('ID', 'Parent')"); + "time_table('PT0.1s').update(['ID=i', 'Parent=i == 0 ? null : (int)(i/10)']).format_columns(['ID=ID>0 ? GREEN : RED']).tree('ID', 'Parent')") + .script("table_to_rollup", + "time_table('PT0.1s').update(['Y=Math.sin(i/3)', 'X=i%3']).format_columns(['Y=Y>0 ? GREEN : RED'])") + .script("ticking_rollup", + "table_to_rollup.rollup(aggs=[agg.first('Y')],by=['X'],include_constituents=True)"); public void testStaticTreeTable() { connect(tables) @@ -86,4 +100,50 @@ public void testRefreshingTreeTable() { }) .then(this::finish).catch_(this::report); } + + public void testCreateRollup() { + connect(tables) + .then(table("table_to_rollup")) + .then(table -> { + List>> tests = new ArrayList<>(); + // For each supported operation, apply it to the numeric column + // Then expand to verify data can load + String[] count = new String[] { + JsAggregationOperation.COUNT, + JsAggregationOperation.COUNT_DISTINCT, + // TODO(deephaven-core#6201) re-enable this line when fixed + // JsAggregationOperation.DISTINCT, + JsAggregationOperation.MIN, + JsAggregationOperation.MAX, + JsAggregationOperation.SUM, + JsAggregationOperation.ABS_SUM, + JsAggregationOperation.VAR, + JsAggregationOperation.AVG, + JsAggregationOperation.STD, + JsAggregationOperation.FIRST, + JsAggregationOperation.LAST, + JsAggregationOperation.UNIQUE + }; + for (int i = 0; i < count.length; i++) { + final int step = i; + String operation = count[i]; + JsRollupConfig cfg = new JsRollupConfig(); + cfg.groupingColumns = Js.uncheckedCast(JsArray.of("X")); + cfg.includeConstituents = true; + cfg.aggregations = (JsPropertyMap) JsPropertyMap.of(operation, JsArray.of("Y")); + tests.add(() -> table.rollup(cfg).then(r -> { + r.setViewport(0, 99, null, null); + delayTestFinish(15000 + step); + + return waitForEventWhere(r, JsTreeTable.EVENT_UPDATED, + (CustomEvent d) -> r.getSize() == 4, 13000 + step) + .then(event -> Promise.resolve(r)); + })); + } + + return tests.stream().reduce((p1, p2) -> () -> p1.get().then(result -> p2.get())).get().get(); + }) + .then(this::finish).catch_(this::report); + } + }