@pythia-software/query-table-react 0.9.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -17
- package/dist/index.d.ts +23 -12
- package/dist/index.js +280 -404
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -339,12 +339,15 @@ import {
|
|
|
339
339
|
createGroupingModel,
|
|
340
340
|
prepareGrouping,
|
|
341
341
|
withoutGrouping as withoutGrouping2,
|
|
342
|
-
sourceExecutionSchema as
|
|
343
|
-
toQueryRequestV3 as
|
|
344
|
-
validateQueryResultV3 as
|
|
345
|
-
|
|
342
|
+
sourceExecutionSchema as sourceExecutionSchema3,
|
|
343
|
+
toQueryRequestV3 as toQueryRequestV33,
|
|
344
|
+
validateQueryResultV3 as validateQueryResultV33,
|
|
345
|
+
fetchDistinctValuesV3,
|
|
346
|
+
fetchFieldStatsV3,
|
|
347
|
+
fetchRowV3,
|
|
348
|
+
isPushdownFilter
|
|
346
349
|
} from "@pythia-software/query-table-core";
|
|
347
|
-
import { ITEM_DETAILS_COLUMN, itemPageFields } from "@pythia-software/query-table-core";
|
|
350
|
+
import { ITEM_DETAILS_COLUMN, itemPageFields, QueryError as QueryError2 } from "@pythia-software/query-table-core";
|
|
348
351
|
|
|
349
352
|
// src/useItemDetails.ts
|
|
350
353
|
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
@@ -577,6 +580,10 @@ function useDetailProperties(mode, schema, config, query, setQuery, enabled, rea
|
|
|
577
580
|
}, [enabled, queryViewKey, localViewKey, properties.loading, properties.setView, setQuery]);
|
|
578
581
|
return properties;
|
|
579
582
|
}
|
|
583
|
+
function projectedFields(schema, row) {
|
|
584
|
+
const keys = new Set(Object.keys(row));
|
|
585
|
+
return schema.fields.filter((field) => keys.has(field.name) || keys.has((field.source.kind === "backend" ? field.source.path ?? field.name : field.name).split(".")[0])).map((field) => field.name);
|
|
586
|
+
}
|
|
580
587
|
function useItemDetails({
|
|
581
588
|
schema,
|
|
582
589
|
query,
|
|
@@ -585,7 +592,7 @@ function useItemDetails({
|
|
|
585
592
|
rows,
|
|
586
593
|
clientRows,
|
|
587
594
|
rowId,
|
|
588
|
-
|
|
595
|
+
fetchRow,
|
|
589
596
|
refreshKey,
|
|
590
597
|
links,
|
|
591
598
|
queryReady = true,
|
|
@@ -621,7 +628,9 @@ function useItemDetails({
|
|
|
621
628
|
const hasLookup = Boolean(lookupRow);
|
|
622
629
|
const hasCustomLoad = Boolean(customLoad);
|
|
623
630
|
const sourceType = config?.objectType ?? detailSchema.name;
|
|
624
|
-
const fallbackRows = !customLoad && !clientRows && !
|
|
631
|
+
const fallbackRows = !customLoad && !clientRows && !fetchRow ? rows : void 0;
|
|
632
|
+
const detailSchemaRef = useRef3(detailSchema);
|
|
633
|
+
detailSchemaRef.current = detailSchema;
|
|
625
634
|
const loadRow = useCallback3(async (id, signal) => {
|
|
626
635
|
if (lookupRef.current) return lookupRef.current(id) ?? null;
|
|
627
636
|
if (customLoadRef.current) return customLoadRef.current(id, signal);
|
|
@@ -629,13 +638,13 @@ function useItemDetails({
|
|
|
629
638
|
const row2 = clientRows.find((row3) => rowId(row3) === id);
|
|
630
639
|
return row2 ? { row: row2 } : null;
|
|
631
640
|
}
|
|
632
|
-
if (
|
|
633
|
-
const row2 = await
|
|
634
|
-
return row2 ? { row: row2 } : null;
|
|
641
|
+
if (fetchRow) {
|
|
642
|
+
const row2 = await fetchRow(id, signal);
|
|
643
|
+
return row2 ? { row: row2, loadedFields: projectedFields(detailSchemaRef.current, row2) } : null;
|
|
635
644
|
}
|
|
636
645
|
const row = rows.find((row2) => rowId(row2) === id);
|
|
637
|
-
return row ? { row, loadedFields:
|
|
638
|
-
}, [hasLookup, hasCustomLoad, sourceType, clientRows,
|
|
646
|
+
return row ? { row, loadedFields: projectedFields(detailSchemaRef.current, row) } : null;
|
|
647
|
+
}, [hasLookup, hasCustomLoad, sourceType, clientRows, fetchRow, fallbackRows, rowId]);
|
|
639
648
|
const [retryKey, setRetryKey] = useState3(0);
|
|
640
649
|
const [result, setResult] = useState3({ source: void 0, epoch: "", id: void 0, record: null, loading: false, error: null });
|
|
641
650
|
const activeId = enabled && !lookupRow ? active?.id : void 0;
|
|
@@ -768,116 +777,53 @@ function useItemDetails({
|
|
|
768
777
|
}
|
|
769
778
|
|
|
770
779
|
// src/computedExecution.ts
|
|
771
|
-
import {
|
|
772
|
-
predicatesOf as predicatesOf2,
|
|
773
|
-
isComputedField,
|
|
774
|
-
readFieldValue
|
|
775
|
-
} from "@pythia-software/query-table-core";
|
|
776
780
|
function executionRevision(execution, id) {
|
|
777
781
|
return execution?.resolvedRevisions[id] ?? execution?.resolvedRevisions[`@computed/${id}`];
|
|
778
782
|
}
|
|
779
|
-
function validateComputedResponse(request, result, schema) {
|
|
780
|
-
const execution = result.execution;
|
|
781
|
-
if (result.version !== 2 || !execution || execution.profile !== request.profile || request.planToken && execution.planToken !== request.planToken || request.snapshot !== void 0 && execution.snapshot !== request.snapshot)
|
|
782
|
-
throw Error(
|
|
783
|
-
"Server computed execution identity changed. Refresh the handshake."
|
|
784
|
-
);
|
|
785
|
-
const executed = /* @__PURE__ */ new Set([
|
|
786
|
-
...request.select.filter(isComputedField),
|
|
787
|
-
...request.orderBy.map((term) => term.field).filter(isComputedField),
|
|
788
|
-
...request.where.flatMap((term) => predicatesOf2(term).map((predicate) => predicate.field)).filter(isComputedField)
|
|
789
|
-
]);
|
|
790
|
-
for (const [name, revision] of Object.entries(request.expectedRevisions)) {
|
|
791
|
-
const id = name.replace(/^@computed\//, "");
|
|
792
|
-
if (!executed.has(`@computed/${id}`)) continue;
|
|
793
|
-
if (executionRevision(execution, id) !== revision)
|
|
794
|
-
throw Error(
|
|
795
|
-
"Server computed definition revision changed. Refresh the handshake."
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
const idField = schema.fields.find((f) => f.name === schema.idField);
|
|
799
|
-
if (!idField)
|
|
800
|
-
throw Error("Stable row identity is required for computed values.");
|
|
801
|
-
const ids = new Set(result.rows.map((row) => readFieldValue(idField, row)));
|
|
802
|
-
if (ids.has(null) || ids.has(void 0) || ids.size !== result.rows.length)
|
|
803
|
-
throw Error(
|
|
804
|
-
"Computed response contains missing or duplicate row identities."
|
|
805
|
-
);
|
|
806
|
-
const sidecars = new Map(
|
|
807
|
-
result.computed.map((item) => [item.id, item.values])
|
|
808
|
-
);
|
|
809
|
-
if (sidecars.size !== result.computed.length || [...sidecars.keys()].some((id) => !ids.has(id)))
|
|
810
|
-
throw Error("Computed sidecars do not match returned row identities.");
|
|
811
|
-
for (const field of request.select.filter(isComputedField)) {
|
|
812
|
-
const id = field.slice("@computed/".length);
|
|
813
|
-
for (const rowId of ids) {
|
|
814
|
-
const value = sidecars.get(rowId)?.[id];
|
|
815
|
-
if (!value || value.value !== null && !["string", "number", "boolean"].includes(typeof value.value) || typeof value.value === "number" && !Number.isFinite(value.value))
|
|
816
|
-
throw Error(`Missing or invalid server computed result: ${field}.`);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
783
|
|
|
821
784
|
// src/previewRows.ts
|
|
822
785
|
import {
|
|
823
|
-
predicatesOf as
|
|
786
|
+
predicatesOf as predicatesOf2,
|
|
824
787
|
sourceExecutionSchema,
|
|
825
788
|
toQueryRequestV3,
|
|
826
|
-
toServerQuery,
|
|
827
|
-
toServerQueryV2,
|
|
828
789
|
validateQueryResultV3
|
|
829
790
|
} from "@pythia-software/query-table-core";
|
|
830
791
|
async function fetchPreviewRows(transport, query, schema, execution, definitions, now, signal, baseSchema = schema) {
|
|
831
792
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
832
793
|
const sourceSchema = sourceExecutionSchema(baseSchema, execution, definitions);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
sourceSchema,
|
|
850
|
-
transport.queryCapabilities,
|
|
851
|
-
execution,
|
|
852
|
-
{ now }
|
|
853
|
-
);
|
|
854
|
-
if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
|
|
855
|
-
const response = await transport.fetchQueryV3(request, signal);
|
|
856
|
-
validateQueryResultV3(response, request, query.grouping ? schema : sourceSchema);
|
|
857
|
-
result = { rows: response.rows, total: response.total };
|
|
858
|
-
} else if (transport.fetchRows && !query.grouping && !(computedFilter && transport.fetchRowsV2 && execution)) {
|
|
859
|
-
result = await transport.fetchRows(toServerQuery(query, schema), signal);
|
|
860
|
-
} else {
|
|
861
|
-
throw Error("Row execution requires fetchRows, negotiated fetchRowsV2, or negotiated fetchQueryV3.");
|
|
862
|
-
}
|
|
794
|
+
const request = toQueryRequestV3(
|
|
795
|
+
{ ...query, aggregations: [] },
|
|
796
|
+
sourceSchema,
|
|
797
|
+
transport.queryCapabilities,
|
|
798
|
+
execution,
|
|
799
|
+
{ now }
|
|
800
|
+
);
|
|
801
|
+
const executed = /* @__PURE__ */ new Set([
|
|
802
|
+
...request.result.select,
|
|
803
|
+
...request.result.orderBy.map((term) => term.field),
|
|
804
|
+
...[...request.source.where, ...request.result.where].flatMap((term) => predicatesOf2(term).map((predicate) => predicate.field))
|
|
805
|
+
]);
|
|
806
|
+
for (const definition of definitions)
|
|
807
|
+
if (executed.has(`@computed/${definition.id}`) && executionRevision(execution, definition.id) !== definition.revision)
|
|
808
|
+
throw Error("Computed definitions changed. Refresh the server execution handshake.");
|
|
809
|
+
const response = await transport.fetchQueryV3(request, signal);
|
|
863
810
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
return
|
|
811
|
+
validateQueryResultV3(response, request, query.grouping ? schema : sourceSchema);
|
|
812
|
+
if (response.rows.length > query.limit) throw Error("Invalid preview row response.");
|
|
813
|
+
return { rows: response.rows, total: response.total };
|
|
867
814
|
}
|
|
868
815
|
|
|
869
816
|
// src/useComputedColumns.ts
|
|
870
817
|
import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
871
818
|
import {
|
|
872
|
-
predicatesOf as predicatesOf4,
|
|
873
819
|
isGroupLinkField,
|
|
874
820
|
applyQuery,
|
|
875
821
|
compileFormula,
|
|
876
822
|
computedFieldName,
|
|
877
823
|
groupPreview,
|
|
878
|
-
isComputedField
|
|
824
|
+
isComputedField,
|
|
879
825
|
memoryComputedColumnStore,
|
|
880
|
-
readFieldValue
|
|
826
|
+
readFieldValue,
|
|
881
827
|
validateComputedColumn,
|
|
882
828
|
isSelectable,
|
|
883
829
|
hasRegex
|
|
@@ -1036,7 +982,7 @@ function useComputedColumns(options) {
|
|
|
1036
982
|
const stack = new Set(editingId ? [editingId] : []);
|
|
1037
983
|
let expanded = 0;
|
|
1038
984
|
const resolve = (name) => {
|
|
1039
|
-
if (!
|
|
985
|
+
if (!isComputedField(name)) return void 0;
|
|
1040
986
|
const id = name.slice("@computed/".length), def = definitions.find((d) => d.id === id);
|
|
1041
987
|
if (!def) throw new Error(`Computed column unavailable: ${id}`);
|
|
1042
988
|
if (stack.has(id))
|
|
@@ -1094,7 +1040,7 @@ function useComputedColumns(options) {
|
|
|
1094
1040
|
const values = /* @__PURE__ */ Object.create(null);
|
|
1095
1041
|
for (const name of plan.dependencies) {
|
|
1096
1042
|
const field = inputFields.find((f) => f.name === name);
|
|
1097
|
-
const raw =
|
|
1043
|
+
const raw = readFieldValue(field, row);
|
|
1098
1044
|
values[name] = raw instanceof Date ? raw.toISOString() : raw;
|
|
1099
1045
|
}
|
|
1100
1046
|
size += JSON.stringify(values).length;
|
|
@@ -1108,14 +1054,14 @@ function useComputedColumns(options) {
|
|
|
1108
1054
|
[inputFields]
|
|
1109
1055
|
);
|
|
1110
1056
|
const activeSelect = query.select.length ? query.select : schema.defaultSelect ?? [];
|
|
1111
|
-
const activeIds = activeSelect.filter((c) =>
|
|
1057
|
+
const activeIds = activeSelect.filter((c) => isComputedField(c.field)).map((c) => c.field.slice("@computed/".length)).sort().join(",");
|
|
1112
1058
|
const [evaluated, setEvaluated] = useState4(null);
|
|
1113
1059
|
useEffect4(() => {
|
|
1114
1060
|
const ac = new AbortController();
|
|
1115
1061
|
const values = /* @__PURE__ */ new Map();
|
|
1116
1062
|
void (async () => {
|
|
1117
1063
|
for (const id of activeIds.split(",").filter(Boolean)) {
|
|
1118
|
-
if (options.execution?.fields[computedFieldName(id)]?.select &&
|
|
1064
|
+
if (options.execution?.fields[computedFieldName(id)]?.select && transport)
|
|
1119
1065
|
continue;
|
|
1120
1066
|
const entry = plans.get(id);
|
|
1121
1067
|
if (!entry?.plan) continue;
|
|
@@ -1167,7 +1113,7 @@ function useComputedColumns(options) {
|
|
|
1167
1113
|
dependencies: entry?.plan ? backendDependencies(entry.plan) : [],
|
|
1168
1114
|
accessor: (row) => {
|
|
1169
1115
|
if (entry?.error) return { computedError: entry.error };
|
|
1170
|
-
if (options.execution?.fields[computedFieldName(def.id)]?.select &&
|
|
1116
|
+
if (options.execution?.fields[computedFieldName(def.id)]?.select && transport) {
|
|
1171
1117
|
if (options.serverExecution?.profile !== options.execution.profile || options.serverExecution?.planToken !== options.execution.planToken || options.serverExecution?.snapshot !== options.execution.snapshot || executionRevision(options.execution, def.id) !== def.revision || executionRevision(options.serverExecution, def.id) !== def.revision)
|
|
1172
1118
|
return {
|
|
1173
1119
|
computedError: "Definition changed or values are pending. Refresh the server handshake."
|
|
@@ -1175,7 +1121,7 @@ function useComputedColumns(options) {
|
|
|
1175
1121
|
const idField = schema.fields.find(
|
|
1176
1122
|
(f) => f.name === schema.idField
|
|
1177
1123
|
);
|
|
1178
|
-
const id = idField ?
|
|
1124
|
+
const id = idField ? readFieldValue(idField, row) : null;
|
|
1179
1125
|
const server = id === null ? void 0 : options.serverComputed?.get(id)?.[def.id];
|
|
1180
1126
|
return server ? server.error ? { computedError: server.error.code } : server.value : { computedError: "Server computed result unavailable." };
|
|
1181
1127
|
}
|
|
@@ -1185,16 +1131,16 @@ function useComputedColumns(options) {
|
|
|
1185
1131
|
},
|
|
1186
1132
|
filter: { enabled: options.execution?.fields[computedFieldName(def.id)]?.filter === true && executionRevision(options.execution, def.id) === def.revision && !!entry?.plan && !hasRegex(entry.plan) },
|
|
1187
1133
|
sort: {
|
|
1188
|
-
enabled: !!
|
|
1134
|
+
enabled: !!transport && !!options.execution?.fields[computedFieldName(def.id)]?.sort && executionRevision(options.execution, def.id) === def.revision && !!entry?.plan && !hasRegex(entry.plan)
|
|
1189
1135
|
},
|
|
1190
1136
|
aggregate: {
|
|
1191
1137
|
measure: !!entry?.plan,
|
|
1192
|
-
groupable:
|
|
1138
|
+
groupable: !!metricTransport && !!metricExecution?.fields[computedFieldName(def.id)]?.group && executionRevision(metricExecution, def.id) === def.revision && !!entry?.plan && !hasRegex(entry.plan)
|
|
1193
1139
|
}
|
|
1194
1140
|
};
|
|
1195
1141
|
});
|
|
1196
1142
|
for (const col of activeSelect)
|
|
1197
|
-
if (
|
|
1143
|
+
if (isComputedField(col.field) && !fields.some((f) => f.name === col.field))
|
|
1198
1144
|
fields.push({
|
|
1199
1145
|
name: col.field,
|
|
1200
1146
|
label: col.field.slice(10),
|
|
@@ -1271,9 +1217,7 @@ function useComputedColumns(options) {
|
|
|
1271
1217
|
offset: 0
|
|
1272
1218
|
};
|
|
1273
1219
|
const now = options.now ?? Date.now();
|
|
1274
|
-
const
|
|
1275
|
-
const v3 = !(transport.fetchRowsV2 && options.execution && !request.grouping && !computedFilter) && transport.fetchQueryV3 && transport.queryCapabilities?.version === 3;
|
|
1276
|
-
const pageSize = Math.min(500, v3 ? transport.queryCapabilities.maxRows : 500);
|
|
1220
|
+
const pageSize = Math.min(500, transport.queryCapabilities.maxRows);
|
|
1277
1221
|
if (!Number.isInteger(pageSize) || pageSize < 1) throw Error("Invalid negotiated preview row limit.");
|
|
1278
1222
|
while (batch.length < count) {
|
|
1279
1223
|
const res = await fetchPreviewRows(
|
|
@@ -1361,41 +1305,34 @@ import {
|
|
|
1361
1305
|
memoryStorageAdapter,
|
|
1362
1306
|
normalizeQueryState as normalizeQueryState3,
|
|
1363
1307
|
parseHostQueryState,
|
|
1364
|
-
readFieldValue as
|
|
1308
|
+
readFieldValue as readFieldValue3,
|
|
1365
1309
|
selectedFields as selectedFields2,
|
|
1366
|
-
toServerQuery
|
|
1310
|
+
toServerQuery,
|
|
1367
1311
|
queriesEqual as queriesEqual2,
|
|
1368
1312
|
isWhereConjunction as isWhereConjunction2,
|
|
1369
1313
|
isWhereGroup,
|
|
1370
|
-
predicatesOf as
|
|
1314
|
+
predicatesOf as predicatesOf4,
|
|
1315
|
+
opsForField as opsForField2,
|
|
1371
1316
|
metricPlans as metricPlans2,
|
|
1372
1317
|
validateMetric as validateMetric2,
|
|
1373
1318
|
metricDependencies as metricDependencies2,
|
|
1374
|
-
isComputedField as
|
|
1319
|
+
isComputedField as isComputedField3,
|
|
1375
1320
|
computedFieldName as computedFieldName2,
|
|
1376
|
-
toServerQueryV2 as toServerQueryV22,
|
|
1377
1321
|
groupKeyField,
|
|
1378
1322
|
groupKeyFields as groupKeyFields2
|
|
1379
1323
|
} from "@pythia-software/query-table-core";
|
|
1380
1324
|
|
|
1381
1325
|
// src/useRequestActivity.ts
|
|
1382
1326
|
import { useCallback as useCallback5, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef5, useState as useState5 } from "react";
|
|
1383
|
-
|
|
1384
|
-
"
|
|
1385
|
-
|
|
1386
|
-
"fetchQueryV3",
|
|
1387
|
-
"fetchMetrics",
|
|
1388
|
-
"fetchAggregations",
|
|
1389
|
-
"fetchRow",
|
|
1390
|
-
"fetchDistinctValues",
|
|
1391
|
-
"fetchFieldStats"
|
|
1392
|
-
]);
|
|
1327
|
+
function requestKind(request) {
|
|
1328
|
+
return request?.distinct ? "DistinctValues" : request?.rows ? "Rows" : "Metrics";
|
|
1329
|
+
}
|
|
1393
1330
|
function observeTransport(transport, publish, nextId) {
|
|
1394
1331
|
const wrappers = /* @__PURE__ */ new Map();
|
|
1395
1332
|
return new Proxy(Object.create(transport), {
|
|
1396
1333
|
get(_target, key) {
|
|
1397
1334
|
const method = Reflect.get(transport, key, transport);
|
|
1398
|
-
if (
|
|
1335
|
+
if (key !== "fetchQueryV3" || typeof method !== "function")
|
|
1399
1336
|
return method;
|
|
1400
1337
|
if (!wrappers.has(key))
|
|
1401
1338
|
wrappers.set(key, async (...args) => {
|
|
@@ -1403,7 +1340,7 @@ function observeTransport(transport, publish, nextId) {
|
|
|
1403
1340
|
const start = performance.now();
|
|
1404
1341
|
const entry = {
|
|
1405
1342
|
id: nextId(),
|
|
1406
|
-
kind:
|
|
1343
|
+
kind: requestKind(args[0]),
|
|
1407
1344
|
startedAt: Date.now(),
|
|
1408
1345
|
status: "pending"
|
|
1409
1346
|
};
|
|
@@ -1484,6 +1421,7 @@ import {
|
|
|
1484
1421
|
|
|
1485
1422
|
// src/metricExecution.ts
|
|
1486
1423
|
import {
|
|
1424
|
+
QueryError,
|
|
1487
1425
|
applyAggregations,
|
|
1488
1426
|
normalizeQueryState as normalizeQueryState2,
|
|
1489
1427
|
evaluateMetrics,
|
|
@@ -1493,25 +1431,77 @@ import {
|
|
|
1493
1431
|
metricPopulationError,
|
|
1494
1432
|
hasMetricSeries,
|
|
1495
1433
|
planMetricSeries,
|
|
1496
|
-
predicatesOf as
|
|
1497
|
-
|
|
1498
|
-
|
|
1434
|
+
predicatesOf as predicatesOf3,
|
|
1435
|
+
sourceExecutionSchema as sourceExecutionSchema2,
|
|
1436
|
+
toQueryRequestV3 as toQueryRequestV32,
|
|
1437
|
+
validateQueryResultV3 as validateQueryResultV32,
|
|
1499
1438
|
validateMetric,
|
|
1500
1439
|
selectMetricShownRows,
|
|
1501
|
-
readFieldValue as
|
|
1440
|
+
readFieldValue as readFieldValue2,
|
|
1502
1441
|
resolveFieldName,
|
|
1503
|
-
isComputedField as
|
|
1442
|
+
isComputedField as isComputedField2
|
|
1504
1443
|
} from "@pythia-software/query-table-core";
|
|
1505
1444
|
var abort = (signal) => {
|
|
1506
1445
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
1507
1446
|
};
|
|
1508
1447
|
function usesLocalMetricPage(clause, context) {
|
|
1509
|
-
|
|
1448
|
+
if (clause.scope !== "shownRows") return false;
|
|
1449
|
+
if (!context.executeV3 && !context.transport) return true;
|
|
1450
|
+
if (context.transport?.queryCapabilities.regexAggregateExpressions) return false;
|
|
1451
|
+
try {
|
|
1452
|
+
return metricPlans(clause, context.schema, context.resolveComputed).some(hasRegex2);
|
|
1453
|
+
} catch {
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
async function executeV3Metrics(transport, query, baseSchema, resultSchema, execution, now, signal) {
|
|
1458
|
+
const capabilities = transport.queryCapabilities;
|
|
1459
|
+
const refused = /* @__PURE__ */ new Map();
|
|
1460
|
+
for (const clause of query.aggregations ?? []) {
|
|
1461
|
+
if (clause.unsupportedOp) {
|
|
1462
|
+
refused.set(clause.id, new QueryError("invalid_metric", `Metric ${clause.id} uses an unsupported operation: ${clause.unsupportedOp}.`, { metricId: clause.id }));
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
const issue = metricPopulationError(clause, { filters: capabilities.metricFilters, minCount: capabilities.metricMinCount, minCountMeasures: capabilities.metricMinCountMeasures });
|
|
1466
|
+
if (issue) refused.set(clause.id, new QueryError("capability_unsupported", issue, { metricId: clause.id }));
|
|
1467
|
+
}
|
|
1468
|
+
let runnable = (query.aggregations ?? []).filter((clause) => !refused.has(clause.id));
|
|
1469
|
+
const refusal = (q) => {
|
|
1470
|
+
try {
|
|
1471
|
+
toQueryRequestV32(q, baseSchema, capabilities, execution, { now, rows: false });
|
|
1472
|
+
} catch (error) {
|
|
1473
|
+
if (error instanceof QueryError) return error;
|
|
1474
|
+
throw error;
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
const unpaged = { ...query, orderBy: [], offset: 0 };
|
|
1478
|
+
const shown = runnable.filter((clause) => clause.scope === "shownRows");
|
|
1479
|
+
const page = shown.length ? refusal({ ...query, aggregations: [] }) : void 0;
|
|
1480
|
+
if (page && !refusal({ ...unpaged, aggregations: [] })) {
|
|
1481
|
+
for (const clause of shown) refused.set(clause.id, page);
|
|
1482
|
+
runnable = runnable.filter((clause) => clause.scope !== "shownRows");
|
|
1483
|
+
}
|
|
1484
|
+
let metrics = [];
|
|
1485
|
+
if (runnable.length) {
|
|
1486
|
+
const paged = runnable.some((clause) => clause.scope === "shownRows");
|
|
1487
|
+
const request = toQueryRequestV32({ ...paged ? query : unpaged, aggregations: runnable }, baseSchema, capabilities, execution, { now, rows: false });
|
|
1488
|
+
const result = await transport.fetchQueryV3(request, signal);
|
|
1489
|
+
validateQueryResultV32(result, request, resultSchema);
|
|
1490
|
+
metrics = result.metrics;
|
|
1491
|
+
}
|
|
1492
|
+
if (!refused.size) return { metrics };
|
|
1493
|
+
const byId = new Map(metrics.map((metric) => [metric.id, metric]));
|
|
1494
|
+
return { metrics: (query.aggregations ?? []).flatMap((clause) => {
|
|
1495
|
+
const issue = refused.get(clause.id);
|
|
1496
|
+
if (issue) return [{ id: clause.id, buckets: [], error: issue.message, errorCode: issue.code }];
|
|
1497
|
+
const metric = byId.get(clause.id);
|
|
1498
|
+
return metric ? [metric] : [];
|
|
1499
|
+
}) };
|
|
1510
1500
|
}
|
|
1511
1501
|
async function localMetrics(rows, query, context, signal) {
|
|
1512
1502
|
const originalClauses = query.aggregations ?? [];
|
|
1513
1503
|
const advanced = (query.aggregations ?? []).filter(
|
|
1514
|
-
(c) => context.strictLocalMetrics || c.dateGrouping ||
|
|
1504
|
+
(c) => context.strictLocalMetrics || c.dateGrouping || isComputedField2(c.field ?? "") || c.groupBy.some(isComputedField2) || c.diagnostics?.length || c.expression !== void 0 || c.expressionY !== void 0 || c.scope !== void 0 || c.distribution || c.sort || c.groupLimit !== void 0 || c.where !== void 0 || c.minCount !== void 0 || hasMetricSeries(c)
|
|
1515
1505
|
);
|
|
1516
1506
|
if (advanced.length && (originalClauses.length > 20 || normalizeQueryState2(query).aggregations?.length !== originalClauses.length))
|
|
1517
1507
|
return evaluateMetrics(rows, query, context.schema, {
|
|
@@ -1545,7 +1535,7 @@ async function localMetrics(rows, query, context, signal) {
|
|
|
1545
1535
|
context.resolveComputed
|
|
1546
1536
|
);
|
|
1547
1537
|
if (diagnostics.length)
|
|
1548
|
-
throw
|
|
1538
|
+
throw new QueryError("invalid_metric", diagnostics.map((d) => d.message).join(" "), { metricId: clause.id });
|
|
1549
1539
|
}
|
|
1550
1540
|
const requiresPage = (query.aggregations ?? []).some(
|
|
1551
1541
|
(clause) => clause.scope === "shownRows"
|
|
@@ -1573,7 +1563,7 @@ async function localMetrics(rows, query, context, signal) {
|
|
|
1573
1563
|
for (const name of plan.dependencies) {
|
|
1574
1564
|
const field = context.schema.fields.find((f) => f.name === name);
|
|
1575
1565
|
if (!field) throw Error(`Unavailable metric input: ${name}`);
|
|
1576
|
-
const raw =
|
|
1566
|
+
const raw = readFieldValue2(field, row);
|
|
1577
1567
|
values2[name] = raw instanceof Date ? raw.toISOString() : raw ?? null;
|
|
1578
1568
|
}
|
|
1579
1569
|
bytes += JSON.stringify(values2).length;
|
|
@@ -1631,14 +1621,33 @@ async function executeMetrics(query, context, signal) {
|
|
|
1631
1621
|
);
|
|
1632
1622
|
return { metrics: series.combine(parts.flatMap((part) => part.metrics)) };
|
|
1633
1623
|
}
|
|
1634
|
-
if (context.executeV3
|
|
1624
|
+
if (context.executeV3 || context.transport) {
|
|
1625
|
+
const page = clauses.filter((c) => usesLocalMetricPage(c, context));
|
|
1626
|
+
const remote = (q) => context.executeV3 ? context.executeV3(q, signal) : executeV3Metrics(
|
|
1627
|
+
context.transport,
|
|
1628
|
+
q,
|
|
1629
|
+
context.schema,
|
|
1630
|
+
sourceExecutionSchema2(context.schema, context.execution),
|
|
1631
|
+
context.execution,
|
|
1632
|
+
context.now ?? Date.now(),
|
|
1633
|
+
signal
|
|
1634
|
+
);
|
|
1635
|
+
if (!page.length) return remote(query);
|
|
1636
|
+
const { executeV3: _executeV3, transport: _transport, ...local } = context;
|
|
1637
|
+
const rest = clauses.filter((c) => !page.includes(c));
|
|
1638
|
+
const [own, served] = await Promise.all([
|
|
1639
|
+
executeMetrics({ ...query, aggregations: page }, local, signal),
|
|
1640
|
+
rest.length ? remote({ ...query, aggregations: rest }) : { metrics: [] }
|
|
1641
|
+
]);
|
|
1642
|
+
const byId2 = new Map([...own.metrics, ...served.metrics].map((metric) => [metric.id, metric]));
|
|
1643
|
+
return { metrics: clauses.flatMap((c) => byId2.get(c.id) ?? []) };
|
|
1644
|
+
}
|
|
1635
1645
|
if (context.rowsReady === false && clauses.some((c) => usesLocalMetricPage(c, context)))
|
|
1636
1646
|
throw Error("Wait for the current table page before previewing shown-row metrics.");
|
|
1637
|
-
|
|
1638
|
-
if (context.clientRows && !supportsV2 && !context.transport?.fetchAggregations)
|
|
1647
|
+
if (context.clientRows)
|
|
1639
1648
|
return localMetrics(context.clientRows, query, context, signal);
|
|
1640
1649
|
const shown = clauses.filter((c) => usesLocalMetricPage(c, context));
|
|
1641
|
-
|
|
1650
|
+
const matching = clauses.filter((c) => !shown.includes(c));
|
|
1642
1651
|
const results = [];
|
|
1643
1652
|
if (shown.length) {
|
|
1644
1653
|
if (!context.rowsReady || !context.shownRows)
|
|
@@ -1649,7 +1658,7 @@ async function executeMetrics(query, context, signal) {
|
|
|
1649
1658
|
shown.flatMap((clause) => [
|
|
1650
1659
|
...clause.groupBy,
|
|
1651
1660
|
...(clause.where ?? []).flatMap(
|
|
1652
|
-
(term) =>
|
|
1661
|
+
(term) => predicatesOf3(term).map((predicate) => predicate.field)
|
|
1653
1662
|
),
|
|
1654
1663
|
// Includes a measured minimum's field, which the page must also hold.
|
|
1655
1664
|
...metricDependencies(clause, context.schema, context.resolveComputed)
|
|
@@ -1681,103 +1690,10 @@ async function executeMetrics(query, context, signal) {
|
|
|
1681
1690
|
...(await localMetrics(context.shownRows, pageQuery, context, signal)).metrics
|
|
1682
1691
|
);
|
|
1683
1692
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
unsupported(
|
|
1688
|
-
clause,
|
|
1689
|
-
"This server does not support calendar grouping. Enable the v3 query executor."
|
|
1693
|
+
if (matching.length)
|
|
1694
|
+
throw Error(
|
|
1695
|
+
"Remote all-matching metrics require a metric-capable server transport."
|
|
1690
1696
|
);
|
|
1691
|
-
matching = matching.filter((c) => !c.dateGrouping);
|
|
1692
|
-
{
|
|
1693
|
-
const transport = context.transport;
|
|
1694
|
-
if (transport?.fetchMetrics || transport?.fetchAggregations) {
|
|
1695
|
-
const support = transport.fetchMetrics && transport.metricCapabilities?.version === 2 ? transport.metricCapabilities : {};
|
|
1696
|
-
matching = matching.filter((c) => {
|
|
1697
|
-
const error = metricPopulationError(c, support);
|
|
1698
|
-
if (error) unsupported(c, error);
|
|
1699
|
-
return !error;
|
|
1700
|
-
});
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
if (matching.length) {
|
|
1704
|
-
const transport = context.transport;
|
|
1705
|
-
if (transport?.fetchMetrics && transport.metricCapabilities?.version === 2) {
|
|
1706
|
-
const capabilities = transport.metricCapabilities;
|
|
1707
|
-
const request = toMetricQuery(
|
|
1708
|
-
{ ...query, aggregations: matching },
|
|
1709
|
-
context.schema,
|
|
1710
|
-
context.execution
|
|
1711
|
-
);
|
|
1712
|
-
if (request.diagnostics.length)
|
|
1713
|
-
throw Error(request.diagnostics.map((d) => d.message).join(" "));
|
|
1714
|
-
if (request.profile && capabilities.profile && request.profile !== capabilities.profile)
|
|
1715
|
-
throw Error(
|
|
1716
|
-
"Metric transport and computed execution profiles do not match. Refresh the handshake."
|
|
1717
|
-
);
|
|
1718
|
-
const remoteFields = new Map(
|
|
1719
|
-
context.schema.fields.map((field) => [field.name, field])
|
|
1720
|
-
);
|
|
1721
|
-
for (const [name, capability] of Object.entries(
|
|
1722
|
-
context.execution?.fields ?? {}
|
|
1723
|
-
))
|
|
1724
|
-
if (isComputedField3(name))
|
|
1725
|
-
remoteFields.set(name, {
|
|
1726
|
-
name,
|
|
1727
|
-
label: name,
|
|
1728
|
-
type: capability.type,
|
|
1729
|
-
source: { kind: "backend" }
|
|
1730
|
-
});
|
|
1731
|
-
const remoteSchema = {
|
|
1732
|
-
...context.schema,
|
|
1733
|
-
fields: [...remoteFields.values()]
|
|
1734
|
-
};
|
|
1735
|
-
const wire = new Map(request.metrics.map((m) => [m.id, m]));
|
|
1736
|
-
const limitation = (clause) => {
|
|
1737
|
-
if (clause.scope === "shownRows" && !capabilities.shownRows)
|
|
1738
|
-
return "This server does not support shown-row metrics.";
|
|
1739
|
-
if (!capabilities.computedFields && [
|
|
1740
|
-
...clause.groupBy,
|
|
1741
|
-
...metricPlans(clause, remoteSchema).flatMap(
|
|
1742
|
-
(plan) => plan.dependencies
|
|
1743
|
-
)
|
|
1744
|
-
].some(isComputedField3))
|
|
1745
|
-
return "This metric server does not support computed fields.";
|
|
1746
|
-
const sent = wire.get(clause.id);
|
|
1747
|
-
if (!capabilities.expressions && sent && !sent.distribution && (sent.expression !== void 0 || sent.expressionY !== void 0))
|
|
1748
|
-
return "This server does not support composed metric expressions.";
|
|
1749
|
-
if (!capabilities.distributions && clause.distribution && (!clause.display || ["box", "histogram"].includes(clause.display.kind)))
|
|
1750
|
-
return "This server does not support distributions.";
|
|
1751
|
-
return void 0;
|
|
1752
|
-
};
|
|
1753
|
-
const rejected = /* @__PURE__ */ new Set();
|
|
1754
|
-
for (const clause of matching) {
|
|
1755
|
-
const error = limitation(clause);
|
|
1756
|
-
if (error) {
|
|
1757
|
-
rejected.add(clause.id);
|
|
1758
|
-
unsupported(clause, error);
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
const metrics = request.metrics.filter((m) => !rejected.has(m.id));
|
|
1762
|
-
if (metrics.length)
|
|
1763
|
-
results.push(
|
|
1764
|
-
...(await transport.fetchMetrics({ ...request, metrics }, signal)).metrics
|
|
1765
|
-
);
|
|
1766
|
-
} else if (transport?.fetchAggregations) {
|
|
1767
|
-
const request = toAggregationQuery(
|
|
1768
|
-
{ ...query, aggregations: matching },
|
|
1769
|
-
context.schema
|
|
1770
|
-
);
|
|
1771
|
-
if (request.diagnostics?.length)
|
|
1772
|
-
throw Error(request.diagnostics.map((d) => d.message).join(" "));
|
|
1773
|
-
results.push(
|
|
1774
|
-
...(await transport.fetchAggregations(request, signal)).metrics
|
|
1775
|
-
);
|
|
1776
|
-
} else
|
|
1777
|
-
throw Error(
|
|
1778
|
-
"Remote all-matching metrics require a metric-capable server transport."
|
|
1779
|
-
);
|
|
1780
|
-
}
|
|
1781
1697
|
abort(signal);
|
|
1782
1698
|
const byId = new Map(results.map((r) => [r.id, r]));
|
|
1783
1699
|
return {
|
|
@@ -1785,7 +1701,8 @@ async function executeMetrics(query, context, signal) {
|
|
|
1785
1701
|
(c) => byId.get(c.id) ?? {
|
|
1786
1702
|
id: c.id,
|
|
1787
1703
|
buckets: [],
|
|
1788
|
-
error: "Server omitted this metric."
|
|
1704
|
+
error: "Server omitted this metric.",
|
|
1705
|
+
errorCode: "internal"
|
|
1789
1706
|
}
|
|
1790
1707
|
)
|
|
1791
1708
|
};
|
|
@@ -1823,7 +1740,7 @@ function useMetricBatch(query, clauses, context, debounceMs, nonce, validateQuer
|
|
|
1823
1740
|
context.queryContextKey,
|
|
1824
1741
|
page ? context.shownFields : null
|
|
1825
1742
|
]);
|
|
1826
|
-
const localDataset = !context.executeV3 && !
|
|
1743
|
+
const localDataset = !context.executeV3 && !context.transport;
|
|
1827
1744
|
const pageReady = context.rowsReady ?? (localDataset && context.clientRows !== void 0);
|
|
1828
1745
|
const validationToken = useMemo5(() => ({}), [
|
|
1829
1746
|
key,
|
|
@@ -2056,12 +1973,10 @@ function useSelect(query, setQuery, schema) {
|
|
|
2056
1973
|
return schema.fields.filter((f) => !shown.has(f.name) && (f.select?.enabled ?? true));
|
|
2057
1974
|
}, [schema, fields]);
|
|
2058
1975
|
const writeSelect = useCallback8(
|
|
2059
|
-
(mutate
|
|
1976
|
+
(mutate) => {
|
|
2060
1977
|
setQuery((prev) => {
|
|
2061
1978
|
const current = prev.select.length ? prev.select : visible;
|
|
2062
|
-
|
|
2063
|
-
if (clearFitToScreen) delete next.fitToScreen;
|
|
2064
|
-
return next;
|
|
1979
|
+
return { ...prev, select: mutate([...current]) };
|
|
2065
1980
|
});
|
|
2066
1981
|
},
|
|
2067
1982
|
[setQuery, visible]
|
|
@@ -2079,7 +1994,7 @@ function useSelect(query, setQuery, schema) {
|
|
|
2079
1994
|
[writeSelect]
|
|
2080
1995
|
);
|
|
2081
1996
|
const setWidth = useCallback8(
|
|
2082
|
-
(field, width) => writeSelect((c) => c.map((x) => x.field === field ? { ...x, width: Math.round(width) } : x)
|
|
1997
|
+
(field, width) => writeSelect((c) => c.map((x) => x.field === field ? { ...x, width: Math.round(width) } : x)),
|
|
2083
1998
|
[writeSelect]
|
|
2084
1999
|
);
|
|
2085
2000
|
const reset = useCallback8(() => setQuery((prev) => ({ ...prev, select: [] })), [setQuery]);
|
|
@@ -2372,7 +2287,7 @@ function cloneQueryState(q) {
|
|
|
2372
2287
|
limit: q.limit,
|
|
2373
2288
|
offset: q.offset
|
|
2374
2289
|
};
|
|
2375
|
-
if (q.
|
|
2290
|
+
if (q.fit) clone.fit = q.fit;
|
|
2376
2291
|
if (q.hostState) clone.hostState = { version: q.hostState.version, params: Object.fromEntries(Object.entries(q.hostState.params).map(([name, values]) => [name, [...values]])) };
|
|
2377
2292
|
if (q.aggregations)
|
|
2378
2293
|
clone.aggregations = JSON.parse(
|
|
@@ -2401,7 +2316,7 @@ function asDistinctValue(value) {
|
|
|
2401
2316
|
}
|
|
2402
2317
|
function fieldHasNullInRows(field, rows) {
|
|
2403
2318
|
for (const row of rows) {
|
|
2404
|
-
if (isNullLike(
|
|
2319
|
+
if (isNullLike(readFieldValue3(field, row))) return true;
|
|
2405
2320
|
}
|
|
2406
2321
|
return false;
|
|
2407
2322
|
}
|
|
@@ -2411,7 +2326,7 @@ function distinctValuesFromRows(field, rows, search) {
|
|
|
2411
2326
|
const seen = /* @__PURE__ */ new Set();
|
|
2412
2327
|
let hasMore = false;
|
|
2413
2328
|
rowsLoop: for (const row of rows) {
|
|
2414
|
-
const value =
|
|
2329
|
+
const value = readFieldValue3(field, row);
|
|
2415
2330
|
const candidates = field.type === "textarray" && Array.isArray(value) ? value : [value];
|
|
2416
2331
|
for (const candidate of candidates) {
|
|
2417
2332
|
const raw = asDistinctValue(candidate);
|
|
@@ -2465,7 +2380,6 @@ function writeHostParameters(url, host, state) {
|
|
|
2465
2380
|
for (const value of state?.params[name] ?? []) next.searchParams.append(name, value);
|
|
2466
2381
|
return next.toString();
|
|
2467
2382
|
}
|
|
2468
|
-
var HOST_REQUEST_METHODS = /* @__PURE__ */ new Set(["fetchRows", "fetchRowsV2", "fetchQueryV3", "fetchMetrics", "fetchAggregations", "fetchDistinctValues"]);
|
|
2469
2383
|
function withHostTransport(transport, hostStateKey) {
|
|
2470
2384
|
if (!transport || !hostStateKey) return transport;
|
|
2471
2385
|
const hostState = JSON.parse(hostStateKey);
|
|
@@ -2473,7 +2387,7 @@ function withHostTransport(transport, hostStateKey) {
|
|
|
2473
2387
|
return new Proxy(Object.create(transport), {
|
|
2474
2388
|
get(_target, key) {
|
|
2475
2389
|
const method = Reflect.get(transport, key, transport);
|
|
2476
|
-
if (
|
|
2390
|
+
if (key !== "fetchQueryV3" || typeof method !== "function") return method;
|
|
2477
2391
|
if (!wrappers.has(key))
|
|
2478
2392
|
wrappers.set(key, (request, signal) => method.call(transport, { ...request, hostState }, signal));
|
|
2479
2393
|
return wrappers.get(key);
|
|
@@ -2571,10 +2485,10 @@ function useQueryTable(opts) {
|
|
|
2571
2485
|
const metadataTransport = useMemo9(() => withHostTransport(activityTransport, hostStateKey), [activityTransport, hostStateKey]);
|
|
2572
2486
|
const metricTransport = useMemo9(() => withHostTransport(activityMetricTransport, hostStateKey), [activityMetricTransport, hostStateKey]);
|
|
2573
2487
|
const transport = metadataTransport;
|
|
2574
|
-
const sourceCatalogue = useMemo9(() =>
|
|
2488
|
+
const sourceCatalogue = useMemo9(() => sourceExecutionSchema3(sourceSchema, opts.computedExecution), [sourceSchema, opts.computedExecution]);
|
|
2575
2489
|
const sourceRows = providedRows;
|
|
2576
2490
|
const groupingModel = useMemo9(() => createGroupingModel(sourceCatalogue), [sourceCatalogue]);
|
|
2577
|
-
const remoteGrouping = !!transport
|
|
2491
|
+
const remoteGrouping = !!transport && transport.queryCapabilities.version === 3 && transport.queryCapabilities.groupedRows;
|
|
2578
2492
|
const groupingSupported = (!transport && sourceRows !== void 0 || remoteGrouping) && opts.grouping !== false;
|
|
2579
2493
|
const groupKey = JSON.stringify(query.grouping ?? null);
|
|
2580
2494
|
const groupingNow = useMemo9(
|
|
@@ -2775,10 +2689,10 @@ function useQueryTable(opts) {
|
|
|
2775
2689
|
...clientRows ? { clientRows } : {}
|
|
2776
2690
|
});
|
|
2777
2691
|
const resolveComputed = useCallback12(
|
|
2778
|
-
(name) =>
|
|
2692
|
+
(name) => isComputedField3(name) ? computed.compile(`[${name.replace(/]/g, "]] ".trim())}]`) : void 0,
|
|
2779
2693
|
[computed.compile]
|
|
2780
2694
|
);
|
|
2781
|
-
const metricCompileSchema = useMemo9(() => opts.metricExecution ?
|
|
2695
|
+
const metricCompileSchema = useMemo9(() => opts.metricExecution ? sourceExecutionSchema3(displaySchema, opts.metricExecution, computed.definitions) : displaySchema, [displaySchema, opts.metricExecution, computed.definitions]);
|
|
2782
2696
|
const metricCompile = useCallback12(
|
|
2783
2697
|
(clause) => {
|
|
2784
2698
|
const diagnostics = validateMetric2(
|
|
@@ -2787,7 +2701,7 @@ function useQueryTable(opts) {
|
|
|
2787
2701
|
resolveComputed
|
|
2788
2702
|
);
|
|
2789
2703
|
if (diagnostics.length)
|
|
2790
|
-
throw new
|
|
2704
|
+
throw new QueryError2("invalid_metric", diagnostics.map((d) => d.message).join(" "), { metricId: clause.id });
|
|
2791
2705
|
return metricPlans2(clause, metricCompileSchema, resolveComputed);
|
|
2792
2706
|
},
|
|
2793
2707
|
[metricCompileSchema, resolveComputed]
|
|
@@ -2799,7 +2713,6 @@ function useQueryTable(opts) {
|
|
|
2799
2713
|
const definitionKey = JSON.stringify(computedRevisions);
|
|
2800
2714
|
const queryKey = useMemo9(() => JSON.stringify(query), [query]);
|
|
2801
2715
|
const serverRowQuery = useMemo9(() => {
|
|
2802
|
-
const execution2 = opts.computedExecution;
|
|
2803
2716
|
const detailConfig = typeof opts.itemDetails === "object" ? opts.itemDetails : void 0;
|
|
2804
2717
|
const page = query.itemDetails?.page ?? detailConfig?.page;
|
|
2805
2718
|
const pageEnabled = Boolean(opts.itemDetails) && (query.itemDetails?.modes ?? initialDetailModes(detailConfig)).includes("page") && (!detailConfig?.modes || detailConfig.modes.includes("page"));
|
|
@@ -2808,20 +2721,7 @@ function useQueryTable(opts) {
|
|
|
2808
2721
|
...query.select.length ? query.select : defaults.select,
|
|
2809
2722
|
...pageFields.filter((name) => displaySchema.fields.some((field) => field.name === name)).map((field) => ({ field }))
|
|
2810
2723
|
] } : executionQuery;
|
|
2811
|
-
const
|
|
2812
|
-
const request = v2Request ?? toServerQuery2(requestQuery, displaySchema);
|
|
2813
|
-
if (v2Request)
|
|
2814
|
-
for (const definition of computed.definitions) {
|
|
2815
|
-
if ((execution2?.fields[computedFieldName2(definition.id)]?.select || v2Request.orderBy.some(
|
|
2816
|
-
(term) => term.field === computedFieldName2(definition.id)
|
|
2817
|
-
)) && executionRevision(execution2, definition.id) !== void 0 && executionRevision(execution2, definition.id) !== definition.revision)
|
|
2818
|
-
v2Request.diagnostics.push({
|
|
2819
|
-
from: 0,
|
|
2820
|
-
to: 1,
|
|
2821
|
-
code: "definition_changed",
|
|
2822
|
-
message: "Computed definitions changed. Refresh the server execution handshake."
|
|
2823
|
-
});
|
|
2824
|
-
}
|
|
2724
|
+
const request = toServerQuery(requestQuery, displaySchema);
|
|
2825
2725
|
for (const clause of query.aggregations ?? [])
|
|
2826
2726
|
if (clause.scope === "shownRows") {
|
|
2827
2727
|
try {
|
|
@@ -2844,16 +2744,14 @@ function useQueryTable(opts) {
|
|
|
2844
2744
|
}, [
|
|
2845
2745
|
queryKey,
|
|
2846
2746
|
displaySchema,
|
|
2847
|
-
transport,
|
|
2848
|
-
computedRevisions,
|
|
2849
2747
|
schema,
|
|
2850
2748
|
resolveComputed,
|
|
2851
|
-
opts.computedExecution,
|
|
2852
2749
|
opts.itemDetails,
|
|
2853
2750
|
defaults
|
|
2854
2751
|
]);
|
|
2752
|
+
const serverDefinitionKey = JSON.stringify(computed.definitions.flatMap((definition) => executionRevision(opts.computedExecution, definition.id) !== void 0 ? [[definition.id, definition.revision]] : []));
|
|
2855
2753
|
const rowQueryKey = useMemo9(
|
|
2856
|
-
() => transport ? `server:${JSON.stringify(serverRowQuery)}:${
|
|
2754
|
+
() => transport ? `server:${JSON.stringify(serverRowQuery)}:${serverDefinitionKey}:${JSON.stringify([
|
|
2857
2755
|
groupKey,
|
|
2858
2756
|
groupingNow,
|
|
2859
2757
|
query.select.map((column) => column.field),
|
|
@@ -2861,10 +2759,11 @@ function useQueryTable(opts) {
|
|
|
2861
2759
|
query.orderBy,
|
|
2862
2760
|
query.limit,
|
|
2863
2761
|
query.offset
|
|
2864
|
-
])
|
|
2762
|
+
])}` : `client:${JSON.stringify([query.where, query.orderBy, query.limit, query.offset])}`,
|
|
2865
2763
|
[
|
|
2866
2764
|
transport,
|
|
2867
2765
|
serverRowQuery,
|
|
2766
|
+
serverDefinitionKey,
|
|
2868
2767
|
groupKey,
|
|
2869
2768
|
groupingNow,
|
|
2870
2769
|
query.select,
|
|
@@ -2893,67 +2792,37 @@ function useQueryTable(opts) {
|
|
|
2893
2792
|
try {
|
|
2894
2793
|
validateExecution(query);
|
|
2895
2794
|
if (transport) {
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
Object.fromEntries(Object.entries(row).filter(([name]) => isComputedField4(name)).map(([name, value]) => [name.slice(10), value && typeof value === "object" && "computedError" in value ? { value: null, error: { code: String(value.computedError) } } : { value }]))
|
|
2916
|
-
])));
|
|
2917
|
-
setRows(result.rows);
|
|
2918
|
-
setTotal(result.total);
|
|
2919
|
-
setCommittedPage(pageContext);
|
|
2920
|
-
setCommittedSource({ schema, transport, clientRows });
|
|
2921
|
-
}
|
|
2922
|
-
return;
|
|
2795
|
+
const request = toQueryRequestV33(
|
|
2796
|
+
{ ...query, aggregations: [], select: [
|
|
2797
|
+
...query.select.filter((column) => isComputedField3(column.field)),
|
|
2798
|
+
...serverRowQuery.select.map((field) => ({ field }))
|
|
2799
|
+
] },
|
|
2800
|
+
sourceSchema,
|
|
2801
|
+
transport.queryCapabilities,
|
|
2802
|
+
opts.computedExecution,
|
|
2803
|
+
{ now: groupingNow ?? (opts.evaluationNow ?? Date.now)() }
|
|
2804
|
+
);
|
|
2805
|
+
const executed = /* @__PURE__ */ new Set([
|
|
2806
|
+
...request.result.select,
|
|
2807
|
+
...request.result.orderBy.map((term) => term.field),
|
|
2808
|
+
...[...request.source.where, ...request.result.where].flatMap((term) => predicatesOf4(term).map((predicate) => predicate.field))
|
|
2809
|
+
]);
|
|
2810
|
+
for (const definition of computed.definitions) {
|
|
2811
|
+
const revision = executionRevision(opts.computedExecution, definition.id);
|
|
2812
|
+
if (executed.has(computedFieldName2(definition.id)) && revision !== void 0 && revision !== definition.revision)
|
|
2813
|
+
throw Error("Computed definitions changed. Refresh the server execution handshake.");
|
|
2923
2814
|
}
|
|
2924
|
-
const
|
|
2925
|
-
|
|
2926
|
-
throw Error("Row execution requires fetchRows, negotiated fetchRowsV2, or negotiated fetchQueryV3.");
|
|
2927
|
-
if ("diagnostics" in serverRowQuery && Array.isArray(serverRowQuery.diagnostics) && serverRowQuery.diagnostics.length)
|
|
2928
|
-
throw Error(
|
|
2929
|
-
serverRowQuery.diagnostics.map((d) => d.message).join(" ")
|
|
2930
|
-
);
|
|
2931
|
-
if (!useV2 && query.orderBy.some((s) => isComputedField4(s.field)))
|
|
2932
|
-
throw Error(
|
|
2933
|
-
"Global computed sorting requires a server execution handshake and fetchRowsV2."
|
|
2934
|
-
);
|
|
2935
|
-
const res = useV2 ? await transport.fetchRowsV2(
|
|
2936
|
-
serverRowQuery,
|
|
2937
|
-
ac.signal
|
|
2938
|
-
) : await transport.fetchRows(serverRowQuery, ac.signal);
|
|
2939
|
-
if (useV2)
|
|
2940
|
-
validateComputedResponse(
|
|
2941
|
-
serverRowQuery,
|
|
2942
|
-
res,
|
|
2943
|
-
schema
|
|
2944
|
-
);
|
|
2815
|
+
const result = await transport.fetchQueryV3(request, ac.signal);
|
|
2816
|
+
validateQueryResultV33(result, request, query.grouping ? schema : sourceCatalogue);
|
|
2945
2817
|
if (!cancelled) {
|
|
2946
|
-
setCommittedFields(
|
|
2947
|
-
setServerExecution(
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
else setServerComputed(/* @__PURE__ */ new Map());
|
|
2955
|
-
setRows(res.rows);
|
|
2956
|
-
setTotal(res.total);
|
|
2818
|
+
setCommittedFields(request.result.select);
|
|
2819
|
+
setServerExecution(result.execution);
|
|
2820
|
+
setServerComputed(new Map(result.rows.map((row) => [
|
|
2821
|
+
row[schema.idField],
|
|
2822
|
+
Object.fromEntries(Object.entries(row).filter(([name]) => isComputedField3(name)).map(([name, value]) => [name.slice(10), value && typeof value === "object" && "computedError" in value ? { value: null, error: { code: String(value.computedError) } } : { value }]))
|
|
2823
|
+
])));
|
|
2824
|
+
setRows(result.rows);
|
|
2825
|
+
setTotal(result.total);
|
|
2957
2826
|
setCommittedPage(pageContext);
|
|
2958
2827
|
setCommittedSource({ schema, transport, clientRows });
|
|
2959
2828
|
}
|
|
@@ -3016,7 +2885,7 @@ function useQueryTable(opts) {
|
|
|
3016
2885
|
);
|
|
3017
2886
|
const rowId = useCallback12(
|
|
3018
2887
|
(row) => (() => {
|
|
3019
|
-
const value = idField ?
|
|
2888
|
+
const value = idField ? readFieldValue3(idField, row) : row?.[schema.idField];
|
|
3020
2889
|
return typeof value === "string" || typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
3021
2890
|
})(),
|
|
3022
2891
|
[idField, schema.idField]
|
|
@@ -3075,55 +2944,33 @@ function useQueryTable(opts) {
|
|
|
3075
2944
|
const metricExecution = opts.metricExecution ?? (opts.computedExecution ? pageReady && serverExecution ? serverExecution : opts.computedExecution : void 0);
|
|
3076
2945
|
const v3MetricExecution = opts.metricExecution ?? opts.computedExecution;
|
|
3077
2946
|
const metricSourceCatalogue = useMemo9(
|
|
3078
|
-
() =>
|
|
2947
|
+
() => sourceExecutionSchema3(sourceSchema, v3MetricExecution),
|
|
3079
2948
|
[sourceSchema, v3MetricExecution]
|
|
3080
2949
|
);
|
|
3081
|
-
const
|
|
3082
|
-
if (!metricTransport
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
const scoped = { ...metricQuery, aggregations: runnable };
|
|
3093
|
-
const request = toQueryRequestV32(
|
|
3094
|
-
query.grouping ? { ...scoped, grouping: query.grouping } : scoped,
|
|
3095
|
-
sourceSchema,
|
|
3096
|
-
capabilities,
|
|
3097
|
-
v3MetricExecution,
|
|
3098
|
-
{ now: groupingNow ?? (opts.evaluationNow ?? Date.now)(), rows: false }
|
|
3099
|
-
);
|
|
3100
|
-
if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
|
|
3101
|
-
const result = await metricTransport.fetchQueryV3(request, signal);
|
|
3102
|
-
validateQueryResultV32(result, request, query.grouping ? schema : metricSourceCatalogue);
|
|
3103
|
-
metrics = result.metrics;
|
|
3104
|
-
}
|
|
3105
|
-
if (!refused.size) return { metrics };
|
|
3106
|
-
const byId = new Map(metrics.map((metric) => [metric.id, metric]));
|
|
3107
|
-
return { metrics: (metricQuery.aggregations ?? []).flatMap((clause) => {
|
|
3108
|
-
const issue = refused.get(clause.id);
|
|
3109
|
-
if (issue) return [{ id: clause.id, buckets: [], error: issue }];
|
|
3110
|
-
const metric = byId.get(clause.id);
|
|
3111
|
-
return metric ? [metric] : [];
|
|
3112
|
-
}) };
|
|
2950
|
+
const executeV3Metrics2 = useCallback12(async (metricQuery, signal) => {
|
|
2951
|
+
if (!metricTransport) throw Error("The v3 query executor is unavailable.");
|
|
2952
|
+
return executeV3Metrics(
|
|
2953
|
+
metricTransport,
|
|
2954
|
+
query.grouping ? { ...metricQuery, grouping: query.grouping } : metricQuery,
|
|
2955
|
+
sourceSchema,
|
|
2956
|
+
query.grouping ? schema : metricSourceCatalogue,
|
|
2957
|
+
v3MetricExecution,
|
|
2958
|
+
groupingNow ?? (opts.evaluationNow ?? Date.now)(),
|
|
2959
|
+
signal
|
|
2960
|
+
);
|
|
3113
2961
|
}, [metricTransport, sourceSchema, metricSourceCatalogue, schema, v3MetricExecution, groupKey, groupingNow, opts.evaluationNow]);
|
|
3114
2962
|
const previewGrouping = useCallback12(async (draft, signal) => {
|
|
3115
2963
|
if (!active || !storageReady || restoration.loading || restoration.error) throw Error("Wait for query restoration before previewing groups.");
|
|
3116
|
-
if (!transport?.
|
|
3117
|
-
const request =
|
|
2964
|
+
if (!transport?.queryCapabilities.groupedRows || !draft.grouping) throw Error("The server group preview is unavailable.");
|
|
2965
|
+
const request = toQueryRequestV33(
|
|
3118
2966
|
{ ...draft, aggregations: [] },
|
|
3119
2967
|
sourceSchema,
|
|
3120
2968
|
transport.queryCapabilities,
|
|
3121
2969
|
opts.computedExecution,
|
|
3122
2970
|
{ now: groupingNow ?? (opts.evaluationNow ?? Date.now)() }
|
|
3123
2971
|
);
|
|
3124
|
-
if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
|
|
3125
2972
|
const result = await transport.fetchQueryV3(request, signal);
|
|
3126
|
-
|
|
2973
|
+
validateQueryResultV33(result, request, groupingModel.resultSchema(draft.grouping.keys, draft.grouping.summaries));
|
|
3127
2974
|
return { rows: result.rows, total: result.total, sourceTotal: result.sourceTotal };
|
|
3128
2975
|
}, [transport, sourceSchema, opts.computedExecution, groupingModel, groupingNow, opts.evaluationNow, active, storageReady, restoration.loading, restoration.error]);
|
|
3129
2976
|
const aggState = useAggregations(
|
|
@@ -3135,7 +2982,7 @@ function useQueryTable(opts) {
|
|
|
3135
2982
|
nonce,
|
|
3136
2983
|
validateExecution,
|
|
3137
2984
|
{
|
|
3138
|
-
...metricTransport
|
|
2985
|
+
...metricTransport ? { executeV3: executeV3Metrics2, queryContextKey: groupKey } : {},
|
|
3139
2986
|
...groupingNow !== void 0 ? { now: groupingNow, strictLocalMetrics: true } : {},
|
|
3140
2987
|
shownRows: rows,
|
|
3141
2988
|
rowsReady: metricRowsReady,
|
|
@@ -3279,9 +3126,24 @@ function useQueryTable(opts) {
|
|
|
3279
3126
|
() => setQuery((q) => ({ ...q, offset: Math.max(0, q.offset - q.limit) })),
|
|
3280
3127
|
[setQuery]
|
|
3281
3128
|
);
|
|
3129
|
+
const auxiliaryRef = useRef9({ evaluationNow: opts.evaluationNow, execution: opts.computedExecution });
|
|
3130
|
+
auxiliaryRef.current = { evaluationNow: opts.evaluationNow, execution: opts.computedExecution };
|
|
3131
|
+
const auxiliaryOptions = useCallback12(() => {
|
|
3132
|
+
const { evaluationNow, execution: execution2 } = auxiliaryRef.current;
|
|
3133
|
+
return { now: (evaluationNow ?? Date.now)(), ...execution2 ? { execution: execution2 } : {} };
|
|
3134
|
+
}, []);
|
|
3135
|
+
const remoteDistinct = useCallback12(async (field, search, signal) => {
|
|
3136
|
+
const definition = sourceCatalogue.fields.find((f) => f.name === field);
|
|
3137
|
+
if (!metadataTransport || !definition || !isPushdownFilter(definition)) return { values: [], hasMore: false };
|
|
3138
|
+
return fetchDistinctValuesV3(metadataTransport, sourceCatalogue, field, search.slice(0, 2e3), { ...auxiliaryOptions(), limit: AUTOCOMPLETE_LIMIT }, signal);
|
|
3139
|
+
}, [metadataTransport, sourceCatalogue, auxiliaryOptions]);
|
|
3140
|
+
const remoteFieldStats = useCallback12(
|
|
3141
|
+
async (fields, signal) => metadataTransport && opts.remoteFieldStats ? fetchFieldStatsV3(metadataTransport, sourceCatalogue, fields, auxiliaryOptions(), signal) : {},
|
|
3142
|
+
[metadataTransport, sourceCatalogue, auxiliaryOptions, opts.remoteFieldStats]
|
|
3143
|
+
);
|
|
3282
3144
|
const filterValues = useCallback12(
|
|
3283
|
-
async (field, search) => {
|
|
3284
|
-
if (!metadataTransport
|
|
3145
|
+
async (field, search, signal) => {
|
|
3146
|
+
if (!metadataTransport || query.grouping && !query.grouping.keys.includes(field)) {
|
|
3285
3147
|
const f = byName.get(field);
|
|
3286
3148
|
if (!f || !clientRows) return { values: [], hasMore: false };
|
|
3287
3149
|
const fieldHasNull = fieldHasNullInRows(f, clientRows);
|
|
@@ -3292,35 +3154,39 @@ function useQueryTable(opts) {
|
|
|
3292
3154
|
hasNull: fieldHasNull
|
|
3293
3155
|
};
|
|
3294
3156
|
}
|
|
3295
|
-
return
|
|
3157
|
+
return remoteDistinct(field, search, signal);
|
|
3296
3158
|
},
|
|
3297
|
-
[metadataTransport, byName, clientRows, groupKey]
|
|
3159
|
+
[metadataTransport, remoteDistinct, byName, clientRows, groupKey]
|
|
3298
3160
|
);
|
|
3299
3161
|
const fieldStats = useCallback12(
|
|
3300
3162
|
async (fields, signal) => {
|
|
3301
3163
|
const requested = [...new Set(fields)];
|
|
3302
|
-
if (metadataTransport
|
|
3303
|
-
|
|
3304
|
-
(name) => byName.get(name)?.source.kind === "backend"
|
|
3305
|
-
);
|
|
3306
|
-
return backendFields.length ? metadataTransport.fetchFieldStats(backendFields, signal) : {};
|
|
3307
|
-
}
|
|
3164
|
+
if (metadataTransport && !query.grouping)
|
|
3165
|
+
return remoteFieldStats(requested.filter((name) => byName.get(name)?.source.kind === "backend"), signal);
|
|
3308
3166
|
if (!clientRows) return {};
|
|
3309
|
-
const result =
|
|
3167
|
+
const result = [];
|
|
3310
3168
|
for (const name of requested) {
|
|
3311
3169
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
3312
3170
|
const field = byName.get(name);
|
|
3313
3171
|
if (!field) continue;
|
|
3314
3172
|
const distinct = /* @__PURE__ */ new Set();
|
|
3315
3173
|
for (const row of clientRows) {
|
|
3316
|
-
const value =
|
|
3174
|
+
const value = readFieldValue3(field, row);
|
|
3317
3175
|
if (value !== null) distinct.add(JSON.stringify(value));
|
|
3318
3176
|
}
|
|
3319
|
-
result[name
|
|
3177
|
+
result.push([name, { distinct: distinct.size }]);
|
|
3320
3178
|
}
|
|
3321
|
-
return result;
|
|
3179
|
+
return Object.fromEntries(result);
|
|
3322
3180
|
},
|
|
3323
|
-
[metadataTransport, byName, clientRows, groupKey]
|
|
3181
|
+
[metadataTransport, remoteFieldStats, byName, clientRows, groupKey]
|
|
3182
|
+
);
|
|
3183
|
+
const identityLookup = useMemo9(() => {
|
|
3184
|
+
const identity = sourceCatalogue.fields.find((field) => field.name === sourceCatalogue.idField);
|
|
3185
|
+
return !identity || isPushdownFilter(identity) && opsForField2(identity).includes("=");
|
|
3186
|
+
}, [sourceCatalogue]);
|
|
3187
|
+
const fetchRow = useCallback12(
|
|
3188
|
+
async (id, signal, select2) => fetchRowV3(metadataTransport, sourceCatalogue, id, { ...auxiliaryOptions(), ...select2?.length ? { select: select2 } : {} }, signal),
|
|
3189
|
+
[metadataTransport, sourceCatalogue, auxiliaryOptions]
|
|
3324
3190
|
);
|
|
3325
3191
|
const refresh = useCallback12(() => {
|
|
3326
3192
|
setNonce((n) => n + 1);
|
|
@@ -3385,17 +3251,17 @@ function useQueryTable(opts) {
|
|
|
3385
3251
|
);
|
|
3386
3252
|
const refreshRow = useCallback12(
|
|
3387
3253
|
async (id) => {
|
|
3388
|
-
if (
|
|
3254
|
+
if (!transport) return;
|
|
3255
|
+
if (!identityLookup || query.grouping || committedFields.some(isComputedField3) || query.orderBy.some((term) => isComputedField3(term.field))) {
|
|
3389
3256
|
refresh();
|
|
3390
3257
|
return;
|
|
3391
3258
|
}
|
|
3392
|
-
|
|
3393
|
-
const updated = await transport.fetchRow(id);
|
|
3259
|
+
const updated = await fetchRow(id, void 0, committedFields);
|
|
3394
3260
|
setRows(
|
|
3395
3261
|
(prev) => prev.map((r) => rowId(r) === id ? updated ?? r : r)
|
|
3396
3262
|
);
|
|
3397
3263
|
},
|
|
3398
|
-
[transport, rowId,
|
|
3264
|
+
[transport, fetchRow, rowId, groupKey, committedFields, query.orderBy, refresh, identityLookup]
|
|
3399
3265
|
);
|
|
3400
3266
|
const visibleFields = useMemo9(
|
|
3401
3267
|
() => selectedFields2(columnSchema, query),
|
|
@@ -3494,9 +3360,23 @@ function useQueryTable(opts) {
|
|
|
3494
3360
|
}, []);
|
|
3495
3361
|
const sourceRowId = useCallback12((row) => {
|
|
3496
3362
|
const field = sourceSchema.fields.find((f) => f.name === sourceSchema.idField);
|
|
3497
|
-
const value = field ?
|
|
3363
|
+
const value = field ? readFieldValue3(field, row) : row[sourceSchema.idField];
|
|
3498
3364
|
return typeof value === "string" || typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
3499
3365
|
}, [sourceSchema]);
|
|
3366
|
+
const detailCatalogue = typeof opts.itemDetails === "object" ? opts.itemDetails.schema : void 0;
|
|
3367
|
+
const lookupCatalogue = useMemo9(
|
|
3368
|
+
() => !detailCatalogue ? sourceCatalogue : {
|
|
3369
|
+
...sourceCatalogue,
|
|
3370
|
+
fields: [...sourceCatalogue.fields, ...detailCatalogue.fields.filter((field) => !sourceCatalogue.fields.some((f) => f.name === field.name))]
|
|
3371
|
+
},
|
|
3372
|
+
[sourceCatalogue, detailCatalogue]
|
|
3373
|
+
);
|
|
3374
|
+
const lookupCatalogueRef = useRef9(lookupCatalogue);
|
|
3375
|
+
lookupCatalogueRef.current = lookupCatalogue;
|
|
3376
|
+
const fetchItem = useCallback12(
|
|
3377
|
+
async (id, signal) => fetchRowV3(metadataTransport, lookupCatalogueRef.current, id, auxiliaryOptions(), signal),
|
|
3378
|
+
[metadataTransport, auxiliaryOptions]
|
|
3379
|
+
);
|
|
3500
3380
|
const itemDetails = useItemDetails({
|
|
3501
3381
|
schema: query.grouping ? sourceSchema : displaySchema,
|
|
3502
3382
|
query: sourceQuery,
|
|
@@ -3504,8 +3384,9 @@ function useQueryTable(opts) {
|
|
|
3504
3384
|
options: opts.itemDetails,
|
|
3505
3385
|
rows: query.grouping ? sourceRows ?? [] : rows,
|
|
3506
3386
|
clientRows: sourceRows,
|
|
3387
|
+
// Without an identity lookup, item details read the rows on the page.
|
|
3507
3388
|
rowId: sourceRowId,
|
|
3508
|
-
|
|
3389
|
+
fetchRow: metadataTransport && identityLookup ? fetchItem : void 0,
|
|
3509
3390
|
refreshKey: nonce,
|
|
3510
3391
|
links,
|
|
3511
3392
|
queryParameter: parameter,
|
|
@@ -3546,7 +3427,7 @@ function useQueryTable(opts) {
|
|
|
3546
3427
|
rows,
|
|
3547
3428
|
clientRows: void 0,
|
|
3548
3429
|
rowId,
|
|
3549
|
-
|
|
3430
|
+
fetchRow: void 0,
|
|
3550
3431
|
refreshKey: nonce,
|
|
3551
3432
|
links,
|
|
3552
3433
|
queryParameter: parameter,
|
|
@@ -3618,25 +3499,20 @@ function useQueryTable(opts) {
|
|
|
3618
3499
|
visibleFields,
|
|
3619
3500
|
rowId
|
|
3620
3501
|
};
|
|
3621
|
-
const sourceFilterValues = useCallback12(async (name, search) => {
|
|
3622
|
-
if (metadataTransport
|
|
3502
|
+
const sourceFilterValues = useCallback12(async (name, search, signal) => {
|
|
3503
|
+
if (metadataTransport) return remoteDistinct(name, search, signal);
|
|
3623
3504
|
const field = groupingModel.sourceField(name);
|
|
3624
3505
|
return field && sourceRows ? { ...distinctValuesFromRows(field, sourceRows, search), hasNull: fieldHasNullInRows(field, sourceRows) } : { values: [], hasMore: false };
|
|
3625
|
-
}, [metadataTransport, groupingModel, sourceRows]);
|
|
3506
|
+
}, [metadataTransport, remoteDistinct, groupingModel, sourceRows]);
|
|
3626
3507
|
const sourceFieldStats = useCallback12(async (names, signal) => {
|
|
3627
3508
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
3628
3509
|
const requested = [...new Set(names)];
|
|
3629
|
-
if (metadataTransport
|
|
3630
|
-
const backendFields = requested.filter(
|
|
3631
|
-
(name) => sourceCatalogue.fields.find((field) => field.name === name)?.source.kind === "backend"
|
|
3632
|
-
);
|
|
3633
|
-
return backendFields.length ? metadataTransport.fetchFieldStats(backendFields, signal) : {};
|
|
3634
|
-
}
|
|
3510
|
+
if (metadataTransport) return remoteFieldStats(requested, signal);
|
|
3635
3511
|
return Object.fromEntries(requested.flatMap((name) => {
|
|
3636
3512
|
const field = groupingModel.sourceField(name);
|
|
3637
|
-
return field && sourceRows ? [[name, { distinct: new Set(sourceRows.map((row) => JSON.stringify(
|
|
3513
|
+
return field && sourceRows ? [[name, { distinct: new Set(sourceRows.map((row) => JSON.stringify(readFieldValue3(field, row)))).size }]] : [];
|
|
3638
3514
|
}));
|
|
3639
|
-
}, [metadataTransport, groupingModel, sourceRows
|
|
3515
|
+
}, [metadataTransport, remoteFieldStats, groupingModel, sourceRows]);
|
|
3640
3516
|
const sourceApi = {
|
|
3641
3517
|
...sourceFilters,
|
|
3642
3518
|
loading,
|