@milaboratories/pl-model-common 1.24.6 → 1.24.8
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/drivers/pframe/filter_spec.d.ts +123 -0
- package/dist/drivers/pframe/filter_spec.d.ts.map +1 -0
- package/dist/drivers/pframe/index.d.ts +1 -0
- package/dist/drivers/pframe/index.d.ts.map +1 -1
- package/dist/drivers/pframe/query/index.d.ts +1 -0
- package/dist/drivers/pframe/query/index.d.ts.map +1 -1
- package/dist/drivers/pframe/query/query_common.d.ts +60 -8
- package/dist/drivers/pframe/query/query_common.d.ts.map +1 -1
- package/dist/drivers/pframe/query/query_data.d.ts +15 -15
- package/dist/drivers/pframe/query/query_data.d.ts.map +1 -1
- package/dist/drivers/pframe/query/query_spec.d.ts +19 -15
- package/dist/drivers/pframe/query/query_spec.d.ts.map +1 -1
- package/dist/drivers/pframe/query/utils.cjs +205 -0
- package/dist/drivers/pframe/query/utils.cjs.map +1 -0
- package/dist/drivers/pframe/query/utils.d.ts +6 -0
- package/dist/drivers/pframe/query/utils.d.ts.map +1 -0
- package/dist/drivers/pframe/query/utils.js +201 -0
- package/dist/drivers/pframe/query/utils.js.map +1 -0
- package/dist/drivers/pframe/table_calculate.cjs +153 -0
- package/dist/drivers/pframe/table_calculate.cjs.map +1 -1
- package/dist/drivers/pframe/table_calculate.d.ts +12 -1
- package/dist/drivers/pframe/table_calculate.d.ts.map +1 -1
- package/dist/drivers/pframe/table_calculate.js +150 -1
- package/dist/drivers/pframe/table_calculate.js.map +1 -1
- package/dist/flags/block_flags.cjs +5 -1
- package/dist/flags/block_flags.cjs.map +1 -1
- package/dist/flags/block_flags.d.ts +2 -1
- package/dist/flags/block_flags.d.ts.map +1 -1
- package/dist/flags/block_flags.js +5 -1
- package/dist/flags/block_flags.js.map +1 -1
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/drivers/pframe/filter_spec.ts +51 -0
- package/src/drivers/pframe/index.ts +1 -0
- package/src/drivers/pframe/query/index.ts +1 -0
- package/src/drivers/pframe/query/query_common.ts +60 -8
- package/src/drivers/pframe/query/query_data.ts +40 -34
- package/src/drivers/pframe/query/query_spec.ts +50 -34
- package/src/drivers/pframe/query/utils.ts +205 -0
- package/src/drivers/pframe/table_calculate.ts +163 -1
- package/src/flags/block_flags.ts +6 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SUniversalPColumnId } from "./spec/ids";
|
|
2
|
+
|
|
3
|
+
export type FilterSpecNode<Leaf, CommonNode = {}, CommonLeaf = {}> =
|
|
4
|
+
| (CommonLeaf & Leaf)
|
|
5
|
+
| (CommonNode & { type: "not"; filter: FilterSpecNode<Leaf, CommonNode, CommonLeaf> })
|
|
6
|
+
| (CommonNode & { type: "or"; filters: FilterSpecNode<Leaf, CommonNode, CommonLeaf>[] })
|
|
7
|
+
| (CommonNode & { type: "and"; filters: FilterSpecNode<Leaf, CommonNode, CommonLeaf>[] });
|
|
8
|
+
|
|
9
|
+
export type FilterSpecLeaf<T = SUniversalPColumnId> =
|
|
10
|
+
| { type: undefined }
|
|
11
|
+
| { type: "isNA"; column: T }
|
|
12
|
+
| { type: "isNotNA"; column: T }
|
|
13
|
+
| { type: "ifNa"; column: T; replacement: string }
|
|
14
|
+
| { type: "patternEquals"; column: T; value: string }
|
|
15
|
+
| { type: "patternNotEquals"; column: T; value: string }
|
|
16
|
+
| { type: "patternContainSubsequence"; column: T; value: string }
|
|
17
|
+
| { type: "patternNotContainSubsequence"; column: T; value: string }
|
|
18
|
+
| { type: "patternMatchesRegularExpression"; column: T; value: string }
|
|
19
|
+
| {
|
|
20
|
+
type: "patternFuzzyContainSubsequence";
|
|
21
|
+
column: T;
|
|
22
|
+
value: string;
|
|
23
|
+
maxEdits?: number;
|
|
24
|
+
substitutionsOnly?: boolean;
|
|
25
|
+
wildcard?: string;
|
|
26
|
+
}
|
|
27
|
+
| { type: "inSet"; column: T; value: string[] }
|
|
28
|
+
| { type: "notInSet"; column: T; value: string[] }
|
|
29
|
+
| { type: "topN"; column: T; n: number }
|
|
30
|
+
| { type: "bottomN"; column: T; n: number }
|
|
31
|
+
| { type: "equal"; column: T; x: number }
|
|
32
|
+
| { type: "notEqual"; column: T; x: number }
|
|
33
|
+
| { type: "lessThan"; column: T; x: number }
|
|
34
|
+
| { type: "greaterThan"; column: T; x: number }
|
|
35
|
+
| { type: "lessThanOrEqual"; column: T; x: number }
|
|
36
|
+
| { type: "greaterThanOrEqual"; column: T; x: number }
|
|
37
|
+
| { type: "equalToColumn"; column: T; rhs: T }
|
|
38
|
+
| { type: "lessThanColumn"; column: T; rhs: T; minDiff?: number }
|
|
39
|
+
| { type: "greaterThanColumn"; column: T; rhs: T; minDiff?: number }
|
|
40
|
+
| { type: "lessThanColumnOrEqual"; column: T; rhs: T; minDiff?: number }
|
|
41
|
+
| { type: "greaterThanColumnOrEqual"; column: T; rhs: T; minDiff?: number };
|
|
42
|
+
|
|
43
|
+
export type FilterSpec<
|
|
44
|
+
Leaf extends FilterSpecLeaf<unknown> = FilterSpecLeaf<SUniversalPColumnId>,
|
|
45
|
+
CommonNode = {},
|
|
46
|
+
CommonLeaf = CommonNode,
|
|
47
|
+
> = FilterSpecNode<Leaf, CommonNode, CommonLeaf>;
|
|
48
|
+
|
|
49
|
+
export type FilterSpecType = Exclude<FilterSpec, { type: undefined }>["type"];
|
|
50
|
+
|
|
51
|
+
export type FilterSpecOfType<T extends FilterSpecType> = Extract<FilterSpec, { type: T }>;
|
|
@@ -117,6 +117,54 @@ export type ExprConstant = {
|
|
|
117
117
|
value: string | number | boolean;
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Null check expression.
|
|
122
|
+
*
|
|
123
|
+
* Tests if an expression evaluates to null.
|
|
124
|
+
* **Input**: Any expression.
|
|
125
|
+
* **Output**: Boolean (true if input is null, false otherwise).
|
|
126
|
+
*
|
|
127
|
+
* @template I - The expression type (for recursion)
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Check if column value is null
|
|
131
|
+
* { type: 'isNull', input: columnRef }
|
|
132
|
+
*
|
|
133
|
+
* // Combine with NOT to check for non-null
|
|
134
|
+
* { type: 'not', input: { type: 'isNull', input: columnRef } }
|
|
135
|
+
*/
|
|
136
|
+
export interface ExprIsNull<I> {
|
|
137
|
+
type: "isNull";
|
|
138
|
+
/** Input expression to check for null */
|
|
139
|
+
input: I;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Null coalescing expression.
|
|
144
|
+
*
|
|
145
|
+
* Returns the input value if it is not null, otherwise returns the replacement value.
|
|
146
|
+
* Equivalent to SQL's `IFNULL(input, replacement)` or `COALESCE(input, replacement)`.
|
|
147
|
+
* **Input**: Any expression.
|
|
148
|
+
* **Output**: Same type as input/replacement.
|
|
149
|
+
* **Null handling**: If input is null, returns replacement; otherwise returns input.
|
|
150
|
+
*
|
|
151
|
+
* @template I - The expression type (for recursion)
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* // Replace null values with 0
|
|
155
|
+
* { type: 'ifNull', input: columnRef, replacement: { type: 'constant', value: 0 } }
|
|
156
|
+
*
|
|
157
|
+
* // Replace null strings with 'unknown'
|
|
158
|
+
* { type: 'ifNull', input: nameColumn, replacement: { type: 'constant', value: 'unknown' } }
|
|
159
|
+
*/
|
|
160
|
+
export interface ExprIfNull<I> {
|
|
161
|
+
type: "ifNull";
|
|
162
|
+
/** Value to check for null */
|
|
163
|
+
input: I;
|
|
164
|
+
/** Replacement value if input is null */
|
|
165
|
+
replacement: I;
|
|
166
|
+
}
|
|
167
|
+
|
|
120
168
|
// ============ Generic Expression Interfaces ============
|
|
121
169
|
// I = expression type (recursive), S = selector type
|
|
122
170
|
|
|
@@ -479,6 +527,7 @@ export type InferBooleanExpressionUnion<E> = [
|
|
|
479
527
|
E extends ExprStringContains<unknown> ? Extract<E, { type: "stringContains" }> : never,
|
|
480
528
|
E extends ExprStringContainsFuzzy<unknown> ? Extract<E, { type: "stringContainsFuzzy" }> : never,
|
|
481
529
|
E extends ExprStringRegex<unknown> ? Extract<E, { type: "stringRegex" }> : never,
|
|
530
|
+
E extends ExprIsNull<unknown> ? Extract<E, { type: "isNull" }> : never,
|
|
482
531
|
E extends ExprLogicalUnary<unknown> ? Extract<E, { type: "not" }> : never,
|
|
483
532
|
E extends ExprLogicalVariadic<unknown> ? Extract<E, { type: "and" | "or" }> : never,
|
|
484
533
|
E extends ExprIsIn<unknown, string | number> ? Extract<E, { type: "isIn" }> : never,
|
|
@@ -702,12 +751,14 @@ export interface QueryFilter<Q, E> {
|
|
|
702
751
|
*
|
|
703
752
|
* @example
|
|
704
753
|
* // Reference column by ID
|
|
705
|
-
* { type: 'column',
|
|
754
|
+
* { type: 'column', column: 'col_abc123' }
|
|
755
|
+
*
|
|
756
|
+
* @template C - Column reference type (e.g., PObjectId for spec, full PColumn for rich queries)
|
|
706
757
|
*/
|
|
707
|
-
export interface QueryColumn {
|
|
758
|
+
export interface QueryColumn<C = PObjectId> {
|
|
708
759
|
type: "column";
|
|
709
|
-
/**
|
|
710
|
-
|
|
760
|
+
/** Column reference (ID or full column object depending on context) */
|
|
761
|
+
column: C;
|
|
711
762
|
}
|
|
712
763
|
|
|
713
764
|
/**
|
|
@@ -751,21 +802,22 @@ export interface QueryInlineColumn<T> {
|
|
|
751
802
|
* - Expands it across specified axes indices
|
|
752
803
|
* - Result has Cartesian product of original axes × new axes
|
|
753
804
|
*
|
|
805
|
+
* @template C - Column reference type
|
|
754
806
|
* @template SO - Spec override type
|
|
755
807
|
*
|
|
756
808
|
* @example
|
|
757
809
|
* // Expand column across axes at indices 0 and 2
|
|
758
810
|
* {
|
|
759
811
|
* type: 'sparseToDenseColumn',
|
|
760
|
-
*
|
|
812
|
+
* column: 'col_abc123',
|
|
761
813
|
* axesIndices: [0, 2],
|
|
762
814
|
* specOverride: { ... } // optional spec modifications
|
|
763
815
|
* }
|
|
764
816
|
*/
|
|
765
|
-
export interface QuerySparseToDenseColumn<SO> {
|
|
817
|
+
export interface QuerySparseToDenseColumn<C, SO> {
|
|
766
818
|
type: "sparseToDenseColumn";
|
|
767
|
-
/** ID
|
|
768
|
-
|
|
819
|
+
/** Column reference (ID or full column object depending on context) */
|
|
820
|
+
column: C;
|
|
769
821
|
/** Optional override for the column specification */
|
|
770
822
|
specOverride?: SO;
|
|
771
823
|
/** Indices of axes to expand across */
|
|
@@ -3,8 +3,11 @@ import type {
|
|
|
3
3
|
ExprAxisRef,
|
|
4
4
|
ExprColumnRef,
|
|
5
5
|
ExprNumericBinary,
|
|
6
|
+
ExprNumericComparison,
|
|
6
7
|
ExprConstant,
|
|
7
8
|
ExprIsIn,
|
|
9
|
+
ExprIsNull,
|
|
10
|
+
ExprIfNull,
|
|
8
11
|
ExprLogicalUnary,
|
|
9
12
|
ExprLogicalVariadic,
|
|
10
13
|
ExprStringContains,
|
|
@@ -51,27 +54,27 @@ type ColumnIdAndTypeSpec = {
|
|
|
51
54
|
* // - This entry's axis 1 maps to result axis 2
|
|
52
55
|
* { entry: queryData, axesMapping: [0, 2] }
|
|
53
56
|
*/
|
|
54
|
-
export interface
|
|
57
|
+
export interface DataQueryJoinEntry extends QueryJoinEntry<DataQuery> {
|
|
55
58
|
/** Maps this entry's axes to the result axes by index */
|
|
56
59
|
axesMapping: number[];
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/** @see QueryColumn */
|
|
60
|
-
export type
|
|
63
|
+
export type DataQueryColumn = QueryColumn;
|
|
61
64
|
/** @see QueryInlineColumn */
|
|
62
|
-
export type
|
|
65
|
+
export type DataQueryInlineColumn = QueryInlineColumn<ColumnIdAndTypeSpec>;
|
|
63
66
|
/** @see QuerySparseToDenseColumn */
|
|
64
|
-
export type
|
|
67
|
+
export type DataQuerySparseToDenseColumn = QuerySparseToDenseColumn<PObjectId, ColumnIdAndTypeSpec>;
|
|
65
68
|
/** @see QuerySymmetricJoin */
|
|
66
|
-
export type
|
|
69
|
+
export type DataQuerySymmetricJoin = QuerySymmetricJoin<DataQueryJoinEntry>;
|
|
67
70
|
/** @see QueryOuterJoin */
|
|
68
|
-
export type
|
|
71
|
+
export type DataQueryOuterJoin = QueryOuterJoin<DataQueryJoinEntry>;
|
|
69
72
|
/** @see QuerySliceAxes */
|
|
70
|
-
export type
|
|
73
|
+
export type DataQuerySliceAxes = QuerySliceAxes<DataQuery, QueryAxisSelector<number>>;
|
|
71
74
|
/** @see QuerySort */
|
|
72
|
-
export type
|
|
75
|
+
export type DataQuerySort = QuerySort<DataQuery, DataQueryExpression>;
|
|
73
76
|
/** @see QueryFilter */
|
|
74
|
-
export type
|
|
77
|
+
export type DataQueryFilter = QueryFilter<DataQuery, DataQueryBooleanExpression>;
|
|
75
78
|
|
|
76
79
|
/**
|
|
77
80
|
* Union of all data layer query types.
|
|
@@ -84,34 +87,37 @@ export type QueryFilterData = QueryFilter<QueryData, QueryBooleanExpressionData>
|
|
|
84
87
|
* - Join operations: innerJoin, fullJoin, outerJoin
|
|
85
88
|
* - Transformations: sliceAxes, sort, filter
|
|
86
89
|
*/
|
|
87
|
-
export type
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
90
|
+
export type DataQuery =
|
|
91
|
+
| DataQueryColumn
|
|
92
|
+
| DataQueryInlineColumn
|
|
93
|
+
| DataQuerySparseToDenseColumn
|
|
94
|
+
| DataQuerySymmetricJoin
|
|
95
|
+
| DataQueryOuterJoin
|
|
96
|
+
| DataQuerySliceAxes
|
|
97
|
+
| DataQuerySort
|
|
98
|
+
| DataQueryFilter;
|
|
96
99
|
|
|
97
100
|
/** @see ExprAxisRef */
|
|
98
|
-
export type
|
|
101
|
+
export type DataExprAxisRef = ExprAxisRef<number>;
|
|
99
102
|
/** @see ExprColumnRef */
|
|
100
|
-
export type
|
|
103
|
+
export type DataExprColumnRef = ExprColumnRef<number>;
|
|
101
104
|
|
|
102
|
-
export type
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
+
export type DataQueryExpression =
|
|
106
|
+
| DataExprColumnRef
|
|
107
|
+
| DataExprAxisRef
|
|
105
108
|
| ExprConstant
|
|
106
|
-
| ExprNumericBinary<
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
109
|
+
| ExprNumericBinary<DataQueryExpression>
|
|
110
|
+
| ExprNumericComparison<DataQueryExpression>
|
|
111
|
+
| ExprNumericUnary<DataQueryExpression>
|
|
112
|
+
| ExprStringEquals<DataQueryExpression>
|
|
113
|
+
| ExprStringContains<DataQueryExpression>
|
|
114
|
+
| ExprStringRegex<DataQueryExpression>
|
|
115
|
+
| ExprStringContainsFuzzy<DataQueryExpression>
|
|
116
|
+
| ExprIsNull<DataQueryExpression>
|
|
117
|
+
| ExprIfNull<DataQueryExpression>
|
|
118
|
+
| ExprLogicalUnary<DataQueryExpression>
|
|
119
|
+
| ExprLogicalVariadic<DataQueryExpression>
|
|
120
|
+
| ExprIsIn<DataQueryExpression, string>
|
|
121
|
+
| ExprIsIn<DataQueryExpression, number>;
|
|
116
122
|
|
|
117
|
-
export type
|
|
123
|
+
export type DataQueryBooleanExpression = InferBooleanExpressionUnion<DataQueryExpression>;
|
|
@@ -3,8 +3,11 @@ import type {
|
|
|
3
3
|
ExprAxisRef,
|
|
4
4
|
ExprColumnRef,
|
|
5
5
|
ExprNumericBinary,
|
|
6
|
+
ExprNumericComparison,
|
|
6
7
|
ExprConstant,
|
|
7
8
|
ExprIsIn,
|
|
9
|
+
ExprIsNull,
|
|
10
|
+
ExprIfNull,
|
|
8
11
|
ExprLogicalUnary,
|
|
9
12
|
ExprLogicalVariadic,
|
|
10
13
|
ExprStringContains,
|
|
@@ -55,7 +58,7 @@ type ColumnIdAndSpec = {
|
|
|
55
58
|
* ]
|
|
56
59
|
* }
|
|
57
60
|
*/
|
|
58
|
-
export type
|
|
61
|
+
export type SpecQueryJoinEntry<C = PObjectId> = QueryJoinEntry<SpecQuery<C>> & {
|
|
59
62
|
/** Axis qualifications with additional domain constraints */
|
|
60
63
|
qualifications: {
|
|
61
64
|
/** Axis selector identifying which axis to qualify */
|
|
@@ -66,21 +69,27 @@ export type QueryJoinEntrySpec = QueryJoinEntry<QuerySpec> & {
|
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
/** @see QueryColumn */
|
|
69
|
-
export type
|
|
72
|
+
export type SpecQueryColumn<C = PObjectId> = QueryColumn<C>;
|
|
70
73
|
/** @see QueryInlineColumn */
|
|
71
|
-
export type
|
|
74
|
+
export type SpecQueryInlineColumn = QueryInlineColumn<ColumnIdAndSpec>;
|
|
72
75
|
/** @see QuerySparseToDenseColumn */
|
|
73
|
-
export type
|
|
76
|
+
export type SpecQuerySparseToDenseColumn<C = PObjectId> = QuerySparseToDenseColumn<
|
|
77
|
+
C,
|
|
78
|
+
PColumnIdAndSpec
|
|
79
|
+
>;
|
|
74
80
|
/** @see QuerySymmetricJoin */
|
|
75
|
-
export type
|
|
81
|
+
export type SpecQuerySymmetricJoin<C = PObjectId> = QuerySymmetricJoin<SpecQueryJoinEntry<C>>;
|
|
76
82
|
/** @see QueryOuterJoin */
|
|
77
|
-
export type
|
|
83
|
+
export type SpecQueryOuterJoin<C = PObjectId> = QueryOuterJoin<SpecQueryJoinEntry<C>>;
|
|
78
84
|
/** @see QuerySliceAxes */
|
|
79
|
-
export type
|
|
85
|
+
export type SpecQuerySliceAxes<C = PObjectId> = QuerySliceAxes<
|
|
86
|
+
SpecQuery<C>,
|
|
87
|
+
QueryAxisSelector<SingleAxisSelector>
|
|
88
|
+
>;
|
|
80
89
|
/** @see QuerySort */
|
|
81
|
-
export type
|
|
90
|
+
export type SpecQuerySort<C = PObjectId> = QuerySort<SpecQuery<C>, SpecQueryExpression>;
|
|
82
91
|
/** @see QueryFilter */
|
|
83
|
-
export type
|
|
92
|
+
export type SpecQueryFilter<C = PObjectId> = QueryFilter<SpecQuery<C>, SpecQueryBooleanExpression>;
|
|
84
93
|
|
|
85
94
|
/**
|
|
86
95
|
* Union of all spec layer query types.
|
|
@@ -88,39 +97,46 @@ export type QueryFilterSpec = QueryFilter<QuerySpec, QueryBooleanExpressionSpec>
|
|
|
88
97
|
* The spec layer operates with named selectors and column IDs,
|
|
89
98
|
* making it suitable for user-facing query construction and validation.
|
|
90
99
|
*
|
|
100
|
+
* @template C - Column reference type. Defaults to PObjectId (ID-only).
|
|
101
|
+
* Can be parameterized with richer types (e.g., PColumn<Data>) to carry
|
|
102
|
+
* full column data directly in the query tree.
|
|
103
|
+
*
|
|
91
104
|
* Includes:
|
|
92
105
|
* - Leaf nodes: column, inlineColumn, sparseToDenseColumn
|
|
93
106
|
* - Join operations: innerJoin, fullJoin, outerJoin
|
|
94
107
|
* - Transformations: sliceAxes, sort, filter
|
|
95
108
|
*/
|
|
96
|
-
export type
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
109
|
+
export type SpecQuery<C = PObjectId> =
|
|
110
|
+
| SpecQueryColumn<C>
|
|
111
|
+
| SpecQueryInlineColumn
|
|
112
|
+
| SpecQuerySparseToDenseColumn<C>
|
|
113
|
+
| SpecQuerySymmetricJoin<C>
|
|
114
|
+
| SpecQueryOuterJoin<C>
|
|
115
|
+
| SpecQuerySliceAxes<C>
|
|
116
|
+
| SpecQuerySort<C>
|
|
117
|
+
| SpecQueryFilter<C>;
|
|
105
118
|
|
|
106
119
|
/** @see ExprAxisRef */
|
|
107
|
-
export type
|
|
120
|
+
export type SpecExprAxisRef = ExprAxisRef<SingleAxisSelector>;
|
|
108
121
|
/** @see ExprColumnRef */
|
|
109
|
-
export type
|
|
122
|
+
export type SpecExprColumnRef = ExprColumnRef<PObjectId>;
|
|
110
123
|
|
|
111
|
-
export type
|
|
112
|
-
|
|
|
113
|
-
|
|
|
124
|
+
export type SpecQueryExpression =
|
|
125
|
+
| SpecExprColumnRef
|
|
126
|
+
| SpecExprAxisRef
|
|
114
127
|
| ExprConstant
|
|
115
|
-
| ExprNumericBinary<
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
|
|
|
128
|
+
| ExprNumericBinary<SpecQueryExpression>
|
|
129
|
+
| ExprNumericComparison<SpecQueryExpression>
|
|
130
|
+
| ExprNumericUnary<SpecQueryExpression>
|
|
131
|
+
| ExprStringEquals<SpecQueryExpression>
|
|
132
|
+
| ExprStringContains<SpecQueryExpression>
|
|
133
|
+
| ExprStringRegex<SpecQueryExpression>
|
|
134
|
+
| ExprStringContainsFuzzy<SpecQueryExpression>
|
|
135
|
+
| ExprIsNull<SpecQueryExpression>
|
|
136
|
+
| ExprIfNull<SpecQueryExpression>
|
|
137
|
+
| ExprLogicalUnary<SpecQueryExpression>
|
|
138
|
+
| ExprLogicalVariadic<SpecQueryExpression>
|
|
139
|
+
| ExprIsIn<SpecQueryExpression, string>
|
|
140
|
+
| ExprIsIn<SpecQueryExpression, number>;
|
|
125
141
|
|
|
126
|
-
export type
|
|
142
|
+
export type SpecQueryBooleanExpression = InferBooleanExpressionUnion<SpecQueryExpression>;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { canonicalizeJson } from "../../../json";
|
|
2
|
+
import { assertNever } from "../../../util";
|
|
3
|
+
import type {
|
|
4
|
+
SpecQueryBooleanExpression,
|
|
5
|
+
SpecQueryExpression,
|
|
6
|
+
SpecQuery,
|
|
7
|
+
SpecQueryJoinEntry,
|
|
8
|
+
} from "./query_spec";
|
|
9
|
+
|
|
10
|
+
const booleanTypesSet = new Set<SpecQueryBooleanExpression["type"]>([
|
|
11
|
+
"numericComparison",
|
|
12
|
+
"stringEquals",
|
|
13
|
+
"stringContains",
|
|
14
|
+
"stringContainsFuzzy",
|
|
15
|
+
"stringRegex",
|
|
16
|
+
"isNull",
|
|
17
|
+
"not",
|
|
18
|
+
"and",
|
|
19
|
+
"or",
|
|
20
|
+
"isIn",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export function isBooleanExpression(expr: SpecQueryExpression): expr is SpecQueryBooleanExpression {
|
|
24
|
+
return booleanTypesSet.has(
|
|
25
|
+
// @ts-expect-error -- TypeScript doesn't understand the discriminated union here, but we do at runtime
|
|
26
|
+
expr.type,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Collects all column references from a SpecQuery tree. */
|
|
31
|
+
export function collectQueryColumns<C>(query: SpecQuery<C>): C[] {
|
|
32
|
+
const result: C[] = [];
|
|
33
|
+
collectQueryColumnsImpl(query, result);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function collectQueryColumnsImpl<C>(query: SpecQuery<C>, result: C[]): void {
|
|
38
|
+
switch (query.type) {
|
|
39
|
+
case "column":
|
|
40
|
+
result.push(query.column);
|
|
41
|
+
break;
|
|
42
|
+
case "sparseToDenseColumn":
|
|
43
|
+
result.push(query.column);
|
|
44
|
+
break;
|
|
45
|
+
case "inlineColumn":
|
|
46
|
+
break;
|
|
47
|
+
case "innerJoin":
|
|
48
|
+
case "fullJoin":
|
|
49
|
+
for (const e of query.entries) collectQueryColumnsImpl(e.entry, result);
|
|
50
|
+
break;
|
|
51
|
+
case "outerJoin":
|
|
52
|
+
collectQueryColumnsImpl(query.primary.entry, result);
|
|
53
|
+
for (const e of query.secondary) collectQueryColumnsImpl(e.entry, result);
|
|
54
|
+
break;
|
|
55
|
+
case "filter":
|
|
56
|
+
case "sort":
|
|
57
|
+
case "sliceAxes":
|
|
58
|
+
collectQueryColumnsImpl(query.input, result);
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
assertNever(query);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function sortQuerySpec(query: SpecQuery): SpecQuery {
|
|
66
|
+
switch (query.type) {
|
|
67
|
+
case "column":
|
|
68
|
+
case "inlineColumn":
|
|
69
|
+
return query;
|
|
70
|
+
case "sparseToDenseColumn": {
|
|
71
|
+
const sortedAxesIndices = query.axesIndices.toSorted((lhs, rhs) => lhs - rhs);
|
|
72
|
+
return {
|
|
73
|
+
...query,
|
|
74
|
+
axesIndices: sortedAxesIndices,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
case "innerJoin":
|
|
78
|
+
case "fullJoin": {
|
|
79
|
+
const sortedEntries = query.entries.map(sortQueryJoinEntrySpec);
|
|
80
|
+
sortedEntries.sort(cmpQueryJoinEntrySpec);
|
|
81
|
+
return {
|
|
82
|
+
...query,
|
|
83
|
+
entries: sortedEntries,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
case "outerJoin": {
|
|
87
|
+
const sortedSecondary = query.secondary.map(sortQueryJoinEntrySpec);
|
|
88
|
+
sortedSecondary.sort(cmpQueryJoinEntrySpec);
|
|
89
|
+
return {
|
|
90
|
+
...query,
|
|
91
|
+
primary: sortQueryJoinEntrySpec(query.primary),
|
|
92
|
+
secondary: sortedSecondary,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
case "sliceAxes": {
|
|
96
|
+
const sortedAxisFilters = query.axisFilters.toSorted((lhs, rhs) => {
|
|
97
|
+
const lhsKey = canonicalizeJson(lhs.axisSelector);
|
|
98
|
+
const rhsKey = canonicalizeJson(rhs.axisSelector);
|
|
99
|
+
return lhsKey < rhsKey ? -1 : lhsKey === rhsKey ? 0 : 1;
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
...query,
|
|
103
|
+
input: sortQuerySpec(query.input),
|
|
104
|
+
axisFilters: sortedAxisFilters,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
case "sort":
|
|
108
|
+
return {
|
|
109
|
+
...query,
|
|
110
|
+
input: sortQuerySpec(query.input),
|
|
111
|
+
};
|
|
112
|
+
case "filter":
|
|
113
|
+
return {
|
|
114
|
+
...query,
|
|
115
|
+
input: sortQuerySpec(query.input),
|
|
116
|
+
};
|
|
117
|
+
default:
|
|
118
|
+
assertNever(query);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function sortQueryJoinEntrySpec(entry: SpecQueryJoinEntry): SpecQueryJoinEntry {
|
|
123
|
+
const sortedQualifications = entry.qualifications.toSorted((lhs, rhs) => {
|
|
124
|
+
const lhsKey = canonicalizeJson(lhs.axis);
|
|
125
|
+
const rhsKey = canonicalizeJson(rhs.axis);
|
|
126
|
+
return lhsKey < rhsKey ? -1 : lhsKey === rhsKey ? 0 : 1;
|
|
127
|
+
});
|
|
128
|
+
return {
|
|
129
|
+
entry: sortQuerySpec(entry.entry),
|
|
130
|
+
qualifications: sortedQualifications,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {
|
|
135
|
+
if (lhs.type !== rhs.type) {
|
|
136
|
+
return lhs.type < rhs.type ? -1 : 1;
|
|
137
|
+
}
|
|
138
|
+
switch (lhs.type) {
|
|
139
|
+
case "column":
|
|
140
|
+
return lhs.column < (rhs as typeof lhs).column
|
|
141
|
+
? -1
|
|
142
|
+
: lhs.column === (rhs as typeof lhs).column
|
|
143
|
+
? 0
|
|
144
|
+
: 1;
|
|
145
|
+
case "inlineColumn":
|
|
146
|
+
return lhs.spec.id < (rhs as typeof lhs).spec.id
|
|
147
|
+
? -1
|
|
148
|
+
: lhs.spec.id === (rhs as typeof lhs).spec.id
|
|
149
|
+
? 0
|
|
150
|
+
: 1;
|
|
151
|
+
case "sparseToDenseColumn":
|
|
152
|
+
return lhs.column < (rhs as typeof lhs).column
|
|
153
|
+
? -1
|
|
154
|
+
: lhs.column === (rhs as typeof lhs).column
|
|
155
|
+
? 0
|
|
156
|
+
: 1;
|
|
157
|
+
case "innerJoin":
|
|
158
|
+
case "fullJoin": {
|
|
159
|
+
const rhsJoin = rhs as typeof lhs;
|
|
160
|
+
if (lhs.entries.length !== rhsJoin.entries.length) {
|
|
161
|
+
return lhs.entries.length - rhsJoin.entries.length;
|
|
162
|
+
}
|
|
163
|
+
for (let i = 0; i < lhs.entries.length; i++) {
|
|
164
|
+
const cmp = cmpQueryJoinEntrySpec(lhs.entries[i], rhsJoin.entries[i]);
|
|
165
|
+
if (cmp !== 0) return cmp;
|
|
166
|
+
}
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
case "outerJoin": {
|
|
170
|
+
const rhsOuter = rhs as typeof lhs;
|
|
171
|
+
const cmp = cmpQueryJoinEntrySpec(lhs.primary, rhsOuter.primary);
|
|
172
|
+
if (cmp !== 0) return cmp;
|
|
173
|
+
if (lhs.secondary.length !== rhsOuter.secondary.length) {
|
|
174
|
+
return lhs.secondary.length - rhsOuter.secondary.length;
|
|
175
|
+
}
|
|
176
|
+
for (let i = 0; i < lhs.secondary.length; i++) {
|
|
177
|
+
const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsOuter.secondary[i]);
|
|
178
|
+
if (cmp !== 0) return cmp;
|
|
179
|
+
}
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
case "sliceAxes":
|
|
183
|
+
return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);
|
|
184
|
+
case "sort":
|
|
185
|
+
return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);
|
|
186
|
+
case "filter":
|
|
187
|
+
return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);
|
|
188
|
+
default:
|
|
189
|
+
assertNever(lhs);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function cmpQueryJoinEntrySpec(lhs: SpecQueryJoinEntry, rhs: SpecQueryJoinEntry): number {
|
|
194
|
+
const cmp = cmpQuerySpec(lhs.entry, rhs.entry);
|
|
195
|
+
if (cmp !== 0) return cmp;
|
|
196
|
+
if (lhs.qualifications.length !== rhs.qualifications.length) {
|
|
197
|
+
return lhs.qualifications.length - rhs.qualifications.length;
|
|
198
|
+
}
|
|
199
|
+
for (let i = 0; i < lhs.qualifications.length; i++) {
|
|
200
|
+
const lhsQ = canonicalizeJson(lhs.qualifications[i]);
|
|
201
|
+
const rhsQ = canonicalizeJson(rhs.qualifications[i]);
|
|
202
|
+
if (lhsQ !== rhsQ) return lhsQ < rhsQ ? -1 : 1;
|
|
203
|
+
}
|
|
204
|
+
return 0;
|
|
205
|
+
}
|