@platforma-sdk/model 1.65.9 → 1.66.2
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/dist/block_model.cjs +8 -11
- package/dist/block_model.cjs.map +1 -1
- package/dist/block_model.d.ts.map +1 -1
- package/dist/block_model.js +8 -10
- package/dist/block_model.js.map +1 -1
- package/dist/columns/column_collection_builder.cjs +61 -74
- package/dist/columns/column_collection_builder.cjs.map +1 -1
- package/dist/columns/column_collection_builder.d.ts +16 -22
- package/dist/columns/column_collection_builder.d.ts.map +1 -1
- package/dist/columns/column_collection_builder.js +62 -75
- package/dist/columns/column_collection_builder.js.map +1 -1
- package/dist/columns/column_selector.cjs.map +1 -1
- package/dist/columns/column_selector.d.ts +1 -1
- package/dist/columns/column_selector.js.map +1 -1
- package/dist/columns/column_snapshot.cjs.map +1 -1
- package/dist/columns/column_snapshot.d.ts +4 -4
- package/dist/columns/column_snapshot.d.ts.map +1 -1
- package/dist/columns/column_snapshot.js.map +1 -1
- package/dist/columns/ctx_column_sources.cjs.map +1 -1
- package/dist/columns/ctx_column_sources.d.ts +1 -1
- package/dist/columns/ctx_column_sources.d.ts.map +1 -1
- package/dist/columns/ctx_column_sources.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs +17 -18
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js +17 -18
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +99 -91
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +16 -16
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +102 -94
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +32 -23
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts +5 -5
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +33 -24
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts +2 -3
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs +133 -16
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.d.ts +8 -6
- package/dist/components/PlDataTable/createPlDataTable/utils.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.js +130 -17
- package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
- package/dist/components/PlDataTable/labels.cjs +1 -2
- package/dist/components/PlDataTable/labels.cjs.map +1 -1
- package/dist/components/PlDataTable/labels.js +1 -2
- package/dist/components/PlDataTable/labels.js.map +1 -1
- package/dist/filters/distill.cjs +73 -30
- package/dist/filters/distill.cjs.map +1 -1
- package/dist/filters/distill.d.ts.map +1 -1
- package/dist/filters/distill.js +73 -30
- package/dist/filters/distill.js.map +1 -1
- package/dist/index.cjs +19 -15
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -4
- package/dist/labels/derive_distinct_tooltips.cjs +85 -0
- package/dist/labels/derive_distinct_tooltips.cjs.map +1 -0
- package/dist/labels/derive_distinct_tooltips.d.ts +17 -0
- package/dist/labels/derive_distinct_tooltips.d.ts.map +1 -0
- package/dist/labels/derive_distinct_tooltips.js +84 -0
- package/dist/labels/derive_distinct_tooltips.js.map +1 -0
- package/dist/labels/index.cjs +1 -0
- package/dist/labels/index.d.ts +2 -1
- package/dist/labels/index.js +1 -0
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/dist/render/api.cjs +8 -13
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +8 -11
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/api.js +8 -13
- package/dist/render/api.js.map +1 -1
- package/dist/services/get_services.cjs +19 -0
- package/dist/services/get_services.cjs.map +1 -0
- package/dist/services/get_services.d.ts +7 -0
- package/dist/services/get_services.d.ts.map +1 -0
- package/dist/services/get_services.js +19 -0
- package/dist/services/get_services.js.map +1 -0
- package/dist/services/index.cjs +1 -0
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +1 -0
- package/dist/services/service_bridge.cjs +4 -4
- package/dist/services/service_bridge.cjs.map +1 -1
- package/dist/services/service_bridge.d.ts +4 -4
- package/dist/services/service_bridge.d.ts.map +1 -1
- package/dist/services/service_bridge.js +4 -4
- package/dist/services/service_bridge.js.map +1 -1
- package/package.json +6 -6
- package/src/block_model.ts +8 -11
- package/src/columns/column_collection_builder.test.ts +75 -30
- package/src/columns/column_collection_builder.ts +96 -133
- package/src/columns/column_selector.ts +1 -1
- package/src/columns/column_snapshot.ts +7 -4
- package/src/columns/ctx_column_sources.ts +1 -3
- package/src/components/PFrameForGraphs.test.ts +4 -4
- package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/createPTableDefV3.ts +44 -21
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +202 -218
- package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +69 -56
- package/src/components/PlDataTable/createPlDataTable/index.ts +6 -7
- package/src/components/PlDataTable/createPlDataTable/utils.test.ts +97 -1
- package/src/components/PlDataTable/createPlDataTable/utils.ts +190 -35
- package/src/components/PlDataTable/labels.ts +3 -7
- package/src/filters/distill.test.ts +91 -0
- package/src/filters/distill.ts +102 -46
- package/src/labels/derive_distinct_tooltips.test.ts +233 -0
- package/src/labels/derive_distinct_tooltips.ts +130 -0
- package/src/labels/index.ts +1 -0
- package/src/render/api.ts +15 -50
- package/src/services/get_services.ts +28 -0
- package/src/services/index.ts +1 -0
- package/src/services/service_bridge.ts +5 -5
|
@@ -1,34 +1,24 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AxisQualification,
|
|
3
|
-
ColumnAxesWithQualifications,
|
|
4
3
|
DiscoverColumnsConstraints,
|
|
5
4
|
DiscoverColumnsRequest,
|
|
6
5
|
DiscoverColumnsResponse,
|
|
7
6
|
MultiColumnSelector,
|
|
8
7
|
NativePObjectId,
|
|
9
|
-
PColumnIdAndSpec,
|
|
10
8
|
PColumnSpec,
|
|
11
9
|
PObjectId,
|
|
12
|
-
SUniversalPColumnId,
|
|
13
|
-
} from "@milaboratories/pl-model-common";
|
|
14
|
-
import {
|
|
15
|
-
AnchoredIdDeriver,
|
|
16
|
-
canonicalizeJson,
|
|
17
|
-
deriveNativeId,
|
|
18
|
-
getAxesId,
|
|
19
|
-
isPColumnSpec,
|
|
20
10
|
} from "@milaboratories/pl-model-common";
|
|
11
|
+
import { deriveNativeId, isPColumnSpec } from "@milaboratories/pl-model-common";
|
|
21
12
|
import type { ColumnSelector, RelaxedColumnSelector } from "./column_selector";
|
|
22
13
|
import { convertColumnSelectorToMultiColumnSelector } from "./column_selector";
|
|
23
14
|
import { TreeNodeAccessor } from "../render/accessor";
|
|
24
15
|
import type { ColumnSnapshot } from "./column_snapshot";
|
|
25
|
-
import { createColumnSnapshot } from "./column_snapshot";
|
|
26
16
|
import type { ColumnSnapshotProvider, ColumnSource } from "./column_snapshot_provider";
|
|
27
17
|
import { ArrayColumnProvider, toColumnSnapshotProvider } from "./column_snapshot_provider";
|
|
28
18
|
|
|
29
19
|
import type { PFrameSpecDriver, PoolEntry, SpecFrameHandle } from "@milaboratories/pl-model-common";
|
|
30
20
|
import { throwError } from "@milaboratories/helpers";
|
|
31
|
-
import {
|
|
21
|
+
import { getService } from "../services";
|
|
32
22
|
|
|
33
23
|
// --- FindColumnsOptions ---
|
|
34
24
|
|
|
@@ -47,9 +37,6 @@ export interface ColumnCollection extends Disposable {
|
|
|
47
37
|
/** Release the underlying spec frame WASM resource. */
|
|
48
38
|
dispose(): void;
|
|
49
39
|
|
|
50
|
-
/** Point lookup by provider-native ID. */
|
|
51
|
-
getColumn(id: PObjectId): undefined | ColumnSnapshot<PObjectId>;
|
|
52
|
-
|
|
53
40
|
/** Find columns matching selectors. Returns flat list of snapshots.
|
|
54
41
|
* No axis compatibility matching, no linker traversal.
|
|
55
42
|
* Never returns undefined — the "not ready" state was absorbed by the builder. */
|
|
@@ -64,10 +51,7 @@ export interface AnchoredColumnCollection extends Disposable {
|
|
|
64
51
|
dispose(): void;
|
|
65
52
|
|
|
66
53
|
/** List of anchors used for discovery, with their resolved specs. */
|
|
67
|
-
getAnchors(): Map<string,
|
|
68
|
-
|
|
69
|
-
/** Point lookup by anchored ID. */
|
|
70
|
-
getColumn(id: SUniversalPColumnId): undefined | ColumnSnapshot<SUniversalPColumnId>;
|
|
54
|
+
getAnchors(): Map<string, ColumnSnapshot<PObjectId>>;
|
|
71
55
|
|
|
72
56
|
/** Axis-aware column discovery. */
|
|
73
57
|
findColumns(options?: AnchoredFindColumnsOptions): ColumnMatch[];
|
|
@@ -87,34 +71,32 @@ export interface AnchoredFindColumnsOptions extends FindColumnsOptions {
|
|
|
87
71
|
/** Result of anchored discovery — column snapshot + routing info. */
|
|
88
72
|
export interface ColumnMatch {
|
|
89
73
|
/** Column snapshot with anchored SUniversalPColumnId. */
|
|
90
|
-
readonly column: ColumnSnapshot<
|
|
91
|
-
/**
|
|
92
|
-
readonly originalId: PObjectId;
|
|
93
|
-
/** Match variants — different paths/qualifications that reach this column. */
|
|
74
|
+
readonly column: ColumnSnapshot<PObjectId>;
|
|
75
|
+
/** Match variants — different ways (paths/qualifications) to reach this column. */
|
|
94
76
|
readonly variants: MatchVariant[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A single mapping variant describing how a hit column can be integrated. */
|
|
80
|
+
export interface MatchVariant {
|
|
81
|
+
/** Full qualifications needed for integration. */
|
|
82
|
+
readonly qualifications: MatchQualifications;
|
|
83
|
+
/** Distinctive (minimal) qualifications needed for integration. */
|
|
84
|
+
readonly distinctiveQualifications: MatchQualifications;
|
|
95
85
|
/** Linker steps traversed to reach this hit; empty for direct matches. */
|
|
96
86
|
readonly path: {
|
|
97
|
-
linker: ColumnSnapshot<
|
|
87
|
+
linker: ColumnSnapshot<PObjectId>;
|
|
98
88
|
qualifications: AxisQualification[];
|
|
99
89
|
}[];
|
|
100
90
|
}
|
|
101
91
|
|
|
102
|
-
/** Qualifications needed for both
|
|
92
|
+
/** Qualifications needed for both already-integrated anchor columns and the hit column. */
|
|
103
93
|
export interface MatchQualifications {
|
|
104
|
-
/** Qualifications for
|
|
105
|
-
readonly forQueries: AxisQualification[]
|
|
94
|
+
/** Qualifications for already-integrated anchor columns */
|
|
95
|
+
readonly forQueries: Record<PObjectId, AxisQualification[]>;
|
|
106
96
|
/** Qualifications for the hit column. */
|
|
107
97
|
readonly forHit: AxisQualification[];
|
|
108
98
|
}
|
|
109
99
|
|
|
110
|
-
/** A single mapping variant describing how a hit column can be integrated. */
|
|
111
|
-
export interface MatchVariant {
|
|
112
|
-
/** Full qualifications needed for integration. */
|
|
113
|
-
readonly qualifications: MatchQualifications;
|
|
114
|
-
/** Distinctive (minimal) qualifications needed for integration. */
|
|
115
|
-
readonly distinctiveQualifications: MatchQualifications;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
100
|
// --- Build options ---
|
|
119
101
|
|
|
120
102
|
export interface BuildOptions {
|
|
@@ -139,7 +121,7 @@ export interface AnchoredBuildOptions extends BuildOptions {
|
|
|
139
121
|
export class ColumnCollectionBuilder {
|
|
140
122
|
private readonly providers: ColumnSnapshotProvider[] = [];
|
|
141
123
|
|
|
142
|
-
constructor(private readonly specDriver: PFrameSpecDriver) {}
|
|
124
|
+
constructor(private readonly specDriver: PFrameSpecDriver = getService("pframeSpec")) {}
|
|
143
125
|
|
|
144
126
|
/**
|
|
145
127
|
* Register a column source. Sources added first take precedence for dedup.
|
|
@@ -232,12 +214,6 @@ class ColumnCollectionImpl implements ColumnCollection, Disposable {
|
|
|
232
214
|
this.dispose();
|
|
233
215
|
}
|
|
234
216
|
|
|
235
|
-
getColumn(id: PObjectId): undefined | ColumnSnapshot<PObjectId> {
|
|
236
|
-
const col = this.columns.get(id);
|
|
237
|
-
if (col === undefined) return undefined;
|
|
238
|
-
return this.toSnapshot(col);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
217
|
findColumns(options?: FindColumnsOptions): ColumnSnapshot<PObjectId>[] {
|
|
242
218
|
const includeColumns = options?.include ? toMultiColumnSelectors(options.include) : undefined;
|
|
243
219
|
const excludeColumns = options?.exclude ? toMultiColumnSelectors(options.exclude) : undefined;
|
|
@@ -253,15 +229,10 @@ class ColumnCollectionImpl implements ColumnCollection, Disposable {
|
|
|
253
229
|
// Map hits back to snapshots
|
|
254
230
|
const results = response.hits
|
|
255
231
|
.map((hit) => this.columns.get(hit.hit.columnId as PObjectId))
|
|
256
|
-
.filter((col): col is ColumnSnapshot<PObjectId> => col !== undefined)
|
|
257
|
-
.map((col) => this.toSnapshot(col));
|
|
232
|
+
.filter((col): col is ColumnSnapshot<PObjectId> => col !== undefined);
|
|
258
233
|
|
|
259
234
|
return results;
|
|
260
235
|
}
|
|
261
|
-
|
|
262
|
-
private toSnapshot(col: ColumnSnapshot<PObjectId>): ColumnSnapshot<PObjectId> {
|
|
263
|
-
return remapSnapshot(col.id, col);
|
|
264
|
-
}
|
|
265
236
|
}
|
|
266
237
|
|
|
267
238
|
// --- AnchoredColumnCollectionImpl ---
|
|
@@ -271,12 +242,8 @@ interface AnchoredColumnCollectionImplOptions extends ColumnCollectionImplOption
|
|
|
271
242
|
}
|
|
272
243
|
|
|
273
244
|
class AnchoredColumnCollectionImpl implements AnchoredColumnCollection, Disposable {
|
|
274
|
-
private readonly anchorsMap: Map<string,
|
|
245
|
+
private readonly anchorsMap: Map<string, ColumnSnapshot<PObjectId>>;
|
|
275
246
|
private readonly columnsMap: Map<PObjectId, ColumnSnapshot<PObjectId>>;
|
|
276
|
-
|
|
277
|
-
private readonly idDeriver: AnchoredIdDeriver;
|
|
278
|
-
private readonly uniqAnchorAxes: ColumnAxesWithQualifications[];
|
|
279
|
-
private readonly idToOriginalIdMap: Map<SUniversalPColumnId, PObjectId>;
|
|
280
247
|
private readonly specFrameEntry: PoolEntry<SpecFrameHandle>;
|
|
281
248
|
|
|
282
249
|
constructor(
|
|
@@ -293,21 +260,6 @@ class AnchoredColumnCollectionImpl implements AnchoredColumnCollection, Disposab
|
|
|
293
260
|
options.columns,
|
|
294
261
|
this.specDriver.discoverColumns.bind(this.specDriver, this.specFrameEntry.key),
|
|
295
262
|
);
|
|
296
|
-
this.idDeriver = new AnchoredIdDeriver(
|
|
297
|
-
Object.fromEntries(
|
|
298
|
-
Array.from(this.anchorsMap.entries()).map(([k, v]) => [k, v.spec] as const),
|
|
299
|
-
),
|
|
300
|
-
);
|
|
301
|
-
this.uniqAnchorAxes = uniqBy(
|
|
302
|
-
Array.from(this.anchorsMap.values(), ({ spec }) => ({
|
|
303
|
-
axesSpec: spec.axesSpec,
|
|
304
|
-
qualifications: [],
|
|
305
|
-
})),
|
|
306
|
-
(axis) => canonicalizeJson(getAxesId(axis.axesSpec)) + canonicalizeJson(axis.qualifications),
|
|
307
|
-
);
|
|
308
|
-
this.idToOriginalIdMap = new Map(
|
|
309
|
-
options.columns.map((col) => [this.idDeriver.deriveS(col.spec), col.id] as const),
|
|
310
|
-
);
|
|
311
263
|
}
|
|
312
264
|
|
|
313
265
|
dispose(): void {
|
|
@@ -318,64 +270,60 @@ class AnchoredColumnCollectionImpl implements AnchoredColumnCollection, Disposab
|
|
|
318
270
|
this.dispose();
|
|
319
271
|
}
|
|
320
272
|
|
|
321
|
-
getAnchors(): Map<string,
|
|
273
|
+
getAnchors(): Map<string, ColumnSnapshot<PObjectId>> {
|
|
322
274
|
return this.anchorsMap;
|
|
323
275
|
}
|
|
324
276
|
|
|
325
|
-
getColumn(id: SUniversalPColumnId): undefined | ColumnSnapshot<SUniversalPColumnId> {
|
|
326
|
-
const origId = this.idToOriginalIdMap.get(id);
|
|
327
|
-
if (origId === undefined) return undefined;
|
|
328
|
-
const col = this.columnsMap.get(origId);
|
|
329
|
-
if (col === undefined) return undefined;
|
|
330
|
-
return remapSnapshot(id, col);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
277
|
findColumns(options?: AnchoredFindColumnsOptions): ColumnMatch[] {
|
|
334
278
|
const mode = options?.mode ?? "enrichment";
|
|
335
279
|
const constraints = matchingModeToConstraints(mode);
|
|
336
280
|
const includeColumns = options?.include ? toMultiColumnSelectors(options.include) : undefined;
|
|
337
281
|
const excludeColumns = options?.exclude ? toMultiColumnSelectors(options.exclude) : undefined;
|
|
338
|
-
|
|
282
|
+
const anchors = Array.from(this.anchorsMap.values());
|
|
339
283
|
const response = this.specDriver.discoverColumns(this.specFrameEntry.key, {
|
|
340
284
|
includeColumns,
|
|
341
285
|
excludeColumns,
|
|
342
286
|
constraints,
|
|
343
287
|
maxHops: options?.maxHops ?? 4,
|
|
344
|
-
axes:
|
|
288
|
+
axes: anchors.map((anchor) => ({
|
|
289
|
+
axesSpec: anchor.spec.axesSpec,
|
|
290
|
+
qualifications: [],
|
|
291
|
+
})),
|
|
345
292
|
});
|
|
346
293
|
|
|
347
|
-
|
|
348
|
-
// The same physical column may appear multiple times when reachable through
|
|
349
|
-
// different linker paths — each hit becomes a separate ColumnMatch so that
|
|
350
|
-
// the caller can expand them into distinct table columns.
|
|
351
|
-
const results: ColumnMatch[] = [];
|
|
352
|
-
for (const hit of response.hits) {
|
|
294
|
+
const byColumn = response.hits.reduce<Map<PObjectId, ColumnMatch>>((acc, hit) => {
|
|
353
295
|
const origId = hit.hit.columnId as PObjectId;
|
|
354
296
|
const col =
|
|
355
297
|
this.columnsMap.get(origId) ??
|
|
356
298
|
throwError(`Column with id ${origId} not found in collection`);
|
|
357
|
-
const associatedId = this.idDeriver.deriveS(col.spec);
|
|
358
|
-
|
|
359
|
-
results.push({
|
|
360
|
-
path: hit.path.map((step) => {
|
|
361
|
-
if (step.type !== "linker")
|
|
362
|
-
throw new Error(`Unexpected discover-columns step type: ${step.type}`);
|
|
363
|
-
return {
|
|
364
|
-
linker: remapSnapshot(
|
|
365
|
-
this.idDeriver.deriveS(step.linker.spec),
|
|
366
|
-
this.columnsMap.get(step.linker.columnId) ??
|
|
367
|
-
throwError(`Linker column with id ${step.linker.columnId} not found in collection`),
|
|
368
|
-
),
|
|
369
|
-
qualifications: step.qualifications,
|
|
370
|
-
};
|
|
371
|
-
}),
|
|
372
|
-
column: remapSnapshot(associatedId, col),
|
|
373
|
-
variants: hit.mappingVariants,
|
|
374
|
-
originalId: origId,
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
299
|
|
|
378
|
-
|
|
300
|
+
const path = hit.path.map((step) => {
|
|
301
|
+
if (step.type !== "linker") {
|
|
302
|
+
throw new Error(`Unexpected discover-columns step type: ${step.type}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
linker:
|
|
307
|
+
this.columnsMap.get(step.linker.columnId) ??
|
|
308
|
+
throwError(`Linker column with id ${step.linker.columnId} not found in collection`),
|
|
309
|
+
qualifications: step.qualifications,
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
const variants: MatchVariant[] = hit.mappingVariants.map((v) => ({
|
|
313
|
+
path,
|
|
314
|
+
qualifications: remapFromIdxToId(v.qualifications, anchors),
|
|
315
|
+
distinctiveQualifications: remapFromIdxToId(v.distinctiveQualifications, anchors),
|
|
316
|
+
}));
|
|
317
|
+
const existing = acc.get(origId);
|
|
318
|
+
return acc.set(
|
|
319
|
+
origId,
|
|
320
|
+
existing === undefined
|
|
321
|
+
? { column: col, variants }
|
|
322
|
+
: { ...existing, variants: [...existing.variants, ...variants] },
|
|
323
|
+
);
|
|
324
|
+
}, new Map());
|
|
325
|
+
|
|
326
|
+
return Array.from(byColumn.values());
|
|
379
327
|
}
|
|
380
328
|
}
|
|
381
329
|
|
|
@@ -402,15 +350,7 @@ function collectColumns(providers: ColumnSnapshotProvider[]): ColumnSnapshot<POb
|
|
|
402
350
|
|
|
403
351
|
// --- Shared snapshot helpers ---
|
|
404
352
|
|
|
405
|
-
/**
|
|
406
|
-
function remapSnapshot<Id extends PObjectId>(
|
|
407
|
-
id: Id,
|
|
408
|
-
col: ColumnSnapshot<PObjectId>,
|
|
409
|
-
): ColumnSnapshot<Id> {
|
|
410
|
-
return createColumnSnapshot(id, col.spec, col.data, col.dataStatus);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/** Normalize SDK ColumnSelectorInput to MultiColumnSelector[]. */
|
|
353
|
+
/** Normalize ColumnSelector (relaxed, single or array) to MultiColumnSelector[]. */
|
|
414
354
|
function toMultiColumnSelectors(input: ColumnSelector): MultiColumnSelector[] {
|
|
415
355
|
return convertColumnSelectorToMultiColumnSelector(input);
|
|
416
356
|
}
|
|
@@ -418,40 +358,44 @@ function toMultiColumnSelectors(input: ColumnSelector): MultiColumnSelector[] {
|
|
|
418
358
|
// --- Anchor resolution ---
|
|
419
359
|
|
|
420
360
|
/**
|
|
421
|
-
* Resolve each anchor
|
|
422
|
-
* -
|
|
423
|
-
* -
|
|
361
|
+
* Resolve each anchor entry to a ColumnSnapshot from the collected columns.
|
|
362
|
+
* - PObjectId (string): looked up by id in the collected columns
|
|
363
|
+
* - PColumnSpec: matched by deriveNativeId against collected columns
|
|
364
|
+
* - RelaxedColumnSelector: resolved via discoverColumns in "exact" mode;
|
|
365
|
+
* must match exactly one column
|
|
366
|
+
* Throws on unresolved, ambiguous, or duplicated matches. Requires at least one
|
|
367
|
+
* anchor to resolve.
|
|
424
368
|
*/
|
|
425
369
|
function resolveAnchorMap(
|
|
426
370
|
anchors: Record<string, AnchorEntry>,
|
|
427
371
|
columns: ColumnSnapshot<PObjectId>[],
|
|
428
372
|
discoverColumns: (request: DiscoverColumnsRequest) => DiscoverColumnsResponse,
|
|
429
|
-
): Map<string,
|
|
430
|
-
const result = new Map<string,
|
|
373
|
+
): Map<string, ColumnSnapshot<PObjectId>> {
|
|
374
|
+
const result = new Map<string, ColumnSnapshot<PObjectId>>();
|
|
431
375
|
const resovedIds = new Set<PObjectId>();
|
|
432
376
|
const getDuplicateError = (key: string) =>
|
|
433
377
|
`Anchor "${key}": selector matched a column that was already matched by another anchor; please refine the selector to match a different column`;
|
|
434
378
|
|
|
435
|
-
for (const [
|
|
379
|
+
for (const [name, anchor] of Object.entries(anchors)) {
|
|
436
380
|
if (typeof anchor === "string") {
|
|
437
381
|
const found =
|
|
438
382
|
columns.find((col) => col.id === anchor) ??
|
|
439
|
-
throwError(`Anchor "${
|
|
383
|
+
throwError(`Anchor "${name}": column with id "${anchor}" not found in sources`);
|
|
440
384
|
if (resovedIds.has(found.id)) {
|
|
441
|
-
throwError(getDuplicateError(
|
|
385
|
+
throwError(getDuplicateError(name));
|
|
442
386
|
}
|
|
443
|
-
result.set(
|
|
387
|
+
result.set(name, found);
|
|
444
388
|
resovedIds.add(found.id);
|
|
445
389
|
} else if ("kind" in anchor) {
|
|
446
|
-
if (!isPColumnSpec(anchor)) throwError(`Anchor "${
|
|
390
|
+
if (!isPColumnSpec(anchor)) throwError(`Anchor "${name}": invalid PColumnSpec`);
|
|
447
391
|
const nativeId = deriveNativeId(anchor);
|
|
448
392
|
const found =
|
|
449
393
|
columns.find((col) => deriveNativeId(col.spec) === nativeId) ??
|
|
450
|
-
throwError(`Anchor "${
|
|
394
|
+
throwError(`Anchor "${name}": no column matching spec found in sources`);
|
|
451
395
|
if (resovedIds.has(found.id)) {
|
|
452
|
-
throwError(getDuplicateError(
|
|
396
|
+
throwError(getDuplicateError(name));
|
|
453
397
|
}
|
|
454
|
-
result.set(
|
|
398
|
+
result.set(name, found);
|
|
455
399
|
resovedIds.add(found.id);
|
|
456
400
|
} else {
|
|
457
401
|
const matched = discoverColumns({
|
|
@@ -461,20 +405,25 @@ function resolveAnchorMap(
|
|
|
461
405
|
maxHops: 0,
|
|
462
406
|
constraints: matchingModeToConstraints("exact"),
|
|
463
407
|
});
|
|
408
|
+
|
|
464
409
|
if (matched.hits.length === 0) {
|
|
465
|
-
throwError(`Anchor "${
|
|
410
|
+
throwError(`Anchor "${name}": no columns matched selector`);
|
|
466
411
|
}
|
|
467
412
|
if (matched.hits.length > 1) {
|
|
468
413
|
throwError(
|
|
469
|
-
`Anchor "${
|
|
414
|
+
`Anchor "${name}": selector is ambiguous and matched multiple columns; please refine the selector to match exactly one column`,
|
|
470
415
|
);
|
|
471
416
|
}
|
|
472
417
|
if (resovedIds.has(matched.hits[0].hit.columnId as PObjectId)) {
|
|
473
|
-
throwError(getDuplicateError(
|
|
418
|
+
throwError(getDuplicateError(name));
|
|
474
419
|
}
|
|
475
420
|
|
|
476
|
-
|
|
477
|
-
|
|
421
|
+
const id = matched.hits[0].hit.columnId as PObjectId;
|
|
422
|
+
const snap =
|
|
423
|
+
columns.find((col) => col.id === id) ??
|
|
424
|
+
throwError(`Anchor "${name}": matched column with id "${id}" not found in sources`);
|
|
425
|
+
result.set(name, snap);
|
|
426
|
+
resovedIds.add(snap.id);
|
|
478
427
|
}
|
|
479
428
|
}
|
|
480
429
|
|
|
@@ -485,6 +434,20 @@ function resolveAnchorMap(
|
|
|
485
434
|
return result;
|
|
486
435
|
}
|
|
487
436
|
|
|
437
|
+
function remapFromIdxToId(
|
|
438
|
+
qualifications: {
|
|
439
|
+
forQueries: AxisQualification[][];
|
|
440
|
+
forHit: AxisQualification[];
|
|
441
|
+
},
|
|
442
|
+
anchors: ColumnSnapshot<PObjectId>[],
|
|
443
|
+
): MatchQualifications {
|
|
444
|
+
const forQueries = qualifications.forQueries.reduce<Record<PObjectId, AxisQualification[]>>(
|
|
445
|
+
(acc, qs, i) => (anchors[i] ? ((acc[anchors[i].id] = qs), acc) : acc),
|
|
446
|
+
{},
|
|
447
|
+
);
|
|
448
|
+
return { forQueries, forHit: qualifications.forHit };
|
|
449
|
+
}
|
|
450
|
+
|
|
488
451
|
// --- MatchingMode → DiscoverColumnsConstraints ---
|
|
489
452
|
|
|
490
453
|
function matchingModeToConstraints(mode: MatchingMode): DiscoverColumnsConstraints {
|
|
@@ -36,7 +36,7 @@ export interface RelaxedColumnSelector {
|
|
|
36
36
|
partialAxesMatch?: boolean;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
39
|
+
/** One or many relaxed column selectors; normalizes to MultiColumnSelector[]. */
|
|
40
40
|
export type ColumnSelector = RelaxedColumnSelector | RelaxedColumnSelector[];
|
|
41
41
|
|
|
42
42
|
// --- Normalization ---
|
|
@@ -13,7 +13,10 @@ export type ColumnDataStatus = "ready" | "computing" | "absent";
|
|
|
13
13
|
* - `data` holds an active object when data exists (ready or computing),
|
|
14
14
|
* or `undefined` when data is permanently absent.
|
|
15
15
|
*/
|
|
16
|
-
export interface ColumnSnapshot<
|
|
16
|
+
export interface ColumnSnapshot<
|
|
17
|
+
Id extends PObjectId | SUniversalPColumnId,
|
|
18
|
+
Data = PColumnDataUniversal,
|
|
19
|
+
> {
|
|
17
20
|
readonly id: Id;
|
|
18
21
|
readonly spec: PColumnSpec;
|
|
19
22
|
readonly dataStatus: ColumnDataStatus;
|
|
@@ -24,7 +27,7 @@ export interface ColumnSnapshot<Id extends PObjectId | SUniversalPColumnId> {
|
|
|
24
27
|
* - `'computing'`: `data.get()` returns `undefined`, marks context unstable.
|
|
25
28
|
* - `'absent'`: `data` is `undefined` — no active object, no instability.
|
|
26
29
|
*/
|
|
27
|
-
readonly data: ColumnData | undefined;
|
|
30
|
+
readonly data: ColumnData<Data> | undefined;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
// --- ColumnData ---
|
|
@@ -33,8 +36,8 @@ export interface ColumnSnapshot<Id extends PObjectId | SUniversalPColumnId> {
|
|
|
33
36
|
* Active object wrapping lazy column data access.
|
|
34
37
|
* Accessing data on a computing column marks the render context unstable.
|
|
35
38
|
*/
|
|
36
|
-
export interface ColumnData {
|
|
37
|
-
get():
|
|
39
|
+
export interface ColumnData<Data = PColumnDataUniversal> {
|
|
40
|
+
get(): Data | undefined;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/** Creates a ColumnData active object for a ready column. */
|
|
@@ -17,9 +17,7 @@ import type { ValueOf } from "@milaboratories/helpers";
|
|
|
17
17
|
*
|
|
18
18
|
* Returns an array of providers suitable for `ColumnCollectionBuilder.addSource()`.
|
|
19
19
|
*/
|
|
20
|
-
export function collectCtxColumnSnapshotProviders
|
|
21
|
-
ctx: RenderCtxBase<A, U, S>,
|
|
22
|
-
): ColumnSnapshotProvider[] {
|
|
20
|
+
export function collectCtxColumnSnapshotProviders(ctx: RenderCtxBase): ColumnSnapshotProvider[] {
|
|
23
21
|
const providers: ColumnSnapshotProvider[] = [];
|
|
24
22
|
|
|
25
23
|
// ResultPool — all upstream columns
|
|
@@ -214,8 +214,8 @@ describe("PFrameForGraph", () => {
|
|
|
214
214
|
valueType: "String",
|
|
215
215
|
annotations: { [Annotation.IsLinkerColumn]: "true" },
|
|
216
216
|
axesSpec: [
|
|
217
|
-
{ type: "String", name: "axis1" },
|
|
218
217
|
{ type: "String", name: "axis3" },
|
|
218
|
+
{ type: "String", name: "axis1" },
|
|
219
219
|
],
|
|
220
220
|
};
|
|
221
221
|
|
|
@@ -240,8 +240,8 @@ describe("PFrameForGraph", () => {
|
|
|
240
240
|
valueType: "String",
|
|
241
241
|
annotations: { [Annotation.IsLinkerColumn]: "true" },
|
|
242
242
|
axesSpec: [
|
|
243
|
-
{ type: "String", name: "axis1" },
|
|
244
243
|
{ type: "String", name: "axis2" },
|
|
244
|
+
{ type: "String", name: "axis1" },
|
|
245
245
|
],
|
|
246
246
|
};
|
|
247
247
|
const linkerColumn23: PColumnSpec = {
|
|
@@ -250,8 +250,8 @@ describe("PFrameForGraph", () => {
|
|
|
250
250
|
valueType: "String",
|
|
251
251
|
annotations: { [Annotation.IsLinkerColumn]: "true" },
|
|
252
252
|
axesSpec: [
|
|
253
|
-
{ type: "String", name: "axis2" },
|
|
254
253
|
{ type: "String", name: "axis3" },
|
|
254
|
+
{ type: "String", name: "axis2" },
|
|
255
255
|
],
|
|
256
256
|
};
|
|
257
257
|
const linkerColumn34: PColumnSpec = {
|
|
@@ -260,8 +260,8 @@ describe("PFrameForGraph", () => {
|
|
|
260
260
|
valueType: "String",
|
|
261
261
|
annotations: { [Annotation.IsLinkerColumn]: "true" },
|
|
262
262
|
axesSpec: [
|
|
263
|
-
{ type: "String", name: "axis3" },
|
|
264
263
|
{ type: "String", name: "axis4" },
|
|
264
|
+
{ type: "String", name: "axis3" },
|
|
265
265
|
],
|
|
266
266
|
};
|
|
267
267
|
|
|
@@ -33,8 +33,8 @@ export function createPTableDefV2(params: {
|
|
|
33
33
|
secondaryColumns.push(...params.labelColumns);
|
|
34
34
|
|
|
35
35
|
return createPTableDefV3({
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
primary: coreColumns.map((column) => ({ column })),
|
|
37
|
+
secondary: secondaryColumns.map((column) => ({ entries: [{ column }] })),
|
|
38
38
|
primaryJoinType: params.coreJoinType,
|
|
39
39
|
filters: params.filters,
|
|
40
40
|
sorting: params.sorting,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
AxisQualification,
|
|
2
3
|
PColumn,
|
|
3
4
|
PTableColumnId,
|
|
4
5
|
PTableSorting,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
SpecQuery,
|
|
8
9
|
SpecQueryExpression,
|
|
9
10
|
SpecQueryJoinEntry,
|
|
11
|
+
PObjectId,
|
|
10
12
|
} from "@milaboratories/pl-model-common";
|
|
11
13
|
import { isBooleanExpression } from "@milaboratories/pl-model-common";
|
|
12
14
|
import type { PColumnDataUniversal } from "../../../render";
|
|
@@ -15,31 +17,51 @@ import type { PlDataTableFilters } from "../typesV5";
|
|
|
15
17
|
import { distillFilterSpec, filterSpecToSpecQueryExpr } from "../../../filters";
|
|
16
18
|
import type { Nil } from "@milaboratories/helpers";
|
|
17
19
|
|
|
20
|
+
/** Primary side — base row grid. */
|
|
21
|
+
export type PrimaryEntry<Data> = {
|
|
22
|
+
column: PColumn<Data>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Secondary side leaf — the hit column, a linker step, or a label column. */
|
|
26
|
+
export type SecondaryEntry<Data> = {
|
|
27
|
+
column: PColumn<Data>;
|
|
28
|
+
/** For hit: `forHit`. For linker step k: `path[k].qualifications`. For label/direct: omit. */
|
|
29
|
+
qualifications?: AxisQualification[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Secondary group — one join subtree outer-joined onto primary. */
|
|
33
|
+
export type SecondaryGroup<Data> = {
|
|
34
|
+
entries: SecondaryEntry<Data>[];
|
|
35
|
+
/** Per-variant qualifications applied to the cloned primary anchors on this group's side.
|
|
36
|
+
* Keyed by `PrimaryEntry.column.id`. Omit → base primary used unqualified (labels, non-variant columns). */
|
|
37
|
+
primaryQualifications?: Record<PObjectId, AxisQualification[]>;
|
|
38
|
+
};
|
|
39
|
+
|
|
18
40
|
export function createPTableDefV3<Data = PColumnDataUniversal>(params: {
|
|
19
41
|
primaryJoinType: "inner" | "full";
|
|
20
|
-
|
|
21
|
-
|
|
42
|
+
primary: PrimaryEntry<Data>[];
|
|
43
|
+
secondary: SecondaryGroup<Data>[];
|
|
22
44
|
filters?: Nil | PlDataTableFilters;
|
|
23
45
|
sorting?: Nil | PTableSorting[];
|
|
24
46
|
}): PTableDefV2<PColumn<Data>> {
|
|
25
|
-
|
|
26
|
-
const coreJoinQuery: SpecQuery<PColumn<Data>> = {
|
|
47
|
+
let query: SpecQuery<PColumn<Data>> = {
|
|
27
48
|
type: params.primaryJoinType === "inner" ? "innerJoin" : "fullJoin",
|
|
28
|
-
entries: params.
|
|
49
|
+
entries: params.primary.map((a) => toLeaf(a.column, [])),
|
|
29
50
|
};
|
|
30
51
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
for (const group of params.secondary) {
|
|
53
|
+
query = {
|
|
54
|
+
type: "outerJoin",
|
|
55
|
+
primary: {
|
|
56
|
+
entry: query,
|
|
57
|
+
qualifications: params.primary.flatMap((p) => {
|
|
58
|
+
return group.primaryQualifications?.[p.column.id] ?? [];
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
secondary: group.entries.map((e) => toLeaf(e.column, e.qualifications ?? [])),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
41
64
|
|
|
42
|
-
// Apply filters
|
|
43
65
|
if (!isNil(params.filters)) {
|
|
44
66
|
const nonEmpty = distillFilterSpec(params.filters);
|
|
45
67
|
|
|
@@ -58,7 +80,6 @@ export function createPTableDefV3<Data = PColumnDataUniversal>(params: {
|
|
|
58
80
|
}
|
|
59
81
|
}
|
|
60
82
|
|
|
61
|
-
// Apply sorting
|
|
62
83
|
if (!isNil(params.sorting) && params.sorting.length > 0) {
|
|
63
84
|
query = {
|
|
64
85
|
type: "sort",
|
|
@@ -74,16 +95,18 @@ export function createPTableDefV3<Data = PColumnDataUniversal>(params: {
|
|
|
74
95
|
return { query };
|
|
75
96
|
}
|
|
76
97
|
|
|
77
|
-
/** Convert a PTableColumnId to a SpecQueryExpression reference. */
|
|
78
98
|
function columnIdToExpr(col: PTableColumnId): SpecQueryExpression {
|
|
79
99
|
return col.type === "axis"
|
|
80
100
|
? { type: "axisRef", value: col.id as SingleAxisSelector }
|
|
81
101
|
: { type: "columnRef", value: col.id };
|
|
82
102
|
}
|
|
83
103
|
|
|
84
|
-
function
|
|
104
|
+
function toLeaf<Data>(
|
|
105
|
+
col: PColumn<Data>,
|
|
106
|
+
qs: AxisQualification[],
|
|
107
|
+
): SpecQueryJoinEntry<PColumn<Data>> {
|
|
85
108
|
return {
|
|
86
|
-
entry:
|
|
87
|
-
qualifications:
|
|
109
|
+
entry: { type: "column", column: col },
|
|
110
|
+
qualifications: qs,
|
|
88
111
|
};
|
|
89
112
|
}
|