@milaboratories/pl-model-common 1.41.2 → 1.43.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 (33) hide show
  1. package/dist/drivers/index.d.ts +4 -4
  2. package/dist/drivers/ls.d.ts +1 -1
  3. package/dist/drivers/pframe/driver.d.ts +2 -2
  4. package/dist/drivers/pframe/index.d.ts +4 -4
  5. package/dist/drivers/pframe/pframe.d.ts +1 -1
  6. package/dist/drivers/pframe/query/index.d.ts +2 -2
  7. package/dist/drivers/pframe/query/query_common.d.ts +229 -22
  8. package/dist/drivers/pframe/query/query_common.d.ts.map +1 -1
  9. package/dist/drivers/pframe/query/query_data.d.ts +9 -7
  10. package/dist/drivers/pframe/query/query_data.d.ts.map +1 -1
  11. package/dist/drivers/pframe/query/query_spec.d.ts +9 -7
  12. package/dist/drivers/pframe/query/query_spec.d.ts.map +1 -1
  13. package/dist/drivers/pframe/query/utils.cjs +12 -3
  14. package/dist/drivers/pframe/query/utils.cjs.map +1 -1
  15. package/dist/drivers/pframe/query/utils.d.ts.map +1 -1
  16. package/dist/drivers/pframe/query/utils.js +12 -3
  17. package/dist/drivers/pframe/query/utils.js.map +1 -1
  18. package/dist/drivers/pframe/spec_driver.d.ts +11 -1
  19. package/dist/drivers/pframe/spec_driver.d.ts.map +1 -1
  20. package/dist/drivers/pframe/table_calculate.d.ts +1 -1
  21. package/dist/index.d.ts +5 -5
  22. package/dist/services/service_declarations.cjs +4 -2
  23. package/dist/services/service_declarations.cjs.map +1 -1
  24. package/dist/services/service_declarations.js +4 -2
  25. package/dist/services/service_declarations.js.map +1 -1
  26. package/package.json +4 -4
  27. package/src/drivers/pframe/query/query_common.ts +256 -27
  28. package/src/drivers/pframe/query/query_data.ts +33 -12
  29. package/src/drivers/pframe/query/query_spec.ts +29 -11
  30. package/src/drivers/pframe/query/utils.test.ts +24 -8
  31. package/src/drivers/pframe/query/utils.ts +21 -3
  32. package/src/drivers/pframe/spec_driver.ts +12 -1
  33. package/src/services/service_declarations.ts +2 -0
@@ -61,8 +61,9 @@ export type NumericUnaryOperand =
61
61
  * - `mul` - Multiplication: left * right
62
62
  * - `div` - Division: left / right (division by zero returns Infinity or NaN)
63
63
  * - `mod` - Modulo: left % right
64
+ * - `power` - Exponentiation: left ** right
64
65
  */
65
- export type NumericBinaryOperand = "add" | "sub" | "mul" | "div" | "mod";
66
+ export type NumericBinaryOperand = "add" | "sub" | "mul" | "div" | "mod" | "power";
66
67
 
67
68
  /**
68
69
  * Numeric comparison operation kinds.
@@ -83,16 +84,16 @@ export type NumericComparisonOperand = "eq" | "ne" | "lt" | "le" | "gt" | "ge";
83
84
  // ============ Geometric Types ============
84
85
 
85
86
  /**
86
- * 2D point coordinates.
87
+ * 2D point coordinates as an `[x, y]` tuple.
88
+ *
89
+ * Used for geometric operations like point-in-polygon tests. The wire
90
+ * form is a tuple (matching Rust's `(f64, f64)` serialisation), not an
91
+ * object with `x` / `y` fields.
87
92
  *
88
- * Used for geometric operations like point-in-polygon tests.
89
93
  * Common use case: flow cytometry gating where points are tested
90
94
  * against user-defined polygon regions.
91
95
  */
92
- export type Point2D = {
93
- x: number;
94
- y: number;
95
- };
96
+ export type Point2D = [number, number];
96
97
 
97
98
  // ============ Constant Expression ============
98
99
 
@@ -148,17 +149,20 @@ export interface ExprIsNull<I> {
148
149
  * **Output**: Same type as input/replacement.
149
150
  * **Null handling**: If input is null, returns replacement; otherwise returns input.
150
151
  *
152
+ * The Rust runtime also accepts the legacy `"ifNull"` tag as a serde
153
+ * alias; new code should emit `"fillNull"`.
154
+ *
151
155
  * @template I - The expression type (for recursion)
152
156
  *
153
157
  * @example
154
158
  * // Replace null values with 0
155
- * { type: 'ifNull', input: columnRef, replacement: { type: 'constant', value: 0 } }
159
+ * { type: 'fillNull', input: columnRef, replacement: { type: 'constant', value: 0 } }
156
160
  *
157
161
  * // Replace null strings with 'unknown'
158
- * { type: 'ifNull', input: nameColumn, replacement: { type: 'constant', value: 'unknown' } }
162
+ * { type: 'fillNull', input: nameColumn, replacement: { type: 'constant', value: 'unknown' } }
159
163
  */
160
- export interface ExprIfNull<I> {
161
- type: "ifNull";
164
+ export interface ExprFillNull<I> {
165
+ type: "fillNull";
162
166
  /** Value to check for null */
163
167
  input: I;
164
168
  /** Replacement value if input is null */
@@ -475,8 +479,154 @@ export interface ExprIsIn<I, T extends string | number> {
475
479
  input: I;
476
480
  /** Set of allowed values */
477
481
  set: T[];
482
+ /** If true, the predicate is inverted (true for values NOT in `set`). */
483
+ negate: boolean;
478
484
  }
479
485
 
486
+ /**
487
+ * Type cast expression.
488
+ *
489
+ * Converts the input value to a different column value type. Mirrors
490
+ * SQL `CAST(input AS U)`.
491
+ *
492
+ * **Input**: any expression.
493
+ * **Output**: a value of `targetType`.
494
+ * **Null handling**: null in → null out.
495
+ *
496
+ * @template I - The expression type (for recursion)
497
+ */
498
+ export interface ExprCast<I> {
499
+ type: "cast";
500
+ /** Expression to cast. */
501
+ input: I;
502
+ /** Target column value type. */
503
+ targetType: ColumnValueType;
504
+ }
505
+
506
+ /**
507
+ * A single `when → then` case in a {@link ExprConditional} expression.
508
+ *
509
+ * @template I - The expression type (for recursion)
510
+ */
511
+ export interface ExprConditionalCase<I> {
512
+ /** Boolean predicate that selects this branch. */
513
+ when: I;
514
+ /** Value produced when `when` evaluates to true. */
515
+ then: I;
516
+ }
517
+
518
+ /**
519
+ * Conditional (CASE WHEN) expression.
520
+ *
521
+ * Evaluates `cases` in order; the value of the first case whose `when`
522
+ * is true is returned. If no case matches, `otherwise` is used (or
523
+ * null when omitted).
524
+ *
525
+ * **Result type**: the type of the first case's `then`; subsequent
526
+ * `then`s and `otherwise` are cast to it.
527
+ *
528
+ * @template I - The expression type (for recursion)
529
+ *
530
+ * @example
531
+ * {
532
+ * type: 'conditional',
533
+ * cases: [
534
+ * { when: gtTwenty, then: { type: 'constant', value: 'high' } },
535
+ * { when: gtTen, then: { type: 'constant', value: 'mid' } }
536
+ * ],
537
+ * otherwise: { type: 'constant', value: 'low' }
538
+ * }
539
+ */
540
+ export interface ExprConditional<I> {
541
+ type: "conditional";
542
+ /** Cases evaluated in order; first matching wins. At least one. */
543
+ cases: [ExprConditionalCase<I>, ...ExprConditionalCase<I>[]];
544
+ /** Fallback value when no case matches. */
545
+ otherwise?: I;
546
+ }
547
+
548
+ // ============ Disabled — type defined in pframes-rs, runtime not wired yet ============
549
+ //
550
+ // `isInPolygon` and `aggregation` are defined in the Rust query model
551
+ // (`packages/bridge/src/query/expressions/`) but the DataFusion backend
552
+ // returns errors when it tries to evaluate them (filter.rs:276 / :340).
553
+ // Until those stubs land, exposing the TS types here would let block
554
+ // authors construct queries the runtime cannot execute. Re-enable in
555
+ // lock-step with the Rust executor.
556
+
557
+ // export interface ExprIsInPolygon<I> {
558
+ // type: "isInPolygon";
559
+ // x: I;
560
+ // y: I;
561
+ // polygon: [Point2D, ...Point2D[]];
562
+ // negate: boolean;
563
+ // }
564
+
565
+ // export type AggregationKind =
566
+ // | "sum"
567
+ // | "avg"
568
+ // | "count"
569
+ // | "min"
570
+ // | "max"
571
+ // | "first"
572
+ // | "last"
573
+ // | "stddev"
574
+ // | "variance"
575
+ // | "median";
576
+
577
+ // export interface ExprAggregation<I, A, C> {
578
+ // type: "aggregation";
579
+ // kind: AggregationKind;
580
+ // input: I;
581
+ // over?: [QuerySelector<A, C>, ...QuerySelector<A, C>[]];
582
+ // }
583
+
584
+ /** Ranking function kind. */
585
+ export type RankingKind = "rank" | "denseRank" | "rowNumber";
586
+
587
+ /**
588
+ * Ranking expression (always a window function).
589
+ *
590
+ * Orders rows by `orderBy` within each partition and assigns ranks
591
+ * according to `kind`. Output is always `Long`.
592
+ *
593
+ * @template I - The expression type (for recursion)
594
+ * @template A - Axis selector type
595
+ * @template C - Column selector type
596
+ */
597
+ export interface ExprRanking<I, A, C> {
598
+ type: "ranking";
599
+ /** Ranking semantics. */
600
+ kind: RankingKind;
601
+ /** Expression to order by within each partition. */
602
+ orderBy: I;
603
+ /** If true (default), sort ascending; if false, descending. */
604
+ ascending?: boolean;
605
+ /** Partition specification — at least one entry when supplied. */
606
+ partitionBy?: [QuerySelector<A, C>, ...QuerySelector<A, C>[]];
607
+ }
608
+
609
+ // ============ Disabled — type defined in pframes-rs, runtime not wired yet ============
610
+ //
611
+ // `cumulative` is defined in the Rust query model but the DataFusion
612
+ // backend returns an error (filter.rs:387). Re-enable in lock-step
613
+ // with the Rust executor.
614
+
615
+ // export type CumulativeOperand =
616
+ // | "cumulativeSum"
617
+ // | "cumulativeMin"
618
+ // | "cumulativeMax"
619
+ // | "cumulativeAvg";
620
+
621
+ // export interface ExprCumulative<I, A, C> {
622
+ // type: "cumulative";
623
+ // operand: CumulativeOperand;
624
+ // input: I;
625
+ // orderBy: I;
626
+ // ascending?: boolean;
627
+ // partitionBy?: [QuerySelector<A, C>, ...QuerySelector<A, C>[]];
628
+ // }
629
+
480
630
  // ============ Reference Expression Types ============
481
631
 
482
632
  /**
@@ -583,6 +733,15 @@ export interface QueryColumnSelector<C> {
583
733
  id: C;
584
734
  }
585
735
 
736
+ /**
737
+ * Axis-or-column selector — mirrors the Rust `Selector<AxisSelector,
738
+ * ColumnSelector>` enum
739
+ * (`packages/bridge/src/query/query_sort.rs`). Used for the
740
+ * `partitionBy` / `over` fields of window expressions where either an
741
+ * axis or a column can drive the partition.
742
+ */
743
+ export type QuerySelector<A, C> = QueryAxisSelector<A> | QueryColumnSelector<C>;
744
+
586
745
  /**
587
746
  * Left outer join query operation.
588
747
  *
@@ -684,8 +843,8 @@ export interface QuerySliceAxes<Q, A> {
684
843
  * type: 'sort',
685
844
  * input: dataQuery,
686
845
  * sortBy: [
687
- * { expression: { type: 'columnRef', value: 'score' }, ascending: false, nullsFirst: null },
688
- * { expression: { type: 'axisRef', value: { name: 'name' } }, ascending: true, nullsFirst: null }
846
+ * { expression: { type: 'columnRef', value: 'score' }, ascending: false, nullsFirst: false },
847
+ * { expression: { type: 'axisRef', value: { name: 'name' } }, ascending: true, nullsFirst: false }
689
848
  * ]
690
849
  * }
691
850
  */
@@ -702,9 +861,8 @@ export interface QuerySort<Q, E> {
702
861
  * Null placement control:
703
862
  * - true: nulls sort before non-null values
704
863
  * - false: nulls sort after non-null values
705
- * - null: use default behavior (typically nulls last)
706
864
  */
707
- nullsFirst: null | boolean;
865
+ nullsFirst: boolean;
708
866
  }[];
709
867
  }
710
868
 
@@ -799,37 +957,57 @@ export interface QueryInlineColumn<T> {
799
957
  /**
800
958
  * Sparse to dense column query operation.
801
959
  *
802
- * Expands a column across additional axes using Cartesian product.
803
- * Creates all combinations of existing axis values with new axis values.
960
+ * Densifies a sparse column over the Cartesian product of distinct
961
+ * axis values: `axes` partitions the column's own axes into two sets
962
+ * (the listed axes on one side, the rest on the other); both sides
963
+ * contribute their distinct values, and the cross product fills in
964
+ * the missing tuples (null on rows that have no underlying value).
804
965
  *
805
- * **Use case**: When you need to repeat/broadcast a column across
806
- * new dimensions that it doesn't originally have.
966
+ * **Use case**: graph-maker and other UI surfaces that need a dense
967
+ * grid to plot.
807
968
  *
808
969
  * **Behavior**:
809
- * - Takes an existing column
810
- * - Expands it across specified axes indices
811
- * - Result has Cartesian product of original axes × new axes
970
+ * - Output axes = input axes (no axes are added).
971
+ * - Missing axis-tuple combinations become null in the output (or
972
+ * filled later via `pl7.app/graph/isDenseAxis` /
973
+ * `treatAbsentValuesAs` annotations).
812
974
  *
813
975
  * @template C - Column reference type
976
+ * @template A - Axis selector type (named selectors at the spec layer,
977
+ * numeric axis indices at the data layer)
814
978
  * @template SO - Spec override type
815
979
  *
816
980
  * @example
817
- * // Expand column across axes at indices 0 and 2
981
+ * // Spec layer: name the axis to expand across.
818
982
  * {
819
983
  * type: 'sparseToDenseColumn',
820
984
  * column: 'col_abc123',
821
- * axesIndices: [0, 2],
985
+ * axes: [{ name: 'sample' }],
822
986
  * specOverride: { ... } // optional spec modifications
823
987
  * }
988
+ *
989
+ * @example
990
+ * // Data layer: the same query after spec→data lowering.
991
+ * {
992
+ * type: 'sparseToDenseColumn',
993
+ * column: 'col_abc123',
994
+ * axes: [0],
995
+ * specOverride: { ... }
996
+ * }
824
997
  */
825
- export interface QuerySparseToDenseColumn<C, SO> {
998
+ export interface QuerySparseToDenseColumn<C, A, SO> {
826
999
  type: "sparseToDenseColumn";
827
1000
  /** Column reference (ID or full column object depending on context) */
828
1001
  column: C;
829
1002
  /** Optional override for the column specification */
830
1003
  specOverride?: SO;
831
- /** Indices of axes to expand across */
832
- axesIndices: number[];
1004
+ /**
1005
+ * Axes that participate in the cartesian-product densification.
1006
+ * Named selectors at the spec layer; resolved to numeric axis
1007
+ * indices during spec→data lowering. The Rust runtime also accepts
1008
+ * the legacy field name `axesIndices` as a serde alias.
1009
+ */
1010
+ axes: [A, ...A[]];
833
1011
  }
834
1012
 
835
1013
  /**
@@ -928,3 +1106,54 @@ export interface QueryLinkerJoin<L, JE extends QueryJoinEntry<unknown>> {
928
1106
  /** Rest side — one or more subqueries joined with the linker (at least one). */
929
1107
  secondary: JE[];
930
1108
  }
1109
+
1110
+ /**
1111
+ * `transformColumns` mode.
1112
+ *
1113
+ * - `"append"` — existing columns pass through; the new columns are
1114
+ * appended.
1115
+ * - `"replace"` — only the listed columns are kept; everything else is
1116
+ * dropped.
1117
+ *
1118
+ * The Rust runtime accepts the legacy `"add"` tag as a serde alias;
1119
+ * new code should emit `"append"`.
1120
+ */
1121
+ export type TransformColumnsMode = "append" | "replace";
1122
+
1123
+ /**
1124
+ * Single column entry for {@link QueryTransformColumns}.
1125
+ *
1126
+ * @template E - Expression type
1127
+ * @template SO - Spec override type (typically `PColumnIdAndSpec` at
1128
+ * the spec layer, or layer-specific id+typespec at the data layer)
1129
+ */
1130
+ export interface TransformColumnEntry<E, SO> {
1131
+ /** Expression computing the column values. */
1132
+ expression: E;
1133
+ /**
1134
+ * Optional spec override. If omitted, the runtime auto-derives the
1135
+ * column's spec from the host's input and the expression.
1136
+ * `valueType` is always inferred from the expression.
1137
+ */
1138
+ specOverride?: SO;
1139
+ }
1140
+
1141
+ /**
1142
+ * Transform-columns query operation.
1143
+ *
1144
+ * Computes one or more derived columns from `input`. Axes are
1145
+ * preserved.
1146
+ *
1147
+ * @template Q - Input query type
1148
+ * @template E - Expression type
1149
+ * @template SO - Spec override type
1150
+ */
1151
+ export interface QueryTransformColumns<Q, E, SO> {
1152
+ type: "transformColumns";
1153
+ /** Input query. */
1154
+ input: Q;
1155
+ /** `"append"` to add new columns; `"replace"` to keep only listed columns. */
1156
+ mode: TransformColumnsMode;
1157
+ /** Derived columns to compute (at least one). */
1158
+ columns: [TransformColumnEntry<E, SO>, ...TransformColumnEntry<E, SO>[]];
1159
+ }
@@ -1,22 +1,25 @@
1
1
  import type { PObjectId } from "../../../pool";
2
2
  import type {
3
3
  ExprAxisRef,
4
+ ExprCast,
4
5
  ExprColumnRef,
5
- ExprNumericBinary,
6
- ExprNumericComparison,
6
+ ExprConditional,
7
7
  ExprConstant,
8
+ ExprFillNull,
8
9
  ExprIsIn,
9
10
  ExprIsNull,
10
- ExprIfNull,
11
11
  ExprLogicalUnary,
12
12
  ExprLogicalVariadic,
13
+ ExprNumericBinary,
14
+ ExprNumericComparison,
15
+ ExprNumericUnary,
16
+ ExprRanking,
13
17
  ExprStringContains,
14
18
  ExprStringContainsFuzzy,
15
19
  ExprStringEquals,
16
20
  ExprStringRegex,
17
- ExprNumericUnary,
21
+ InferBooleanExpressionUnion,
18
22
  QueryColumn,
19
- QuerySparseToDenseColumn,
20
23
  QueryFilter,
21
24
  QueryInlineColumn,
22
25
  QueryJoinEntry,
@@ -24,9 +27,10 @@ import type {
24
27
  QueryOuterJoin,
25
28
  QuerySliceAxes,
26
29
  QuerySort,
30
+ QuerySparseToDenseColumn,
27
31
  QuerySymmetricJoin,
32
+ QueryTransformColumns,
28
33
  TypeSpec,
29
- InferBooleanExpressionUnion,
30
34
  } from "./query_common";
31
35
 
32
36
  /**
@@ -64,7 +68,11 @@ export type DataQueryColumn = QueryColumn;
64
68
  /** @see QueryInlineColumn */
65
69
  export type DataQueryInlineColumn = QueryInlineColumn<ColumnIdAndTypeSpec>;
66
70
  /** @see QuerySparseToDenseColumn */
67
- export type DataQuerySparseToDenseColumn = QuerySparseToDenseColumn<PObjectId, ColumnIdAndTypeSpec>;
71
+ export type DataQuerySparseToDenseColumn = QuerySparseToDenseColumn<
72
+ PObjectId,
73
+ number,
74
+ ColumnIdAndTypeSpec
75
+ >;
68
76
  /** @see QuerySymmetricJoin */
69
77
  export type DataQuerySymmetricJoin = QuerySymmetricJoin<DataQueryJoinEntry>;
70
78
  /** @see QueryOuterJoin */
@@ -93,6 +101,12 @@ export type DataQuerySliceAxes = QuerySliceAxes<DataQuery, number>;
93
101
  export type DataQuerySort = QuerySort<DataQuery, DataQueryExpression>;
94
102
  /** @see QueryFilter */
95
103
  export type DataQueryFilter = QueryFilter<DataQuery, DataQueryBooleanExpression>;
104
+ /** @see QueryTransformColumns */
105
+ export type DataQueryTransformColumns = QueryTransformColumns<
106
+ DataQuery,
107
+ DataQueryExpression,
108
+ ColumnIdAndTypeSpec
109
+ >;
96
110
 
97
111
  /**
98
112
  * Union of all data layer query types.
@@ -102,8 +116,8 @@ export type DataQueryFilter = QueryFilter<DataQuery, DataQueryBooleanExpression>
102
116
  *
103
117
  * Includes:
104
118
  * - Leaf nodes: column, inlineColumn, sparseToDenseColumn
105
- * - Join operations: innerJoin, fullJoin, outerJoin
106
- * - Transformations: sliceAxes, sort, filter
119
+ * - Join operations: innerJoin, fullJoin, outerJoin, linkerJoin
120
+ * - Transformations: sliceAxes, sort, filter, transformColumns
107
121
  */
108
122
  export type DataQuery =
109
123
  | DataQueryColumn
@@ -114,7 +128,8 @@ export type DataQuery =
114
128
  | DataQueryLinkerJoin
115
129
  | DataQuerySliceAxes
116
130
  | DataQuerySort
117
- | DataQueryFilter;
131
+ | DataQueryFilter
132
+ | DataQueryTransformColumns;
118
133
 
119
134
  /** @see ExprAxisRef */
120
135
  export type DataExprAxisRef = ExprAxisRef<number>;
@@ -133,10 +148,16 @@ export type DataQueryExpression =
133
148
  | ExprStringRegex<DataQueryExpression>
134
149
  | ExprStringContainsFuzzy<DataQueryExpression>
135
150
  | ExprIsNull<DataQueryExpression>
136
- | ExprIfNull<DataQueryExpression>
151
+ | ExprFillNull<DataQueryExpression>
137
152
  | ExprLogicalUnary<DataQueryExpression>
138
153
  | ExprLogicalVariadic<DataQueryExpression>
139
154
  | ExprIsIn<DataQueryExpression, string>
140
- | ExprIsIn<DataQueryExpression, number>;
155
+ | ExprIsIn<DataQueryExpression, number>
156
+ // | ExprIsInPolygon<DataQueryExpression> -- runtime not wired (see query_common.ts)
157
+ | ExprCast<DataQueryExpression>
158
+ | ExprConditional<DataQueryExpression>
159
+ // | ExprAggregation<DataQueryExpression, number, number> -- runtime not wired
160
+ | ExprRanking<DataQueryExpression, number, number>;
161
+ // | ExprCumulative<DataQueryExpression, number, number> -- runtime not wired
141
162
 
142
163
  export type DataQueryBooleanExpression = InferBooleanExpressionUnion<DataQueryExpression>;
@@ -1,22 +1,25 @@
1
1
  import type { PObjectId } from "../../../pool";
2
2
  import type {
3
3
  ExprAxisRef,
4
+ ExprCast,
4
5
  ExprColumnRef,
5
- ExprNumericBinary,
6
- ExprNumericComparison,
6
+ ExprConditional,
7
7
  ExprConstant,
8
+ ExprFillNull,
8
9
  ExprIsIn,
9
10
  ExprIsNull,
10
- ExprIfNull,
11
11
  ExprLogicalUnary,
12
12
  ExprLogicalVariadic,
13
+ ExprNumericBinary,
14
+ ExprNumericComparison,
15
+ ExprNumericUnary,
16
+ ExprRanking,
13
17
  ExprStringContains,
14
18
  ExprStringContainsFuzzy,
15
19
  ExprStringEquals,
16
20
  ExprStringRegex,
17
- ExprNumericUnary,
21
+ InferBooleanExpressionUnion,
18
22
  QueryColumn,
19
- QuerySparseToDenseColumn,
20
23
  QueryFilter,
21
24
  QueryInlineColumn,
22
25
  QueryJoinEntry,
@@ -24,8 +27,9 @@ import type {
24
27
  QueryOuterJoin,
25
28
  QuerySliceAxes,
26
29
  QuerySort,
30
+ QuerySparseToDenseColumn,
27
31
  QuerySymmetricJoin,
28
- InferBooleanExpressionUnion,
32
+ QueryTransformColumns,
29
33
  } from "./query_common";
30
34
  import type { Domain, PColumnIdAndSpec, SingleAxisSelector } from "../spec";
31
35
 
@@ -55,6 +59,7 @@ export type SpecQueryInlineColumn = QueryInlineColumn<PColumnIdAndSpec>;
55
59
  /** @see QuerySparseToDenseColumn */
56
60
  export type SpecQuerySparseToDenseColumn<C = PObjectId> = QuerySparseToDenseColumn<
57
61
  C,
62
+ SingleAxisSelector,
58
63
  PColumnIdAndSpec
59
64
  >;
60
65
  /** @see QuerySymmetricJoin */
@@ -82,6 +87,12 @@ export type SpecQuerySliceAxes<C = PObjectId> = QuerySliceAxes<SpecQuery<C>, Sin
82
87
  export type SpecQuerySort<C = PObjectId> = QuerySort<SpecQuery<C>, SpecQueryExpression>;
83
88
  /** @see QueryFilter */
84
89
  export type SpecQueryFilter<C = PObjectId> = QueryFilter<SpecQuery<C>, SpecQueryBooleanExpression>;
90
+ /** @see QueryTransformColumns */
91
+ export type SpecQueryTransformColumns<C = PObjectId> = QueryTransformColumns<
92
+ SpecQuery<C>,
93
+ SpecQueryExpression,
94
+ PColumnIdAndSpec
95
+ >;
85
96
 
86
97
  /**
87
98
  * Union of all spec layer query types.
@@ -95,8 +106,8 @@ export type SpecQueryFilter<C = PObjectId> = QueryFilter<SpecQuery<C>, SpecQuery
95
106
  *
96
107
  * Includes:
97
108
  * - Leaf nodes: column, inlineColumn, sparseToDenseColumn
98
- * - Join operations: innerJoin, fullJoin, outerJoin
99
- * - Transformations: sliceAxes, sort, filter
109
+ * - Join operations: innerJoin, fullJoin, outerJoin, linkerJoin
110
+ * - Transformations: sliceAxes, sort, filter, transformColumns
100
111
  */
101
112
  export type SpecQuery<C = PObjectId> =
102
113
  | SpecQueryColumn<C>
@@ -107,7 +118,8 @@ export type SpecQuery<C = PObjectId> =
107
118
  | SpecQueryLinkerJoin<C>
108
119
  | SpecQuerySliceAxes<C>
109
120
  | SpecQuerySort<C>
110
- | SpecQueryFilter<C>;
121
+ | SpecQueryFilter<C>
122
+ | SpecQueryTransformColumns<C>;
111
123
 
112
124
  /** @see ExprAxisRef */
113
125
  export type SpecExprAxisRef = ExprAxisRef<SingleAxisSelector>;
@@ -126,10 +138,16 @@ export type SpecQueryExpression =
126
138
  | ExprStringRegex<SpecQueryExpression>
127
139
  | ExprStringContainsFuzzy<SpecQueryExpression>
128
140
  | ExprIsNull<SpecQueryExpression>
129
- | ExprIfNull<SpecQueryExpression>
141
+ | ExprFillNull<SpecQueryExpression>
130
142
  | ExprLogicalUnary<SpecQueryExpression>
131
143
  | ExprLogicalVariadic<SpecQueryExpression>
132
144
  | ExprIsIn<SpecQueryExpression, string>
133
- | ExprIsIn<SpecQueryExpression, number>;
145
+ | ExprIsIn<SpecQueryExpression, number>
146
+ // | ExprIsInPolygon<SpecQueryExpression> -- runtime not wired (see query_common.ts)
147
+ | ExprCast<SpecQueryExpression>
148
+ | ExprConditional<SpecQueryExpression>
149
+ // | ExprAggregation<SpecQueryExpression, SingleAxisSelector, PObjectId> -- runtime not wired
150
+ | ExprRanking<SpecQueryExpression, SingleAxisSelector, PObjectId>;
151
+ // | ExprCumulative<SpecQueryExpression, SingleAxisSelector, PObjectId> -- runtime not wired
134
152
 
135
153
  export type SpecQueryBooleanExpression = InferBooleanExpressionUnion<SpecQueryExpression>;
@@ -27,9 +27,17 @@ describe("traverseQuerySpec", () => {
27
27
  });
28
28
 
29
29
  it("transforms sparseToDenseColumn leaf", () => {
30
- const q: Q = { type: "sparseToDenseColumn", column: "a", axesIndices: [0, 1] } as Q;
30
+ const q: Q = {
31
+ type: "sparseToDenseColumn",
32
+ column: "a",
33
+ axes: [{ name: "s" }, { name: "g" }],
34
+ } as Q;
31
35
  const result = traverseQuerySpec(q, { column: (c) => `${c}!` });
32
- expect(result).toEqual({ type: "sparseToDenseColumn", column: "a!", axesIndices: [0, 1] });
36
+ expect(result).toEqual({
37
+ type: "sparseToDenseColumn",
38
+ column: "a!",
39
+ axes: [{ name: "s" }, { name: "g" }],
40
+ });
33
41
  });
34
42
 
35
43
  it("passes inlineColumn through unchanged", () => {
@@ -107,7 +115,11 @@ describe("traverseQuerySpec", () => {
107
115
  type: "sort",
108
116
  input: col("a"),
109
117
  sortBy: [
110
- { expression: { type: "columnRef", value: pid("id1") }, ascending: true, nullsFirst: null },
118
+ {
119
+ expression: { type: "columnRef", value: pid("id1") },
120
+ ascending: true,
121
+ nullsFirst: false,
122
+ },
111
123
  ],
112
124
  } as Q;
113
125
  const result = traverseQuerySpec(q, { column: (c) => c.toUpperCase() });
@@ -115,7 +127,7 @@ describe("traverseQuerySpec", () => {
115
127
  type: "sort",
116
128
  input: { type: "column", column: "A" },
117
129
  sortBy: [
118
- { expression: { type: "columnRef", value: "id1" }, ascending: true, nullsFirst: null },
130
+ { expression: { type: "columnRef", value: "id1" }, ascending: true, nullsFirst: false },
119
131
  ],
120
132
  });
121
133
  });
@@ -197,7 +209,11 @@ describe("collectSpecQueryColumns", () => {
197
209
  });
198
210
 
199
211
  it("collects from sparseToDenseColumn", () => {
200
- const q: Q = { type: "sparseToDenseColumn", column: "s", axesIndices: [0] } as Q;
212
+ const q: Q = {
213
+ type: "sparseToDenseColumn",
214
+ column: "s",
215
+ axes: [{ name: "s" }],
216
+ } as Q;
201
217
  expect(collectSpecQueryColumns(q)).toEqual(["s"]);
202
218
  });
203
219
 
@@ -258,17 +274,17 @@ describe("sortSpecQuery", () => {
258
274
  });
259
275
  });
260
276
 
261
- it("sorts sparseToDenseColumn axesIndices", () => {
277
+ it("sorts sparseToDenseColumn axes", () => {
262
278
  const q: SpecQuery = {
263
279
  type: "sparseToDenseColumn",
264
280
  column: pid("a"),
265
- axesIndices: [2, 0, 1],
281
+ axes: [{ name: "z" }, { name: "x" }, { name: "y" }],
266
282
  } as SpecQuery;
267
283
  const result = sortSpecQuery(q);
268
284
  expect(result).toEqual({
269
285
  type: "sparseToDenseColumn",
270
286
  column: "a",
271
- axesIndices: [0, 1, 2],
287
+ axes: [{ name: "x" }, { name: "y" }, { name: "z" }],
272
288
  });
273
289
  });
274
290