Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore totals table support in the JS API #3662

Merged
merged 11 commits into from
Jun 2, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -606,14 +606,14 @@ public static Collection<? extends Aggregation> makeAggregations(Table source, T

final Set<AggType> defaultOperations = EnumSet.of(builder.defaultOperation);
final Map<AggType, List<String>> columnsByType = new LinkedHashMap<>();
for (final Map.Entry<String, ? extends ColumnSource> entry : source.getColumnSourceMap().entrySet()) {
for (final Map.Entry<String, ? extends ColumnSource<?>> entry : source.getColumnSourceMap().entrySet()) {
final String columnName = entry.getKey();
if (ColumnFormatting.isFormattingColumn(columnName)) {
continue;
}

final Set<AggType> operations = builder.operationMap.getOrDefault(columnName, defaultOperations);
final Class type = entry.getValue().getType();
final Class<?> type = entry.getValue().getType();

for (final AggType op : operations) {
if (operationApplies(type, op)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.deephaven.web.client.api;

import elemental2.core.JsArray;
import elemental2.promise.Promise;
import io.deephaven.web.client.state.ClientTableState;
import jsinterop.annotations.JsIgnore;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsOptional;
import jsinterop.annotations.JsType;

@JsType(namespace = "dh")
public interface JoinableTable {
@JsIgnore
ClientTableState state();

@JsMethod
Promise<JsTable> freeze();

@JsMethod
Promise<JsTable> snapshot(JsTable baseTable, @JsOptional Boolean doInitialSnapshot,
@JsOptional String[] stampColumns);

@JsMethod
@Deprecated
Promise<JsTable> join(Object joinType, JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional Object asOfMatchRule);

@JsMethod
Promise<JsTable> asOfJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional String asOfMatchRule);

@JsMethod
Promise<JsTable> crossJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional Double reserve_bits);

@JsMethod
Promise<JsTable> exactJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd);

@JsMethod
Promise<JsTable> naturalJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd);
}
102 changes: 71 additions & 31 deletions web/client-api/src/main/java/io/deephaven/web/client/api/JsTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,30 @@
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.object_pb.FetchObjectRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.partitionedtable_pb.PartitionByRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.partitionedtable_pb.PartitionByResponse;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.AggregateRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.AsOfJoinTablesRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.BatchTableRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.CrossJoinTablesRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.ExactJoinTablesRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.ExportedTableCreationResponse;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.Literal;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.NaturalJoinTablesRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.RunChartDownsampleRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.SeekRowRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.SeekRowResponse;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.SelectDistinctRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.SelectOrUpdateRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.SnapshotTableRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.SnapshotWhenTableRequest;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.TableReference;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.batchtablerequest.Operation;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.runchartdownsamplerequest.ZoomRange;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb_service.ResponseStream;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.Ticket;
import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.TypedTicket;
import io.deephaven.web.client.api.barrage.def.ColumnDefinition;
import io.deephaven.web.client.api.barrage.def.TableAttributesDefinition;
import io.deephaven.web.client.api.barrage.stream.ResponseStreamWrapper;
import io.deephaven.web.client.api.batch.RequestBatcher;
import io.deephaven.web.client.api.console.JsVariableType;
import io.deephaven.web.client.api.filter.FilterCondition;
Expand Down Expand Up @@ -79,7 +87,7 @@
* handle/viewport.
*/
@TsName(namespace = "dh", name = "Table")
public class JsTable extends HasLifecycle implements HasTableBinding {
public class JsTable extends HasLifecycle implements HasTableBinding, JoinableTable {
@JsProperty(namespace = "dh.Table")
public static final String EVENT_SIZECHANGED = "sizechanged",
EVENT_UPDATED = "updated",
Expand Down Expand Up @@ -612,18 +620,16 @@ public Promise<JsTable> copy(boolean resolved) {
return copy();
}

// TODO: #37: Need SmartKey support for this functionality
// @JsMethod
@JsMethod
public Promise<JsTotalsTable> getTotalsTable(
/* @JsOptional @JsNullable */ @TsTypeRef(JsTotalsTableConfig.class) Object config) {
@JsOptional @JsNullable @TsTypeRef(JsTotalsTableConfig.class) Object config) {
// fetch the handle and wrap it in a new jstable. listen for changes
// on the parent table, and re-fetch each time.

return fetchTotals(config, this::lastVisibleState);
}

// TODO: #37: Need SmartKey support for this functionality
// @JsMethod
@JsMethod
public JsTotalsTableConfig getTotalsTableConfig() {
// we want to communicate to the JS dev that there is no default config, so we allow
// returning null here, rather than a default config. They can then easily build a
Expand All @@ -633,7 +639,6 @@ public JsTotalsTableConfig getTotalsTableConfig() {
}

private Promise<JsTotalsTable> fetchTotals(Object config, JsProvider<ClientTableState> state) {

JsTotalsTableConfig directive = getTotalsDirectiveFromOptionalConfig(config);
ClientTableState[] lastGood = {null};
final JsTableFetch totalsFactory = (callback, newState, metadata) -> {
Expand All @@ -655,14 +660,44 @@ private Promise<JsTotalsTable> fetchTotals(Object config, JsProvider<ClientTable
JsLog.debug("Sending totals table fetch ", directive, " for ", target,
"(", LazyString.of(target::getHandle), "), into ", LazyString.of(newState::getHandle), "(",
newState, ")");
// workerConnection.getServer().fetchTotalsTable(
// target.getHandle(),
// newState.getHandle(),
// directive.serialize(),
// directive.groupByArray(),
// callback
// );
throw new UnsupportedOperationException("totalsTables");

AggregateRequest requestMessage = directive.buildRequest(getColumns());
JsArray<String> updateViewExprs = directive.getCustomColumns();
JsArray<String> dropColumns = directive.getDropColumns();
requestMessage.setSourceId(target.getHandle().makeTableReference());
requestMessage.setResultId(newState.getHandle().makeTicket());
if (updateViewExprs != null && updateViewExprs.length != 0) {
SelectOrUpdateRequest columnExpr = new SelectOrUpdateRequest();
columnExpr.setResultId(requestMessage.getResultId());
requestMessage.setResultId();
columnExpr.setColumnSpecsList(updateViewExprs);
TableReference prev = new TableReference();
prev.setBatchOffset(0);
columnExpr.setSourceId(prev);
BatchTableRequest batch = new BatchTableRequest();
Operation aggOp = new Operation();
aggOp.setAggregate(requestMessage);
Operation colsOp = new Operation();
colsOp.setUpdateView(columnExpr);
batch.addOps(aggOp);
batch.addOps(colsOp);
ResponseStreamWrapper<ExportedTableCreationResponse> stream = ResponseStreamWrapper
.of(workerConnection.tableServiceClient().batch(batch, workerConnection.metadata()));
stream.onData(creationResponse -> {
if (creationResponse.getResultId().hasTicket()) {
// represents the final output
callback.apply(null, creationResponse);
}
});
stream.onEnd(status -> {
if (!status.isOk()) {
callback.apply(status, null);
}
});
} else {
workerConnection.tableServiceClient().aggregate(requestMessage, workerConnection.metadata(),
callback::apply);
}
};
String summary = "totals table " + directive + ", " + directive.groupBy.join(",");
final ClientTableState totals = workerConnection.newState(totalsFactory, summary);
Expand Down Expand Up @@ -748,10 +783,9 @@ private JsTotalsTableConfig getTotalsDirectiveFromOptionalConfig(Object config)
}
}

// TODO: #37: Need SmartKey support for this functionality
// @JsMethod
@JsMethod
public Promise<JsTotalsTable> getGrandTotalsTable(
/* @JsNullable @JsOptional */ @TsTypeRef(JsTotalsTableConfig.class) Object config) {
@JsOptional @JsNullable @TsTypeRef(JsTotalsTableConfig.class) Object config) {
// As in getTotalsTable, but this time we want to skip any filters - this could mean use the
// most-derived table which has no filter, or the least-derived table which has all custom columns.
// Currently, these two mean the same thing.
Expand Down Expand Up @@ -849,6 +883,7 @@ public Promise<JsTable> freeze() {
.then(state -> Promise.resolve(new JsTable(workerConnection, state)));
}

@Override
@JsMethod
public Promise<JsTable> snapshot(JsTable baseTable, @JsOptional Boolean doInitialSnapshot,
@JsOptional String[] stampColumns) {
Expand All @@ -859,12 +894,12 @@ public Promise<JsTable> snapshot(JsTable baseTable, @JsOptional Boolean doInitia
} else {
realDoInitialSnapshot = true;
}
final String[] realStampColums;
final String[] realStampColumns;
if (stampColumns == null) {
realStampColums = new String[0]; // server doesn't like null
realStampColumns = new String[0]; // server doesn't like null
} else {
// make sure we pass an actual string array
realStampColums = Arrays.stream(stampColumns).toArray(String[]::new);
realStampColumns = Arrays.stream(stampColumns).toArray(String[]::new);
}
final String fetchSummary =
"snapshot(" + baseTable + ", " + doInitialSnapshot + ", " + Arrays.toString(stampColumns) + ")";
Expand All @@ -874,16 +909,17 @@ public Promise<JsTable> snapshot(JsTable baseTable, @JsOptional Boolean doInitia
request.setTriggerId(state().getHandle().makeTableReference());
request.setResultId(state.getHandle().makeTicket());
request.setInitial(realDoInitialSnapshot);
request.setStampColumnsList(realStampColums);
request.setStampColumnsList(realStampColumns);

workerConnection.tableServiceClient().snapshotWhen(request, metadata, c::apply);
}, fetchSummary).refetch(this, workerConnection.metadata())
.then(state -> Promise.resolve(new JsTable(workerConnection, state)));
}

@Override
@JsMethod
@Deprecated
public Promise<JsTable> join(Object joinType, JsTable rightTable, JsArray<String> columnsToMatch,
public Promise<JsTable> join(Object joinType, JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional @JsNullable JsArray<String> columnsToAdd, @JsOptional @JsNullable Object asOfMatchRule) {
if (joinType.equals("AJ") || joinType.equals("RAJ")) {
return asOfJoin(rightTable, columnsToMatch, columnsToAdd, (String) asOfMatchRule);
Expand All @@ -898,10 +934,11 @@ public Promise<JsTable> join(Object joinType, JsTable rightTable, JsArray<String
}
}

@Override
@JsMethod
public Promise<JsTable> asOfJoin(JsTable rightTable, JsArray<String> columnsToMatch,
public Promise<JsTable> asOfJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional @JsNullable JsArray<String> columnsToAdd, @JsOptional @JsNullable String asOfMatchRule) {
if (rightTable.workerConnection != workerConnection) {
if (rightTable.state().getConnection() != workerConnection) {
throw new IllegalStateException(
"Table argument passed to join is not from the same worker as current table");
}
Expand All @@ -922,10 +959,11 @@ public Promise<JsTable> asOfJoin(JsTable rightTable, JsArray<String> columnsToMa
.then(state -> Promise.resolve(new JsTable(workerConnection, state)));
}

@Override
@JsMethod
public Promise<JsTable> crossJoin(JsTable rightTable, JsArray<String> columnsToMatch,
public Promise<JsTable> crossJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional Double reserve_bits) {
if (rightTable.workerConnection != workerConnection) {
if (rightTable.state().getConnection() != workerConnection) {
throw new IllegalStateException(
"Table argument passed to join is not from the same worker as current table");
}
Expand All @@ -945,10 +983,11 @@ public Promise<JsTable> crossJoin(JsTable rightTable, JsArray<String> columnsToM
.then(state -> Promise.resolve(new JsTable(workerConnection, state)));
}

@Override
@JsMethod
public Promise<JsTable> exactJoin(JsTable rightTable, JsArray<String> columnsToMatch,
public Promise<JsTable> exactJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd) {
if (rightTable.workerConnection != workerConnection) {
if (rightTable.state().getConnection() != workerConnection) {
throw new IllegalStateException(
"Table argument passed to join is not from the same worker as current table");
}
Expand All @@ -965,10 +1004,11 @@ public Promise<JsTable> exactJoin(JsTable rightTable, JsArray<String> columnsToM
.then(state -> Promise.resolve(new JsTable(workerConnection, state)));
}

@Override
@JsMethod
public Promise<JsTable> naturalJoin(JsTable rightTable, JsArray<String> columnsToMatch,
public Promise<JsTable> naturalJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd) {
if (rightTable.workerConnection != workerConnection) {
if (rightTable.state().getConnection() != workerConnection) {
throw new IllegalStateException(
"Table argument passed to join is not from the same worker as current table");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import elemental2.dom.Event;
import elemental2.promise.Promise;
import io.deephaven.web.client.api.filter.FilterCondition;
import io.deephaven.web.client.state.ClientTableState;
import io.deephaven.web.shared.fu.RemoverFn;
import jsinterop.annotations.JsIgnore;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsOptional;
import jsinterop.annotations.JsProperty;
Expand All @@ -28,7 +30,7 @@
*/
@TsInterface
@TsName(namespace = "dh", name = "TotalsTable")
public class JsTotalsTable {
public class JsTotalsTable implements JoinableTable {
private final JsTable wrappedTable;
private final String directive;
private final JsArray<JsString> groupBy;
Expand Down Expand Up @@ -57,6 +59,12 @@ public void refreshViewport() {
}
}

@JsIgnore
@Override
public ClientTableState state() {
return wrappedTable.state();
}

@JsProperty
public JsTotalsTableConfig getTotalsTableConfig() {
JsTotalsTableConfig parsed = JsTotalsTableConfig.parse(directive);
Expand Down Expand Up @@ -163,6 +171,52 @@ public JsArray<CustomColumn> getCustomColumns() {
return wrappedTable.getCustomColumns();
}

@Override
@JsMethod
public Promise<JsTable> freeze() {
return wrappedTable.freeze();
}

@Override
@JsMethod
public Promise<JsTable> snapshot(JsTable baseTable, @JsOptional Boolean doInitialSnapshot,
@JsOptional String[] stampColumns) {
return wrappedTable.snapshot(baseTable, doInitialSnapshot, stampColumns);
}

@Override
@Deprecated
@JsMethod
public Promise<JsTable> join(Object joinType, JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional Object asOfMatchRule) {
return wrappedTable.join(joinType, rightTable, columnsToMatch, columnsToAdd, asOfMatchRule);
}

@Override
@JsMethod
public Promise<JsTable> asOfJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional String asOfMatchRule) {
return wrappedTable.asOfJoin(rightTable, columnsToMatch, columnsToAdd, asOfMatchRule);
}

@Override
@JsMethod
public Promise<JsTable> crossJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd, @JsOptional Double reserve_bits) {
return wrappedTable.crossJoin(rightTable, columnsToMatch, columnsToAdd, reserve_bits);
}

@Override
@JsMethod
public Promise<JsTable> exactJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd) {
return wrappedTable.exactJoin(rightTable, columnsToMatch, columnsToAdd);
}

@Override
@JsMethod
public Promise<JsTable> naturalJoin(JoinableTable rightTable, JsArray<String> columnsToMatch,
@JsOptional JsArray<String> columnsToAdd) {
return wrappedTable.naturalJoin(rightTable, columnsToMatch, columnsToAdd);
}
}
Loading