@milaboratories/pl-model-common 1.11.4 → 1.13.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.
Files changed (46) hide show
  1. package/dist/drivers/pframe/data_info.d.ts +169 -0
  2. package/dist/drivers/pframe/data_info.d.ts.map +1 -0
  3. package/dist/drivers/pframe/{data.d.ts → data_types.d.ts} +1 -7
  4. package/dist/drivers/pframe/data_types.d.ts.map +1 -0
  5. package/dist/drivers/pframe/driver.d.ts +1 -1
  6. package/dist/drivers/pframe/driver.d.ts.map +1 -1
  7. package/dist/drivers/pframe/index.d.ts +2 -1
  8. package/dist/drivers/pframe/index.d.ts.map +1 -1
  9. package/dist/drivers/pframe/spec/{anchored_id.d.ts → anchored.d.ts} +20 -13
  10. package/dist/drivers/pframe/spec/anchored.d.ts.map +1 -0
  11. package/dist/drivers/pframe/spec/filtered_column.d.ts +31 -0
  12. package/dist/drivers/pframe/spec/filtered_column.d.ts.map +1 -0
  13. package/dist/drivers/pframe/spec/ids.d.ts +24 -0
  14. package/dist/drivers/pframe/spec/ids.d.ts.map +1 -0
  15. package/dist/drivers/pframe/spec/index.d.ts +3 -1
  16. package/dist/drivers/pframe/spec/index.d.ts.map +1 -1
  17. package/dist/drivers/pframe/spec/selectors.d.ts +3 -3
  18. package/dist/drivers/pframe/spec/selectors.d.ts.map +1 -1
  19. package/dist/drivers/pframe/spec/spec.d.ts +2 -0
  20. package/dist/drivers/pframe/spec/spec.d.ts.map +1 -1
  21. package/dist/drivers/pframe/table.d.ts +1 -1
  22. package/dist/drivers/pframe/table.d.ts.map +1 -1
  23. package/dist/drivers/pframe/table_calculate.d.ts +1 -1
  24. package/dist/drivers/pframe/table_calculate.d.ts.map +1 -1
  25. package/dist/drivers/pframe/unique_values.d.ts +1 -1
  26. package/dist/drivers/pframe/unique_values.d.ts.map +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +383 -191
  30. package/dist/index.mjs.map +1 -1
  31. package/package.json +3 -3
  32. package/src/drivers/pframe/data_info.ts +430 -0
  33. package/src/drivers/pframe/{data.ts → data_types.ts} +0 -7
  34. package/src/drivers/pframe/driver.ts +1 -1
  35. package/src/drivers/pframe/index.ts +2 -1
  36. package/src/drivers/pframe/spec/{anchored_id.ts → anchored.ts} +63 -21
  37. package/src/drivers/pframe/spec/filtered_column.ts +39 -0
  38. package/src/drivers/pframe/spec/ids.ts +31 -0
  39. package/src/drivers/pframe/spec/index.ts +3 -1
  40. package/src/drivers/pframe/spec/selectors.ts +4 -3
  41. package/src/drivers/pframe/spec/spec.ts +6 -0
  42. package/src/drivers/pframe/table.ts +1 -1
  43. package/src/drivers/pframe/table_calculate.ts +1 -1
  44. package/src/drivers/pframe/unique_values.ts +1 -1
  45. package/dist/drivers/pframe/data.d.ts.map +0 -1
  46. package/dist/drivers/pframe/spec/anchored_id.d.ts.map +0 -1
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Represents a JavaScript representation of a value in a PColumn. Can be null, a number, or a string.
3
+ * These are the primitive types that can be stored directly in PColumns.
4
+ *
5
+ * Note: Actual columns can hold more value types, which are converted to these JavaScript types
6
+ * once they enter the JavaScript runtime.
7
+ */
8
+ export type PColumnValue = null | number | string;
9
+
10
+ /**
11
+ * Represents a key for a PColumn value.
12
+ * Can be an array of strings or numbers.
13
+ */
14
+ export type PColumnKey = (number | string)[];
15
+
16
+ /**
17
+ * Represents a single entry in a PColumn's data structure.
18
+ * Contains a key and a value.
19
+ */
20
+ export type PColumnDataEntry<T> = {
21
+ /** Key for the value */
22
+ key: PColumnKey;
23
+
24
+ /** Value / blob at the given key */
25
+ value: T;
26
+ };
27
+
28
+ /**
29
+ * Represents column data stored as a simple JSON structure.
30
+ * Used for small datasets that can be efficiently stored directly in memory.
31
+ */
32
+ export type JsonDataInfo = {
33
+ /** Identifier for this data format ('Json') */
34
+ type: 'Json';
35
+
36
+ /** Number of axes that make up the complete key (tuple length) */
37
+ keyLength: number;
38
+
39
+ /**
40
+ * Key-value pairs where keys are stringified tuples of axis values
41
+ * and values are the column values for those coordinates
42
+ */
43
+ data: Record<string, PColumnValue>;
44
+ };
45
+
46
+ /**
47
+ * Represents column data partitioned across multiple JSON blobs.
48
+ * Used for larger datasets that need to be split into manageable chunks.
49
+ */
50
+ export type JsonPartitionedDataInfo<Blob> = {
51
+ /** Identifier for this data format ('JsonPartitioned') */
52
+ type: 'JsonPartitioned';
53
+
54
+ /** Number of leading axes used for partitioning */
55
+ partitionKeyLength: number;
56
+
57
+ /** Map of stringified partition keys to blob references */
58
+ parts: Record<string, Blob>;
59
+ };
60
+
61
+ /**
62
+ * Represents a binary format chunk containing index and values as separate blobs.
63
+ * Used for efficient storage and retrieval of column data in binary format.
64
+ */
65
+ export type BinaryChunk<Blob> = {
66
+ /** Binary blob containing structured index information */
67
+ index: Blob;
68
+
69
+ /** Binary blob containing the actual values */
70
+ values: Blob;
71
+ };
72
+
73
+ /**
74
+ * Represents column data partitioned across multiple binary chunks.
75
+ * Optimized for efficient storage and retrieval of large datasets.
76
+ */
77
+ export type BinaryPartitionedDataInfo<Blob> = {
78
+ /** Identifier for this data format ('BinaryPartitioned') */
79
+ type: 'BinaryPartitioned';
80
+
81
+ /** Number of leading axes used for partitioning */
82
+ partitionKeyLength: number;
83
+
84
+ /** Map of stringified partition keys to binary chunks */
85
+ parts: Record<string, BinaryChunk<Blob>>;
86
+ };
87
+
88
+ /**
89
+ * Union type representing all possible data storage formats for PColumn data.
90
+ * The specific format used depends on data size, access patterns, and performance requirements.
91
+ *
92
+ * @template Blob - Type parameter representing the storage reference type (could be ResourceInfo, PFrameBlobId, etc.)
93
+ */
94
+ export type DataInfo<Blob> =
95
+ | JsonDataInfo
96
+ | JsonPartitionedDataInfo<Blob>
97
+ | BinaryPartitionedDataInfo<Blob>;
98
+
99
+ /**
100
+ * Type guard function that checks if the given value is a valid DataInfo.
101
+ *
102
+ * @param value - The value to check
103
+ * @returns True if the value is a valid DataInfo, false otherwise
104
+ */
105
+ export function isDataInfo<Blob>(value: unknown): value is DataInfo<Blob> {
106
+ if (!value || typeof value !== 'object') {
107
+ return false;
108
+ }
109
+
110
+ const data = value as Record<string, unknown>;
111
+ if (!('type' in data)) {
112
+ return false;
113
+ }
114
+
115
+ switch (data.type) {
116
+ case 'Json':
117
+ return (
118
+ typeof data.keyLength === 'number'
119
+ && data.data !== undefined
120
+ && typeof data.data === 'object'
121
+ );
122
+ case 'JsonPartitioned':
123
+ return (
124
+ typeof data.partitionKeyLength === 'number'
125
+ && data.parts !== undefined
126
+ && typeof data.parts === 'object'
127
+ );
128
+ case 'BinaryPartitioned':
129
+ return (
130
+ typeof data.partitionKeyLength === 'number'
131
+ && data.parts !== undefined
132
+ && typeof data.parts === 'object'
133
+ );
134
+ default:
135
+ return false;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Maps blob references in a DataInfo object from one type to another using a mapping function.
141
+ *
142
+ * @template B1 - Source blob type
143
+ * @template B2 - Target blob type
144
+ * @param dataInfo - The source DataInfo object
145
+ * @param mapFn - Function to transform blobs from type B1 to type B2
146
+ * @returns A new DataInfo object with transformed blob references
147
+ */
148
+ export function mapDataInfo<B1, B2>(
149
+ dataInfo: DataInfo<B1>,
150
+ mapFn: (blob: B1) => B2,
151
+ ): DataInfo<B2>;
152
+ export function mapDataInfo<B1, B2>(
153
+ dataInfo: DataInfo<B1> | undefined,
154
+ mapFn: (blob: B1) => B2,
155
+ ): DataInfo<B2> | undefined {
156
+ if (dataInfo === undefined) {
157
+ return undefined;
158
+ }
159
+
160
+ switch (dataInfo.type) {
161
+ case 'Json':
162
+ // Json type doesn't contain blobs, so return as is
163
+ return dataInfo;
164
+ case 'JsonPartitioned': {
165
+ // Map each blob in parts
166
+ const newParts: Record<string, B2> = {};
167
+ for (const [key, blob] of Object.entries(dataInfo.parts)) {
168
+ newParts[key] = mapFn(blob);
169
+ }
170
+ return {
171
+ ...dataInfo,
172
+ parts: newParts,
173
+ };
174
+ }
175
+ case 'BinaryPartitioned': {
176
+ // Map each index and values blob in parts
177
+ const newParts: Record<string, BinaryChunk<B2>> = {};
178
+ for (const [key, chunk] of Object.entries(dataInfo.parts)) {
179
+ newParts[key] = {
180
+ index: mapFn(chunk.index),
181
+ values: mapFn(chunk.values),
182
+ };
183
+ }
184
+ return {
185
+ ...dataInfo,
186
+ parts: newParts,
187
+ };
188
+ }
189
+ }
190
+ }
191
+
192
+ //
193
+ // Lightway representation for ExplicitJsonData
194
+ //
195
+
196
+ /**
197
+ * Represents a single key-value entry in a column's explicit data structure.
198
+ * Used when directly instantiating PColumns with explicit data.
199
+ */
200
+ export type PColumnValuesEntry = {
201
+ key: PColumnKey;
202
+ val: PColumnValue;
203
+ };
204
+
205
+ /**
206
+ * Array of key-value entries representing explicit column data.
207
+ * Used for lightweight explicit instantiation of PColumns.
208
+ */
209
+ export type PColumnValues = PColumnValuesEntry[];
210
+
211
+ /**
212
+ * Entry-based representation of JsonDataInfo
213
+ */
214
+ export interface JsonDataInfoEntries {
215
+ type: 'Json';
216
+ keyLength: number;
217
+ data: PColumnDataEntry<PColumnValue>[];
218
+ }
219
+
220
+ /**
221
+ * Entry-based representation of JsonPartitionedDataInfo
222
+ */
223
+ export interface JsonPartitionedDataInfoEntries<Blob> {
224
+ type: 'JsonPartitioned';
225
+ partitionKeyLength: number;
226
+ parts: PColumnDataEntry<Blob>[];
227
+ }
228
+
229
+ /**
230
+ * Entry-based representation of BinaryPartitionedDataInfo
231
+ */
232
+ export interface BinaryPartitionedDataInfoEntries<Blob> {
233
+ type: 'BinaryPartitioned';
234
+ partitionKeyLength: number;
235
+ parts: PColumnDataEntry<BinaryChunk<Blob>>[];
236
+ }
237
+
238
+ /**
239
+ * Union type representing all possible entry-based data storage formats
240
+ */
241
+ export type DataInfoEntries<Blob> =
242
+ | JsonDataInfoEntries
243
+ | JsonPartitionedDataInfoEntries<Blob>
244
+ | BinaryPartitionedDataInfoEntries<Blob>;
245
+
246
+ /**
247
+ * Type guard function that checks if the given value is a valid DataInfoEntries.
248
+ *
249
+ * @param value - The value to check
250
+ * @returns True if the value is a valid DataInfoEntries, false otherwise
251
+ */
252
+ export function isDataInfoEntries<Blob>(value: unknown): value is DataInfoEntries<Blob> {
253
+ if (!value || typeof value !== 'object') {
254
+ return false;
255
+ }
256
+
257
+ const data = value as Record<string, unknown>;
258
+ if (!('type' in data)) {
259
+ return false;
260
+ }
261
+
262
+ switch (data.type) {
263
+ case 'Json':
264
+ return (
265
+ typeof data.keyLength === 'number'
266
+ && Array.isArray(data.data)
267
+ );
268
+ case 'JsonPartitioned':
269
+ return (
270
+ typeof data.partitionKeyLength === 'number'
271
+ && Array.isArray(data.parts)
272
+ );
273
+ case 'BinaryPartitioned':
274
+ return (
275
+ typeof data.partitionKeyLength === 'number'
276
+ && Array.isArray(data.parts)
277
+ );
278
+ default:
279
+ return false;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Converts DataInfo to DataInfoEntries
285
+ *
286
+ * @param dataInfo - The record-based DataInfo object
287
+ * @returns The equivalent entry-based DataInfoEntries object
288
+ */
289
+ export function dataInfoToEntries<Blob>(dataInfo: DataInfo<Blob>): DataInfoEntries<Blob> {
290
+ switch (dataInfo.type) {
291
+ case 'Json': {
292
+ const entries: PColumnDataEntry<PColumnValue>[] = Object.entries(dataInfo.data).map(([keyStr, value]) => {
293
+ const key = JSON.parse(keyStr) as PColumnKey;
294
+ return { key, value };
295
+ });
296
+
297
+ return {
298
+ type: 'Json',
299
+ keyLength: dataInfo.keyLength,
300
+ data: entries,
301
+ };
302
+ }
303
+ case 'JsonPartitioned': {
304
+ const parts: PColumnDataEntry<Blob>[] = Object.entries(dataInfo.parts).map(([keyStr, blob]) => {
305
+ const key = JSON.parse(keyStr) as PColumnKey;
306
+ return { key, value: blob };
307
+ });
308
+
309
+ return {
310
+ type: 'JsonPartitioned',
311
+ partitionKeyLength: dataInfo.partitionKeyLength,
312
+ parts,
313
+ };
314
+ }
315
+ case 'BinaryPartitioned': {
316
+ const parts: PColumnDataEntry<BinaryChunk<Blob>>[] = Object.entries(dataInfo.parts).map(([keyStr, chunk]) => {
317
+ const key = JSON.parse(keyStr) as PColumnKey;
318
+ return { key, value: chunk };
319
+ });
320
+
321
+ return {
322
+ type: 'BinaryPartitioned',
323
+ partitionKeyLength: dataInfo.partitionKeyLength,
324
+ parts,
325
+ };
326
+ }
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Converts DataInfoEntries to DataInfo
332
+ *
333
+ * @param dataInfoEntries - The entry-based DataInfoEntries object
334
+ * @returns The equivalent record-based DataInfo object
335
+ */
336
+ export function entriesToDataInfo<Blob>(dataInfoEntries: DataInfoEntries<Blob>): DataInfo<Blob> {
337
+ switch (dataInfoEntries.type) {
338
+ case 'Json': {
339
+ const data: Record<string, PColumnValue> = {};
340
+ for (const entry of dataInfoEntries.data) {
341
+ data[JSON.stringify(entry.key)] = entry.value;
342
+ }
343
+
344
+ return {
345
+ type: 'Json',
346
+ keyLength: dataInfoEntries.keyLength,
347
+ data,
348
+ };
349
+ }
350
+ case 'JsonPartitioned': {
351
+ const parts: Record<string, Blob> = {};
352
+ for (const entry of dataInfoEntries.parts) {
353
+ parts[JSON.stringify(entry.key)] = entry.value;
354
+ }
355
+
356
+ return {
357
+ type: 'JsonPartitioned',
358
+ partitionKeyLength: dataInfoEntries.partitionKeyLength,
359
+ parts,
360
+ };
361
+ }
362
+ case 'BinaryPartitioned': {
363
+ const parts: Record<string, BinaryChunk<Blob>> = {};
364
+ for (const entry of dataInfoEntries.parts) {
365
+ parts[JSON.stringify(entry.key)] = entry.value;
366
+ }
367
+
368
+ return {
369
+ type: 'BinaryPartitioned',
370
+ partitionKeyLength: dataInfoEntries.partitionKeyLength,
371
+ parts,
372
+ };
373
+ }
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Maps blob references in a DataInfoEntries object from one type to another using a mapping function.
379
+ *
380
+ * @template B1 - Source blob type
381
+ * @template B2 - Target blob type
382
+ * @param dataInfoEntries - The source DataInfoEntries object
383
+ * @param mapFn - Function to transform blobs from type B1 to type B2
384
+ * @returns A new DataInfoEntries object with transformed blob references
385
+ */
386
+ export function mapDataInfoEntries<B1, B2>(
387
+ dataInfoEntries: DataInfoEntries<B1>,
388
+ mapFn: (blob: B1) => B2,
389
+ ): DataInfoEntries<B2>;
390
+ export function mapDataInfoEntries<B1, B2>(
391
+ dataInfoEntries: DataInfoEntries<B1> | undefined,
392
+ mapFn: (blob: B1) => B2,
393
+ ): DataInfoEntries<B2> | undefined {
394
+ if (dataInfoEntries === undefined) {
395
+ return undefined;
396
+ }
397
+
398
+ switch (dataInfoEntries.type) {
399
+ case 'Json':
400
+ // Json type doesn't contain blobs, so return as is
401
+ return dataInfoEntries;
402
+ case 'JsonPartitioned': {
403
+ // Map each blob in parts
404
+ const newParts = dataInfoEntries.parts.map((entry) => ({
405
+ key: entry.key,
406
+ value: mapFn(entry.value),
407
+ }));
408
+
409
+ return {
410
+ ...dataInfoEntries,
411
+ parts: newParts,
412
+ };
413
+ }
414
+ case 'BinaryPartitioned': {
415
+ // Map each index and values blob in parts
416
+ const newParts = dataInfoEntries.parts.map((entry) => ({
417
+ key: entry.key,
418
+ value: {
419
+ index: mapFn(entry.value.index),
420
+ values: mapFn(entry.value.values),
421
+ },
422
+ }));
423
+
424
+ return {
425
+ ...dataInfoEntries,
426
+ parts: newParts,
427
+ };
428
+ }
429
+ }
430
+ }
@@ -242,13 +242,6 @@ export function isValueAbsent(absent: Uint8Array, index: number): boolean {
242
242
  return (absent[chunkIndex] & mask) > 0;
243
243
  }
244
244
 
245
- export type PColumnValue = null | number | string;
246
- export type PColumnValuesEntry = {
247
- key: PColumnValue[];
248
- val: PColumnValue;
249
- };
250
- export type PColumnValues = PColumnValuesEntry[];
251
-
252
245
  export const PTableAbsent = { type: 'absent' };
253
246
  export type PTableAbsent = typeof PTableAbsent;
254
247
  export const PTableNA = null;
@@ -2,7 +2,7 @@ import type { Branded } from '../../branding';
2
2
  import type { PTable } from './table';
3
3
  import type { PFrame } from './pframe';
4
4
  import type { AddParameterToAllMethods } from './type_util';
5
- import type { PTableShape, PTableVector, TableRange } from './data';
5
+ import type { PTableShape, PTableVector, TableRange } from './data_types';
6
6
  import type { FindColumnsRequest, FindColumnsResponse } from './find_columns';
7
7
  import type { PObjectId } from '../../pool';
8
8
  import type { PColumnIdAndSpec, PColumnSpec } from './spec/spec';
@@ -1,5 +1,6 @@
1
1
  export * from './column_filter';
2
- export * from './data';
2
+ export * from './data_info';
3
+ export * from './data_types';
3
4
  export * from './find_columns';
4
5
  export * from './pframe';
5
6
  export * from './spec/spec';
@@ -1,8 +1,11 @@
1
1
  import canonicalize from 'canonicalize';
2
2
  import type { AxisId, PColumnSpec } from './spec';
3
3
  import { getAxisId, matchAxisId } from './spec';
4
- import type { AAxisSelector, AnchorAxisRef, AnchorAxisRefByIdx, APColumnId, APColumnSelector, AxisSelector, PColumnSelector } from './selectors';
5
- import type { Branded } from '../../../branding';
4
+ import type { AAxisSelector, AnchorAxisRef, AnchorAxisRefByIdx, AnchoredPColumnId, AnchoredPColumnSelector, AxisSelector, PColumnSelector } from './selectors';
5
+ import type { AxisFilter } from './filtered_column';
6
+ import type { PValue } from '../data_types';
7
+ import type { SUniversalPColumnId, UniversalPColumnId } from './ids';
8
+ import { stringifyColumnId } from './ids';
6
9
 
7
10
  //
8
11
  // Helper functions
@@ -16,18 +19,11 @@ function domainKey(key: string, value: string): string {
16
19
  return JSON.stringify([key, value]);
17
20
  }
18
21
 
19
- //
20
- // Branded types
21
- //
22
-
23
- /** Canonicalized string representation of an anchored column identifier, either anchored or absolute. */
24
- export type CanonicalPColumnId = Branded<string, 'CanonicalPColumnId'>;
25
-
26
22
  /**
27
23
  * Context for resolving and generating anchored references to columns and axes
28
24
  * Maintains maps of known domain values and axes that can be referenced by anchors
29
25
  */
30
- export class AnchorIdDeriver {
26
+ export class AnchoredIdDeriver {
31
27
  private readonly domains = new Map<string, string>();
32
28
  private readonly axes = new Map<string, AnchorAxisRefByIdx>();
33
29
  /**
@@ -70,12 +66,24 @@ export class AnchorIdDeriver {
70
66
 
71
67
  /**
72
68
  * Derives an anchored column identifier from a column specification
73
- * Replaces domain values and axes with anchored references when possible
74
69
  * @param spec Column specification to anchor
75
70
  * @returns An anchored column identifier that can be used to identify columns similar to the input specification
76
71
  */
77
- derive(spec: PColumnSpec): APColumnId {
78
- const result: APColumnId = {
72
+ derive(spec: PColumnSpec): AnchoredPColumnId;
73
+
74
+ /**
75
+ * Derives an anchored column identifier from a column specification
76
+ * @param spec Column specification to anchor
77
+ * @param axisFilters Axis filters to apply to the column
78
+ * @returns An anchored and sliced column identifier that can be used to identify columns similar to the input specification
79
+ */
80
+ derive(spec: PColumnSpec, axisFilters?: AxisFilter[]): UniversalPColumnId;
81
+
82
+ /**
83
+ * Implementation of derive method
84
+ */
85
+ derive(spec: PColumnSpec, axisFilters?: AxisFilter[]): UniversalPColumnId {
86
+ const result: AnchoredPColumnId = {
79
87
  name: spec.name,
80
88
  axes: [],
81
89
  };
@@ -116,28 +124,62 @@ export class AnchorIdDeriver {
116
124
  return anchorAxisRef ?? axis;
117
125
  });
118
126
 
119
- return result;
127
+ // If no axis filters are provided, return the anchored ID as is
128
+ if (!axisFilters || axisFilters.length === 0) {
129
+ return result;
130
+ }
131
+
132
+ // Process axis filters and create a sliced column ID
133
+ const resolvedFilters: [number, PValue][] = [];
134
+
135
+ for (const filter of axisFilters) {
136
+ const [axisIdOrIndex, value] = filter;
137
+
138
+ // If it's already a numeric index, validate it
139
+ if (typeof axisIdOrIndex === 'number') {
140
+ if (axisIdOrIndex < 0 || axisIdOrIndex >= spec.axesSpec.length) {
141
+ throw new Error(`Axis index ${axisIdOrIndex} is out of bounds (0-${spec.axesSpec.length - 1})`);
142
+ }
143
+ resolvedFilters.push([axisIdOrIndex, value]);
144
+ } else {
145
+ // If it's a string (axis name), resolve it to an index
146
+ const axisIndex = spec.axesSpec.findIndex((axis) => axis.name === axisIdOrIndex);
147
+ if (axisIndex === -1) {
148
+ throw new Error(`Axis with name "${axisIdOrIndex}" not found in the column specification`);
149
+ }
150
+ resolvedFilters.push([axisIndex, value]);
151
+ }
152
+ }
153
+
154
+ // Sort filters by axis index to ensure consistency
155
+ resolvedFilters.sort((a, b) => a[0] - b[0]);
156
+
157
+ return {
158
+ source: result,
159
+ axisFilters: resolvedFilters,
160
+ };
120
161
  }
121
162
 
122
163
  /**
123
164
  * Derives a canonicalized string representation of an anchored column identifier, can be used as a unique identifier for the column
124
165
  * @param spec Column specification to anchor
166
+ * @param axisFilters Optional axis filters to apply to the column
125
167
  * @returns A canonicalized string representation of the anchored column identifier
126
168
  */
127
- deriveCanonical(spec: PColumnSpec): CanonicalPColumnId {
128
- const aId = this.derive(spec);
129
- return canonicalize(aId)! as CanonicalPColumnId;
169
+ deriveS(spec: PColumnSpec, axisFilters?: AxisFilter[]): SUniversalPColumnId {
170
+ return stringifyColumnId(this.derive(spec, axisFilters));
130
171
  }
131
172
  }
132
173
 
133
174
  /**
134
- * Resolves anchored references in a column matcher to create a non-anchored matcher
175
+ * Resolves anchored references in a column matcher to create a non-anchored matcher.
176
+ * Doing an opposite operation to {@link AnchorIdDeriver.derive()}.
135
177
  *
136
- * @param anchors - Record of anchor column specifications indexed by anchor ID
137
- * @param matcher - An anchored column matcher containing references that need to be resolved
178
+ * @param anchors - Record of anchor column specifications indexed by anchor id
179
+ * @param matcher - An anchored column matcher (or id, which is subtype of it) containing references that need to be resolved
138
180
  * @returns A non-anchored column matcher with all references resolved to actual values
139
181
  */
140
- export function resolveAnchors(anchors: Record<string, PColumnSpec>, matcher: APColumnSelector): PColumnSelector {
182
+ export function resolveAnchors(anchors: Record<string, PColumnSpec>, matcher: AnchoredPColumnSelector): PColumnSelector {
141
183
  const result = { ...matcher };
142
184
 
143
185
  if (result.domainAnchor !== undefined) {
@@ -0,0 +1,39 @@
1
+ import type { PValue } from '../data_types';
2
+ import type { AnchoredPColumnId } from './selectors';
3
+
4
+ /** Axis filter by index */
5
+ export type AxisFilterByIdx = [number, PValue];
6
+
7
+ /** Axis filter by name */
8
+ export type AxisFilterByName = [string, PValue];
9
+
10
+ /** Axis filter */
11
+ export type AxisFilter = AxisFilterByIdx | AxisFilterByName;
12
+
13
+ /**
14
+ * Identifies a column derived from a CanonicalPColumnId by fixing values on certain axes,
15
+ * thus effectively reducing the dimensionality of the original column.
16
+ */
17
+ export type FilteredPColumn<CID = AnchoredPColumnId, AFI = AxisFilter> = {
18
+ /** The original column identifier */
19
+ source: CID;
20
+
21
+ /**
22
+ * List of fixed axes and their corresponding values.
23
+ * Each entry fixes one axis to a specific value, creating a slice of the multidimensional data.
24
+ * This effectively reduces the dimensionality by one for each fixed axis.
25
+ * Ordered by the axis index in canonical case.
26
+ */
27
+ axisFilters: AFI[];
28
+ };
29
+
30
+ export type FilteredPColumnId = FilteredPColumn<AnchoredPColumnId, AxisFilterByIdx>;
31
+
32
+ /**
33
+ * Checks if a given value is a FilteredPColumn
34
+ * @param id - The value to check
35
+ * @returns True if the value is a FilteredPColumn, false otherwise
36
+ */
37
+ export function isFilteredPColumn(id: unknown): id is FilteredPColumn {
38
+ return typeof id === 'object' && id !== null && 'source' in id && 'axisFilters' in id;
39
+ }
@@ -0,0 +1,31 @@
1
+ import type { Branded } from '../../../branding';
2
+ import type { AnchoredPColumnId } from './selectors';
3
+ import type { FilteredPColumnId } from './filtered_column';
4
+ import canonicalize from 'canonicalize';
5
+ /**
6
+ * Universal column identifier optionally anchored and optionally filtered.
7
+ */
8
+ export type UniversalPColumnId = AnchoredPColumnId | FilteredPColumnId;
9
+
10
+ /**
11
+ * Canonically serialized {@link UniversalPColumnId}.
12
+ */
13
+ export type SUniversalPColumnId = Branded<string, 'SUniversalPColumnId'>;
14
+
15
+ /**
16
+ * Canonically serializes a {@link UniversalPColumnId} to a string.
17
+ * @param id - The column identifier to serialize
18
+ * @returns The canonically serialized string
19
+ */
20
+ export function stringifyColumnId(id: UniversalPColumnId): SUniversalPColumnId {
21
+ return canonicalize(id)! as SUniversalPColumnId;
22
+ }
23
+
24
+ /**
25
+ * Parses a canonically serialized {@link UniversalPColumnId} from a string.
26
+ * @param str - The string to parse
27
+ * @returns The parsed column identifier
28
+ */
29
+ export function parseColumnId(str: SUniversalPColumnId): UniversalPColumnId {
30
+ return JSON.parse(str) as UniversalPColumnId;
31
+ }
@@ -1,3 +1,5 @@
1
- export * from './anchored_id';
1
+ export * from './anchored';
2
+ export * from './ids';
3
+ export * from './filtered_column';
2
4
  export * from './spec';
3
5
  export * from './selectors';