@milaboratories/pl-model-common 1.36.0 → 1.36.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/drivers/pframe/query/query_common.d.ts +14 -28
- package/dist/drivers/pframe/query/query_common.d.ts.map +1 -1
- package/dist/drivers/pframe/query/query_data.d.ts +3 -3
- package/dist/drivers/pframe/query/query_data.d.ts.map +1 -1
- package/dist/drivers/pframe/query/query_spec.d.ts +9 -25
- package/dist/drivers/pframe/query/query_spec.d.ts.map +1 -1
- package/dist/drivers/pframe/query/utils.cjs +19 -14
- package/dist/drivers/pframe/query/utils.cjs.map +1 -1
- package/dist/drivers/pframe/query/utils.js +19 -14
- package/dist/drivers/pframe/query/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/drivers/pframe/query/query_common.ts +13 -5
- package/src/drivers/pframe/query/query_data.ts +2 -3
- package/src/drivers/pframe/query/query_spec.ts +9 -33
- package/src/drivers/pframe/query/utils.ts +26 -16
|
@@ -484,28 +484,6 @@ type InferBooleanExpressionUnion<E> = [E extends ExprNumericComparison<unknown>
|
|
|
484
484
|
}> : never, E extends ExprIsIn<unknown, string | number> ? Extract<E, {
|
|
485
485
|
type: "isIn";
|
|
486
486
|
}> : never][number];
|
|
487
|
-
/**
|
|
488
|
-
* Selector for referencing an axis in queries.
|
|
489
|
-
*
|
|
490
|
-
* Used to identify a specific axis dimension in operations like:
|
|
491
|
-
* - Sorting by axis values
|
|
492
|
-
* - Partitioning for window functions
|
|
493
|
-
* - Filtering/slicing axes
|
|
494
|
-
*
|
|
495
|
-
* @template A - Axis identifier type (typically string name or numeric index)
|
|
496
|
-
*
|
|
497
|
-
* @example
|
|
498
|
-
* // Select axis by name
|
|
499
|
-
* { type: 'axis', id: 'sample' }
|
|
500
|
-
*
|
|
501
|
-
* // Select axis by index
|
|
502
|
-
* { type: 'axis', id: 0 }
|
|
503
|
-
*/
|
|
504
|
-
interface QueryAxisSelector<A> {
|
|
505
|
-
type: "axis";
|
|
506
|
-
/** Axis identifier (name or index depending on context) */
|
|
507
|
-
id: A;
|
|
508
|
-
}
|
|
509
487
|
/**
|
|
510
488
|
* Left outer join query operation.
|
|
511
489
|
*
|
|
@@ -554,23 +532,31 @@ interface QueryOuterJoin<JE extends QueryJoinEntry<unknown>> {
|
|
|
554
532
|
* @template A - Axis selector type
|
|
555
533
|
*
|
|
556
534
|
* @example
|
|
557
|
-
* //
|
|
535
|
+
* // Spec layer: axisSelector is a SingleAxisSelector.
|
|
558
536
|
* {
|
|
559
537
|
* type: 'sliceAxes',
|
|
560
538
|
* input: fullDataQuery,
|
|
561
539
|
* axisFilters: [
|
|
562
|
-
* {
|
|
563
|
-
* {
|
|
540
|
+
* { axisSelector: { name: 'sample' }, constant: 'Sample1' },
|
|
541
|
+
* { axisSelector: { name: 'condition' }, constant: 'Treatment' }
|
|
564
542
|
* ]
|
|
565
543
|
* }
|
|
544
|
+
*
|
|
545
|
+
* @example
|
|
546
|
+
* // Data layer: axisSelector is the axis index.
|
|
547
|
+
* {
|
|
548
|
+
* type: 'sliceAxes',
|
|
549
|
+
* input: fullDataQuery,
|
|
550
|
+
* axisFilters: [{ axisSelector: 0, constant: 'Sample1' }]
|
|
551
|
+
* }
|
|
566
552
|
*/
|
|
567
|
-
interface QuerySliceAxes<Q, A
|
|
553
|
+
interface QuerySliceAxes<Q, A> {
|
|
568
554
|
type: "sliceAxes";
|
|
569
555
|
/** Input query to slice */
|
|
570
556
|
input: Q;
|
|
571
557
|
/** List of axis filters to apply (at least one required) */
|
|
572
558
|
axisFilters: {
|
|
573
|
-
/**
|
|
559
|
+
/** Axis to filter. `SingleAxisSelector` at the spec layer; axis index at the data layer. */axisSelector: A; /** The constant value to filter the axis to */
|
|
574
560
|
constant: string | number;
|
|
575
561
|
}[];
|
|
576
562
|
}
|
|
@@ -832,5 +818,5 @@ interface QueryLinkerJoin<L, JE extends QueryJoinEntry<unknown>> {
|
|
|
832
818
|
secondary: JE[];
|
|
833
819
|
}
|
|
834
820
|
//#endregion
|
|
835
|
-
export { ExprAxisRef, ExprColumnRef, ExprConstant, ExprIfNull, ExprIsIn, ExprIsNull, ExprLogicalUnary, ExprLogicalVariadic, ExprNumericBinary, ExprNumericComparison, ExprNumericUnary, ExprStringContains, ExprStringContainsFuzzy, ExprStringEquals, ExprStringRegex, InferBooleanExpressionUnion,
|
|
821
|
+
export { ExprAxisRef, ExprColumnRef, ExprConstant, ExprIfNull, ExprIsIn, ExprIsNull, ExprLogicalUnary, ExprLogicalVariadic, ExprNumericBinary, ExprNumericComparison, ExprNumericUnary, ExprStringContains, ExprStringContainsFuzzy, ExprStringEquals, ExprStringRegex, InferBooleanExpressionUnion, QueryColumn, QueryFilter, QueryInlineColumn, QueryJoinEntry, QueryLinkerJoin, QueryOuterJoin, QuerySliceAxes, QuerySort, QuerySparseToDenseColumn, QuerySymmetricJoin, TypeSpec };
|
|
836
822
|
//# sourceMappingURL=query_common.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query_common.d.ts","names":[],"sources":["../../../../src/drivers/pframe/query/query_common.ts"],"mappings":";;;;;;;;AAYA;;KAAY,QAAA;EAIc,mEAFxB,IAAA,EAAM,aAAA,IAAA;EAEN,OAAA,EAAS,eAAA;AAAA;;;AAuBX;;;;;AAyBA;;;;;AAgBA;;;;;AAkCA;KA3EY,mBAAA;;;;AAgGZ;;;;;;;;;;KAvEY,oBAAA;;;;;;;;;;;;AA6HZ;;;KA7GY,wBAAA;;;;;;;;;;;;;;;;AA2KZ;KAzIY,YAAA;EACV,IAAA;EACA,KAAA;AAAA;;;;;;;;;;;;;;AAqKF;;;UAlJiB,UAAA;EACf,IAAA;EAkJA;EAhJA,KAAA,EAAO,CAAA;AAAA;;;;;AA4KT;;;;;;;;;;;AAyBA;;;UAhLiB,UAAA;EACf,IAAA;EAgLA;EA9KA,KAAA,EAAO,CAAA;EAgLA;EA9KP,WAAA,EAAa,CAAA;AAAA;;;AAuNf;;;;;;;;;;;;;;;AAwCA;;UAtOiB,gBAAA;EACf,IAAA;EAqOgC;EAnOhC,OAAA,EAAS,mBAAA;EAsOT;EApOA,KAAA,EAAO,CAAA;AAAA;;AA2PT;;;;;;;;;;AA0BA;;;;;;;;UA/PiB,iBAAA;EACf,IAAA;EAmQK;EAjQL,OAAA,EAAS,oBAAA;EAiQH;EA/PN,IAAA,EAAM,CAAA;EAmRoB;EAjR1B,KAAA,EAAO,CAAA;AAAA;;;;;;;AAsST;;;;;;;;;;AAMA;;;;;;;;;UAhRiB,qBAAA;EACf,IAAA;EAkRA;EAhRA,OAAA,EAAS,wBAAA;EAgRuC;EA9QhD,IAAA,EAAM,CAAA;EA+QN;EA7QA,KAAA,EAAO,CAAA;AAAA;;;;;;;;;;;;;;;;;;;;;UAuBQ,gBAAA;EACf,IAAA;EA0PsD;EAxPtD,KAAA,EAAO,CAAA;EAgPP;EA9OA,KAAA;EA8O2C;EA5O3C,eAAA;AAAA;;;;;;;;;;;;;;;;;;;;UAsBe,eAAA;EACf,IAAA;EA0NU;EAxNV,KAAA,EAAO,CAAA;EAwNiC;EAtNxC,KAAA;AAAA;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"query_common.d.ts","names":[],"sources":["../../../../src/drivers/pframe/query/query_common.ts"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExprAxisRef, ExprColumnRef, ExprConstant, ExprIfNull, ExprIsIn, ExprIsNull, ExprLogicalUnary, ExprLogicalVariadic, ExprNumericBinary, ExprNumericComparison, ExprNumericUnary, ExprStringContains, ExprStringContainsFuzzy, ExprStringEquals, ExprStringRegex, InferBooleanExpressionUnion,
|
|
1
|
+
import { ExprAxisRef, ExprColumnRef, ExprConstant, ExprIfNull, ExprIsIn, ExprIsNull, ExprLogicalUnary, ExprLogicalVariadic, ExprNumericBinary, ExprNumericComparison, ExprNumericUnary, ExprStringContains, ExprStringContainsFuzzy, ExprStringEquals, ExprStringRegex, InferBooleanExpressionUnion, QueryColumn, QueryFilter, QueryInlineColumn, QueryJoinEntry, QueryLinkerJoin, QueryOuterJoin, QuerySliceAxes, QuerySort, QuerySparseToDenseColumn, QuerySymmetricJoin, TypeSpec } from "./query_common.js";
|
|
2
2
|
import { PObjectId } from "../../../pool/spec.js";
|
|
3
3
|
//#region src/drivers/pframe/query/query_data.d.ts
|
|
4
4
|
/**
|
|
@@ -9,7 +9,7 @@ import { PObjectId } from "../../../pool/spec.js";
|
|
|
9
9
|
*/
|
|
10
10
|
type ColumnIdAndTypeSpec = {
|
|
11
11
|
/** Unique identifier of the column */id: PObjectId; /** Type specification defining axes and column types */
|
|
12
|
-
|
|
12
|
+
spec: TypeSpec;
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
15
|
* Join entry for data layer queries.
|
|
@@ -53,7 +53,7 @@ type DataQueryLinkerJoinLinker = {
|
|
|
53
53
|
/** @see QueryLinkerJoin */
|
|
54
54
|
type DataQueryLinkerJoin = QueryLinkerJoin<DataQueryLinkerJoinLinker, DataQueryJoinEntry>;
|
|
55
55
|
/** @see QuerySliceAxes */
|
|
56
|
-
type DataQuerySliceAxes = QuerySliceAxes<DataQuery,
|
|
56
|
+
type DataQuerySliceAxes = QuerySliceAxes<DataQuery, number>;
|
|
57
57
|
/** @see QuerySort */
|
|
58
58
|
type DataQuerySort = QuerySort<DataQuery, DataQueryExpression>;
|
|
59
59
|
/** @see QueryFilter */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query_data.d.ts","names":[],"sources":["../../../../src/drivers/pframe/query/query_data.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"query_data.d.ts","names":[],"sources":["../../../../src/drivers/pframe/query/query_data.ts"],"mappings":";;;;;;AA6BwB;;;KAQnB,mBAAA;EAEH,sCAAA,EAAA,EAAI,SAAA,EAEJ;EAAA,IAAA,EAAM,QAAA;AAAA;;AAeR;;;;;;;;;AAMA;;UANiB,kBAAA,SAA2B,cAAA,CAAe,SAAA;EAM7B;EAJ5B,WAAA;AAAA;;KAIU,eAAA,GAAkB,WAAA;;KAElB,qBAAA,GAAwB,iBAAA,CAAkB,mBAAA;AAEtD;AAAA,KAAY,4BAAA,GAA+B,wBAAA,CAAyB,SAAA,EAAW,mBAAA;;KAEnE,sBAAA,GAAyB,kBAAA,CAAmB,kBAAA;;KAE5C,kBAAA,GAAqB,cAAA,CAAe,kBAAA;;;;;;;;AAFhD;KAWY,yBAAA;iCAEV,MAAA,EAAQ,SAAA,EAbgE;EAexE,WAAA,YAb4B;EAe5B,kBAAA;AAAA;;KAGU,mBAAA,GAAsB,eAAA,CAAgB,yBAAA,EAA2B,kBAAA;;KAEjE,kBAAA,GAAqB,cAAA,CAAe,SAAA;;KAEpC,aAAA,GAAgB,SAAA,CAAU,SAAA,EAAW,mBAAA;;KAErC,eAAA,GAAkB,WAAA,CAAY,SAAA,EAAW,0BAAA;;;;AANrD;;;;;;;;KAmBY,SAAA,GACR,eAAA,GACA,qBAAA,GACA,4BAAA,GACA,sBAAA,GACA,kBAAA,GACA,mBAAA,GACA,kBAAA,GACA,aAAA,GACA,eAAA;;KAGQ,eAAA,GAAkB,WAAA;;KAElB,iBAAA,GAAoB,aAAA;AAAA,KAEpB,mBAAA,GACR,iBAAA,GACA,eAAA,GACA,YAAA,GACA,iBAAA,CAAkB,mBAAA,IAClB,qBAAA,CAAsB,mBAAA,IACtB,gBAAA,CAAiB,mBAAA,IACjB,gBAAA,CAAiB,mBAAA,IACjB,kBAAA,CAAmB,mBAAA,IACnB,eAAA,CAAgB,mBAAA,IAChB,uBAAA,CAAwB,mBAAA,IACxB,UAAA,CAAW,mBAAA,IACX,UAAA,CAAW,mBAAA,IACX,gBAAA,CAAiB,mBAAA,IACjB,mBAAA,CAAoB,mBAAA,IACpB,QAAA,CAAS,mBAAA,YACT,QAAA,CAAS,mBAAA;AAAA,KAED,0BAAA,GAA6B,2BAAA,CAA4B,mBAAA"}
|
|
@@ -1,44 +1,28 @@
|
|
|
1
|
-
import { Domain, PColumnIdAndSpec
|
|
1
|
+
import { Domain, PColumnIdAndSpec } from "../spec/spec.js";
|
|
2
2
|
import { SingleAxisSelector } from "../spec/selectors.js";
|
|
3
|
-
import { ExprAxisRef, ExprColumnRef, ExprConstant, ExprIfNull, ExprIsIn, ExprIsNull, ExprLogicalUnary, ExprLogicalVariadic, ExprNumericBinary, ExprNumericComparison, ExprNumericUnary, ExprStringContains, ExprStringContainsFuzzy, ExprStringEquals, ExprStringRegex, InferBooleanExpressionUnion,
|
|
3
|
+
import { ExprAxisRef, ExprColumnRef, ExprConstant, ExprIfNull, ExprIsIn, ExprIsNull, ExprLogicalUnary, ExprLogicalVariadic, ExprNumericBinary, ExprNumericComparison, ExprNumericUnary, ExprStringContains, ExprStringContainsFuzzy, ExprStringEquals, ExprStringRegex, InferBooleanExpressionUnion, QueryColumn, QueryFilter, QueryInlineColumn, QueryJoinEntry, QueryLinkerJoin, QueryOuterJoin, QuerySliceAxes, QuerySort, QuerySparseToDenseColumn, QuerySymmetricJoin } from "./query_common.js";
|
|
4
4
|
import { PObjectId } from "../../../pool/spec.js";
|
|
5
5
|
//#region src/drivers/pframe/query/query_spec.d.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Pairs a column ID with its full PColumnSpec.
|
|
10
|
-
* Used in spec layer to carry column specification alongside references.
|
|
11
|
-
*/
|
|
12
|
-
type ColumnIdAndSpec = {
|
|
13
|
-
/** Unique identifier of the column */id: PObjectId; /** Full column specification including axes and value type */
|
|
14
|
-
spec: PColumnSpec;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Join entry for spec layer queries.
|
|
18
|
-
*
|
|
19
|
-
* Extends the base join entry with axis qualifications.
|
|
20
|
-
* Qualifications specify additional domain constraints for each axis,
|
|
21
|
-
* enabling more precise control over how columns are joined.
|
|
7
|
+
* Join entry for spec-layer queries — the base join entry extended with
|
|
8
|
+
* per-axis domain constraints. Absent `qualifications` is equivalent to `[]`.
|
|
22
9
|
*
|
|
23
10
|
* @example
|
|
24
|
-
* // Join entry with axis qualifications
|
|
25
11
|
* {
|
|
26
12
|
* entry: querySpec,
|
|
27
|
-
* qualifications: [
|
|
28
|
-
* { axis: { name: 'sample' }, contextDomain: { ... } }
|
|
29
|
-
* ]
|
|
13
|
+
* qualifications: [{ axis: { name: 'sample' }, contextDomain: { ... } }]
|
|
30
14
|
* }
|
|
31
15
|
*/
|
|
32
16
|
type SpecQueryJoinEntry<C = PObjectId> = QueryJoinEntry<SpecQuery<C>> & {
|
|
33
|
-
|
|
34
|
-
/** Axis
|
|
17
|
+
qualifications?: {
|
|
18
|
+
/** Axis to qualify. */axis: SingleAxisSelector; /** Additional domain constraints for this axis. */
|
|
35
19
|
contextDomain: Domain;
|
|
36
20
|
}[];
|
|
37
21
|
};
|
|
38
22
|
/** @see QueryColumn */
|
|
39
23
|
type SpecQueryColumn<C = PObjectId> = QueryColumn<C>;
|
|
40
24
|
/** @see QueryInlineColumn */
|
|
41
|
-
type SpecQueryInlineColumn = QueryInlineColumn<
|
|
25
|
+
type SpecQueryInlineColumn = QueryInlineColumn<PColumnIdAndSpec>;
|
|
42
26
|
/** @see QuerySparseToDenseColumn */
|
|
43
27
|
type SpecQuerySparseToDenseColumn<C = PObjectId> = QuerySparseToDenseColumn<C, PColumnIdAndSpec>;
|
|
44
28
|
/** @see QuerySymmetricJoin */
|
|
@@ -57,7 +41,7 @@ type SpecQueryLinkerJoinLinker<C = PObjectId> = {
|
|
|
57
41
|
/** @see QueryLinkerJoin */
|
|
58
42
|
type SpecQueryLinkerJoin<C = PObjectId> = QueryLinkerJoin<SpecQueryLinkerJoinLinker<C>, SpecQueryJoinEntry<C>>;
|
|
59
43
|
/** @see QuerySliceAxes */
|
|
60
|
-
type SpecQuerySliceAxes<C = PObjectId> = QuerySliceAxes<SpecQuery<C>,
|
|
44
|
+
type SpecQuerySliceAxes<C = PObjectId> = QuerySliceAxes<SpecQuery<C>, SingleAxisSelector>;
|
|
61
45
|
/** @see QuerySort */
|
|
62
46
|
type SpecQuerySort<C = PObjectId> = QuerySort<SpecQuery<C>, SpecQueryExpression>;
|
|
63
47
|
/** @see QueryFilter */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query_spec.d.ts","names":[],"sources":["../../../../src/drivers/pframe/query/query_spec.ts"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"query_spec.d.ts","names":[],"sources":["../../../../src/drivers/pframe/query/query_spec.ts"],"mappings":";;;;;;;;;;AAyCA;;;;;KAAY,kBAAA,KAAuB,SAAA,IAAa,cAAA,CAAe,SAAA,CAAU,CAAA;EACvE,cAAA;IAEQ,uBAAN,IAAA,EAAM,kBAAA,EAEe;IAArB,aAAA,EAAe,MAAA;EAAA;AAAA;;KAKP,eAAA,KAAoB,SAAA,IAAa,WAAA,CAAY,CAAA;;KAE7C,qBAAA,GAAwB,iBAAA,CAAkB,gBAAA;;KAE1C,4BAAA,KAAiC,SAAA,IAAa,wBAAA,CACxD,CAAA,EACA,gBAAA;;KAGU,sBAAA,KAA2B,SAAA,IAAa,kBAAA,CAAmB,kBAAA,CAAmB,CAAA;;KAE9E,kBAAA,KAAuB,SAAA,IAAa,cAAA,CAAe,kBAAA,CAAmB,CAAA;AAXlF;;;;;;AAAA,KAkBY,yBAAA,KAA8B,SAAA;EAlBc,+BAoBtD,MAAA,EAAQ,CAAA;AAAA;;KAGE,mBAAA,KAAwB,SAAA,IAAa,eAAA,CAC/C,yBAAA,CAA0B,CAAA,GAC1B,kBAAA,CAAmB,CAAA;;KAGT,kBAAA,KAAuB,SAAA,IAAa,cAAA,CAAe,SAAA,CAAU,CAAA,GAAI,kBAAA;AA1B7E;AAAA,KA4BY,aAAA,KAAkB,SAAA,IAAa,SAAA,CAAU,SAAA,CAAU,CAAA,GAAI,mBAAA;;KAEvD,eAAA,KAAoB,SAAA,IAAa,WAAA,CAAY,SAAA,CAAU,CAAA,GAAI,0BAAA;;AA5BvE;;;;;;;;;;;;;;KA6CY,SAAA,KAAc,SAAA,IACtB,eAAA,CAAgB,CAAA,IAChB,qBAAA,GACA,4BAAA,CAA6B,CAAA,IAC7B,sBAAA,CAAuB,CAAA,IACvB,kBAAA,CAAmB,CAAA,IACnB,mBAAA,CAAoB,CAAA,IACpB,kBAAA,CAAmB,CAAA,IACnB,aAAA,CAAc,CAAA,IACd,eAAA,CAAgB,CAAA;;KAGR,eAAA,GAAkB,WAAA,CAAY,kBAAA;;KAE9B,iBAAA,GAAoB,aAAA,CAAc,SAAA;AAAA,KAElC,mBAAA,GACR,iBAAA,GACA,eAAA,GACA,YAAA,GACA,iBAAA,CAAkB,mBAAA,IAClB,qBAAA,CAAsB,mBAAA,IACtB,gBAAA,CAAiB,mBAAA,IACjB,gBAAA,CAAiB,mBAAA,IACjB,kBAAA,CAAmB,mBAAA,IACnB,eAAA,CAAgB,mBAAA,IAChB,uBAAA,CAAwB,mBAAA,IACxB,UAAA,CAAW,mBAAA,IACX,UAAA,CAAW,mBAAA,IACX,gBAAA,CAAiB,mBAAA,IACjB,mBAAA,CAAoB,mBAAA,IACpB,QAAA,CAAS,mBAAA,YACT,QAAA,CAAS,mBAAA;AAAA,KAED,0BAAA,GAA6B,2BAAA,CAA4B,mBAAA"}
|
|
@@ -142,21 +142,24 @@ function sortSpecQuery(query) {
|
|
|
142
142
|
default: return node;
|
|
143
143
|
}
|
|
144
144
|
},
|
|
145
|
-
joinEntry: (entry) =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
joinEntry: (entry) => {
|
|
146
|
+
if (entry.qualifications === void 0 || entry.qualifications.length === 0) return entry;
|
|
147
|
+
return {
|
|
148
|
+
...entry,
|
|
149
|
+
qualifications: entry.qualifications.toSorted((a, b) => {
|
|
150
|
+
const ak = require_json.canonicalizeJson(a.axis);
|
|
151
|
+
const bk = require_json.canonicalizeJson(b.axis);
|
|
152
|
+
return ak < bk ? -1 : ak === bk ? 0 : 1;
|
|
153
|
+
})
|
|
154
|
+
};
|
|
155
|
+
}
|
|
153
156
|
});
|
|
154
157
|
}
|
|
155
158
|
function cmpQuerySpec(lhs, rhs) {
|
|
156
159
|
if (lhs.type !== rhs.type) return lhs.type < rhs.type ? -1 : 1;
|
|
157
160
|
switch (lhs.type) {
|
|
158
161
|
case "column": return lhs.column < rhs.column ? -1 : lhs.column === rhs.column ? 0 : 1;
|
|
159
|
-
case "inlineColumn": return lhs.spec.
|
|
162
|
+
case "inlineColumn": return lhs.spec.columnId < rhs.spec.columnId ? -1 : lhs.spec.columnId === rhs.spec.columnId ? 0 : 1;
|
|
160
163
|
case "sparseToDenseColumn": return lhs.column < rhs.column ? -1 : lhs.column === rhs.column ? 0 : 1;
|
|
161
164
|
case "innerJoin":
|
|
162
165
|
case "fullJoin": {
|
|
@@ -198,11 +201,13 @@ function cmpQuerySpec(lhs, rhs) {
|
|
|
198
201
|
function cmpQueryJoinEntrySpec(lhs, rhs) {
|
|
199
202
|
const cmp = cmpQuerySpec(lhs.entry, rhs.entry);
|
|
200
203
|
if (cmp !== 0) return cmp;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
const lhsQ = lhs.qualifications ?? [];
|
|
205
|
+
const rhsQ = rhs.qualifications ?? [];
|
|
206
|
+
if (lhsQ.length !== rhsQ.length) return lhsQ.length - rhsQ.length;
|
|
207
|
+
for (let i = 0; i < lhsQ.length; i++) {
|
|
208
|
+
const l = require_json.canonicalizeJson(lhsQ[i]);
|
|
209
|
+
const r = require_json.canonicalizeJson(rhsQ[i]);
|
|
210
|
+
if (l !== r) return l < r ? -1 : 1;
|
|
206
211
|
}
|
|
207
212
|
return 0;
|
|
208
213
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.cjs","names":["canonicalizeJson"],"sources":["../../../../src/drivers/pframe/query/utils.ts"],"sourcesContent":["import { canonicalizeJson } from \"../../../json\";\nimport { assertNever } from \"../../../util\";\nimport type {\n SpecQueryBooleanExpression,\n SpecQueryExpression,\n SpecQuery,\n SpecQueryJoinEntry,\n} from \"./query_spec\";\n\nconst booleanTypesSet = new Set<SpecQueryBooleanExpression[\"type\"]>([\n \"numericComparison\",\n \"stringEquals\",\n \"stringContains\",\n \"stringContainsFuzzy\",\n \"stringRegex\",\n \"isNull\",\n \"not\",\n \"and\",\n \"or\",\n \"isIn\",\n]);\n\nexport function isBooleanExpression(expr: SpecQueryExpression): expr is SpecQueryBooleanExpression {\n return booleanTypesSet.has(\n // @ts-expect-error -- TypeScript doesn't understand the discriminated union here, but we do at runtime\n expr.type,\n );\n}\n\n/**\n * Recursively traverses a SpecQuery tree bottom-up, applying visitor callbacks.\n *\n * Traversal order:\n * 1. Recurse into child queries\n * 2. Apply `column` to transform column references in leaf nodes\n * 3. Apply `joinEntry` to each join entry (with inner query already traversed)\n * 4. Assemble node with transformed children\n * 5. Apply `node` to the assembled node\n */\nexport function traverseQuerySpec<C1, C2>(\n query: SpecQuery<C1>,\n visitor: {\n /** Transform column references in leaf nodes (column, sparseToDenseColumn). */\n column: (c: C1) => C2;\n /** Visit a node after its children have been traversed. */\n node?: (node: SpecQuery<C2>) => SpecQuery<C2>;\n /** Visit a join entry after its inner query has been traversed. */\n joinEntry?: (entry: SpecQueryJoinEntry<C2>) => SpecQueryJoinEntry<C2>;\n },\n): SpecQuery<C2> {\n const traverseEntry = (entry: SpecQueryJoinEntry<C1>): SpecQueryJoinEntry<C2> => {\n const traversed: SpecQueryJoinEntry<C2> = {\n ...entry,\n entry: traverseQuerySpec(entry.entry, visitor),\n };\n return visitor.joinEntry ? visitor.joinEntry(traversed) : traversed;\n };\n\n let result: SpecQuery<C2>;\n switch (query.type) {\n case \"column\":\n result = { type: \"column\", column: visitor.column(query.column) };\n break;\n case \"sparseToDenseColumn\":\n result = { ...query, column: visitor.column(query.column) };\n break;\n case \"inlineColumn\":\n result = query;\n break;\n case \"innerJoin\":\n case \"fullJoin\":\n result = { ...query, entries: query.entries.map(traverseEntry) };\n break;\n case \"outerJoin\":\n result = {\n ...query,\n primary: traverseEntry(query.primary),\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"linkerJoin\":\n result = {\n ...query,\n linker: { ...query.linker, column: visitor.column(query.linker.column) },\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"filter\":\n case \"sort\":\n case \"sliceAxes\":\n result = { ...query, input: traverseQuerySpec(query.input, visitor) };\n break;\n default:\n assertNever(query);\n }\n\n return visitor.node ? visitor.node(result) : result;\n}\n\n/** Recursively maps all column references in a SpecQuery tree. */\nexport function mapSpecQueryColumns<C1, C2>(\n query: SpecQuery<C1>,\n cb: (c: C1) => C2,\n): SpecQuery<C2> {\n return traverseQuerySpec(query, { column: cb });\n}\n\n/** Collects all column references from a SpecQuery tree. */\nexport function collectSpecQueryColumns<C>(query: SpecQuery<C>): C[] {\n const result: C[] = [];\n traverseQuerySpec(query, {\n column: (c: C) => {\n result.push(c);\n return c;\n },\n });\n return result;\n}\n\nexport function sortSpecQuery(query: SpecQuery): SpecQuery {\n return traverseQuerySpec(query, {\n column: (c) => c,\n node: (node) => {\n switch (node.type) {\n case \"sparseToDenseColumn\":\n return { ...node, axesIndices: node.axesIndices.toSorted((a, b) => a - b) };\n case \"innerJoin\":\n case \"fullJoin\": {\n const sorted = [...node.entries].sort(cmpQueryJoinEntrySpec);\n return { ...node, entries: sorted };\n }\n case \"outerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"linkerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"sliceAxes\":\n return {\n ...node,\n axisFilters: node.axisFilters.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axisSelector);\n const bk = canonicalizeJson(b.axisSelector);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n };\n default:\n return node;\n }\n },\n joinEntry: (entry) => ({\n ...entry,\n qualifications: entry.qualifications.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axis);\n const bk = canonicalizeJson(b.axis);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n }),\n });\n}\n\nfunction cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {\n if (lhs.type !== rhs.type) {\n return lhs.type < rhs.type ? -1 : 1;\n }\n switch (lhs.type) {\n case \"column\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"inlineColumn\":\n return lhs.spec.id < (rhs as typeof lhs).spec.id\n ? -1\n : lhs.spec.id === (rhs as typeof lhs).spec.id\n ? 0\n : 1;\n case \"sparseToDenseColumn\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"innerJoin\":\n case \"fullJoin\": {\n const rhsJoin = rhs as typeof lhs;\n if (lhs.entries.length !== rhsJoin.entries.length) {\n return lhs.entries.length - rhsJoin.entries.length;\n }\n for (let i = 0; i < lhs.entries.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.entries[i], rhsJoin.entries[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"outerJoin\": {\n const rhsOuter = rhs as typeof lhs;\n const cmp = cmpQueryJoinEntrySpec(lhs.primary, rhsOuter.primary);\n if (cmp !== 0) return cmp;\n if (lhs.secondary.length !== rhsOuter.secondary.length) {\n return lhs.secondary.length - rhsOuter.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsOuter.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"linkerJoin\": {\n const rhsLinker = rhs as typeof lhs;\n if (lhs.linker.column !== rhsLinker.linker.column) {\n return lhs.linker.column < rhsLinker.linker.column ? -1 : 1;\n }\n if (lhs.secondary.length !== rhsLinker.secondary.length) {\n return lhs.secondary.length - rhsLinker.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsLinker.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"sliceAxes\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"sort\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"filter\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n default:\n assertNever(lhs);\n }\n}\n\nfunction cmpQueryJoinEntrySpec(lhs: SpecQueryJoinEntry, rhs: SpecQueryJoinEntry): number {\n const cmp = cmpQuerySpec(lhs.entry, rhs.entry);\n if (cmp !== 0) return cmp;\n if (lhs.qualifications.length !== rhs.qualifications.length) {\n return lhs.qualifications.length - rhs.qualifications.length;\n }\n for (let i = 0; i < lhs.qualifications.length; i++) {\n const lhsQ = canonicalizeJson(lhs.qualifications[i]);\n const rhsQ = canonicalizeJson(rhs.qualifications[i]);\n if (lhsQ !== rhsQ) return lhsQ < rhsQ ? -1 : 1;\n }\n return 0;\n}\n"],"mappings":";;;AASA,MAAM,kBAAkB,IAAI,IAAwC;CAClE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAgB,oBAAoB,MAA+D;AACjG,QAAO,gBAAgB,IAErB,KAAK,KACN;;;;;;;;;;;;AAaH,SAAgB,kBACd,OACA,SAQe;CACf,MAAM,iBAAiB,UAA0D;EAC/E,MAAM,YAAoC;GACxC,GAAG;GACH,OAAO,kBAAkB,MAAM,OAAO,QAAQ;GAC/C;AACD,SAAO,QAAQ,YAAY,QAAQ,UAAU,UAAU,GAAG;;CAG5D,IAAI;AACJ,SAAQ,MAAM,MAAd;EACE,KAAK;AACH,YAAS;IAAE,MAAM;IAAU,QAAQ,QAAQ,OAAO,MAAM,OAAO;IAAE;AACjE;EACF,KAAK;AACH,YAAS;IAAE,GAAG;IAAO,QAAQ,QAAQ,OAAO,MAAM,OAAO;IAAE;AAC3D;EACF,KAAK;AACH,YAAS;AACT;EACF,KAAK;EACL,KAAK;AACH,YAAS;IAAE,GAAG;IAAO,SAAS,MAAM,QAAQ,IAAI,cAAc;IAAE;AAChE;EACF,KAAK;AACH,YAAS;IACP,GAAG;IACH,SAAS,cAAc,MAAM,QAAQ;IACrC,WAAW,MAAM,UAAU,IAAI,cAAc;IAC9C;AACD;EACF,KAAK;AACH,YAAS;IACP,GAAG;IACH,QAAQ;KAAE,GAAG,MAAM;KAAQ,QAAQ,QAAQ,OAAO,MAAM,OAAO,OAAO;KAAE;IACxE,WAAW,MAAM,UAAU,IAAI,cAAc;IAC9C;AACD;EACF,KAAK;EACL,KAAK;EACL,KAAK;AACH,YAAS;IAAE,GAAG;IAAO,OAAO,kBAAkB,MAAM,OAAO,QAAQ;IAAE;AACrE;EACF,QACE,cAAA,YAAY,MAAM;;AAGtB,QAAO,QAAQ,OAAO,QAAQ,KAAK,OAAO,GAAG;;;AAI/C,SAAgB,oBACd,OACA,IACe;AACf,QAAO,kBAAkB,OAAO,EAAE,QAAQ,IAAI,CAAC;;;AAIjD,SAAgB,wBAA2B,OAA0B;CACnE,MAAM,SAAc,EAAE;AACtB,mBAAkB,OAAO,EACvB,SAAS,MAAS;AAChB,SAAO,KAAK,EAAE;AACd,SAAO;IAEV,CAAC;AACF,QAAO;;AAGT,SAAgB,cAAc,OAA6B;AACzD,QAAO,kBAAkB,OAAO;EAC9B,SAAS,MAAM;EACf,OAAO,SAAS;AACd,WAAQ,KAAK,MAAb;IACE,KAAK,sBACH,QAAO;KAAE,GAAG;KAAM,aAAa,KAAK,YAAY,UAAU,GAAG,MAAM,IAAI,EAAE;KAAE;IAC7E,KAAK;IACL,KAAK,YAAY;KACf,MAAM,SAAS,CAAC,GAAG,KAAK,QAAQ,CAAC,KAAK,sBAAsB;AAC5D,YAAO;MAAE,GAAG;MAAM,SAAS;MAAQ;;IAErC,KAAK,aAAa;KAChB,MAAM,SAAS,CAAC,GAAG,KAAK,UAAU,CAAC,KAAK,sBAAsB;AAC9D,YAAO;MAAE,GAAG;MAAM,WAAW;MAAQ;;IAEvC,KAAK,cAAc;KACjB,MAAM,SAAS,CAAC,GAAG,KAAK,UAAU,CAAC,KAAK,sBAAsB;AAC9D,YAAO;MAAE,GAAG;MAAM,WAAW;MAAQ;;IAEvC,KAAK,YACH,QAAO;KACL,GAAG;KACH,aAAa,KAAK,YAAY,UAAU,GAAG,MAAM;MAC/C,MAAM,KAAKA,aAAAA,iBAAiB,EAAE,aAAa;MAC3C,MAAM,KAAKA,aAAAA,iBAAiB,EAAE,aAAa;AAC3C,aAAO,KAAK,KAAK,KAAK,OAAO,KAAK,IAAI;OACtC;KACH;IACH,QACE,QAAO;;;EAGb,YAAY,WAAW;GACrB,GAAG;GACH,gBAAgB,MAAM,eAAe,UAAU,GAAG,MAAM;IACtD,MAAM,KAAKA,aAAAA,iBAAiB,EAAE,KAAK;IACnC,MAAM,KAAKA,aAAAA,iBAAiB,EAAE,KAAK;AACnC,WAAO,KAAK,KAAK,KAAK,OAAO,KAAK,IAAI;KACtC;GACH;EACF,CAAC;;AAGJ,SAAS,aAAa,KAAgB,KAAwB;AAC5D,KAAI,IAAI,SAAS,IAAI,KACnB,QAAO,IAAI,OAAO,IAAI,OAAO,KAAK;AAEpC,SAAQ,IAAI,MAAZ;EACE,KAAK,SACH,QAAO,IAAI,SAAU,IAAmB,SACpC,KACA,IAAI,WAAY,IAAmB,SACjC,IACA;EACR,KAAK,eACH,QAAO,IAAI,KAAK,KAAM,IAAmB,KAAK,KAC1C,KACA,IAAI,KAAK,OAAQ,IAAmB,KAAK,KACvC,IACA;EACR,KAAK,sBACH,QAAO,IAAI,SAAU,IAAmB,SACpC,KACA,IAAI,WAAY,IAAmB,SACjC,IACA;EACR,KAAK;EACL,KAAK,YAAY;GACf,MAAM,UAAU;AAChB,OAAI,IAAI,QAAQ,WAAW,QAAQ,QAAQ,OACzC,QAAO,IAAI,QAAQ,SAAS,QAAQ,QAAQ;AAE9C,QAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,QAAQ,KAAK;IAC3C,MAAM,MAAM,sBAAsB,IAAI,QAAQ,IAAI,QAAQ,QAAQ,GAAG;AACrE,QAAI,QAAQ,EAAG,QAAO;;AAExB,UAAO;;EAET,KAAK,aAAa;GAChB,MAAM,WAAW;GACjB,MAAM,MAAM,sBAAsB,IAAI,SAAS,SAAS,QAAQ;AAChE,OAAI,QAAQ,EAAG,QAAO;AACtB,OAAI,IAAI,UAAU,WAAW,SAAS,UAAU,OAC9C,QAAO,IAAI,UAAU,SAAS,SAAS,UAAU;AAEnD,QAAK,IAAI,IAAI,GAAG,IAAI,IAAI,UAAU,QAAQ,KAAK;IAC7C,MAAM,MAAM,sBAAsB,IAAI,UAAU,IAAI,SAAS,UAAU,GAAG;AAC1E,QAAI,QAAQ,EAAG,QAAO;;AAExB,UAAO;;EAET,KAAK,cAAc;GACjB,MAAM,YAAY;AAClB,OAAI,IAAI,OAAO,WAAW,UAAU,OAAO,OACzC,QAAO,IAAI,OAAO,SAAS,UAAU,OAAO,SAAS,KAAK;AAE5D,OAAI,IAAI,UAAU,WAAW,UAAU,UAAU,OAC/C,QAAO,IAAI,UAAU,SAAS,UAAU,UAAU;AAEpD,QAAK,IAAI,IAAI,GAAG,IAAI,IAAI,UAAU,QAAQ,KAAK;IAC7C,MAAM,MAAM,sBAAsB,IAAI,UAAU,IAAI,UAAU,UAAU,GAAG;AAC3E,QAAI,QAAQ,EAAG,QAAO;;AAExB,UAAO;;EAET,KAAK,YACH,QAAO,aAAa,IAAI,OAAQ,IAAmB,MAAM;EAC3D,KAAK,OACH,QAAO,aAAa,IAAI,OAAQ,IAAmB,MAAM;EAC3D,KAAK,SACH,QAAO,aAAa,IAAI,OAAQ,IAAmB,MAAM;EAC3D,QACE,cAAA,YAAY,IAAI;;;AAItB,SAAS,sBAAsB,KAAyB,KAAiC;CACvF,MAAM,MAAM,aAAa,IAAI,OAAO,IAAI,MAAM;AAC9C,KAAI,QAAQ,EAAG,QAAO;AACtB,KAAI,IAAI,eAAe,WAAW,IAAI,eAAe,OACnD,QAAO,IAAI,eAAe,SAAS,IAAI,eAAe;AAExD,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,eAAe,QAAQ,KAAK;EAClD,MAAM,OAAOA,aAAAA,iBAAiB,IAAI,eAAe,GAAG;EACpD,MAAM,OAAOA,aAAAA,iBAAiB,IAAI,eAAe,GAAG;AACpD,MAAI,SAAS,KAAM,QAAO,OAAO,OAAO,KAAK;;AAE/C,QAAO"}
|
|
1
|
+
{"version":3,"file":"utils.cjs","names":["canonicalizeJson"],"sources":["../../../../src/drivers/pframe/query/utils.ts"],"sourcesContent":["import { canonicalizeJson } from \"../../../json\";\nimport { assertNever } from \"../../../util\";\nimport type {\n SpecQueryBooleanExpression,\n SpecQueryExpression,\n SpecQuery,\n SpecQueryJoinEntry,\n} from \"./query_spec\";\n\nconst booleanTypesSet = new Set<SpecQueryBooleanExpression[\"type\"]>([\n \"numericComparison\",\n \"stringEquals\",\n \"stringContains\",\n \"stringContainsFuzzy\",\n \"stringRegex\",\n \"isNull\",\n \"not\",\n \"and\",\n \"or\",\n \"isIn\",\n]);\n\nexport function isBooleanExpression(expr: SpecQueryExpression): expr is SpecQueryBooleanExpression {\n return booleanTypesSet.has(\n // @ts-expect-error -- TypeScript doesn't understand the discriminated union here, but we do at runtime\n expr.type,\n );\n}\n\n/**\n * Recursively traverses a SpecQuery tree bottom-up, applying visitor callbacks.\n *\n * Traversal order:\n * 1. Recurse into child queries\n * 2. Apply `column` to transform column references in leaf nodes\n * 3. Apply `joinEntry` to each join entry (with inner query already traversed)\n * 4. Assemble node with transformed children\n * 5. Apply `node` to the assembled node\n */\nexport function traverseQuerySpec<C1, C2>(\n query: SpecQuery<C1>,\n visitor: {\n /** Transform column references in leaf nodes (column, sparseToDenseColumn). */\n column: (c: C1) => C2;\n /** Visit a node after its children have been traversed. */\n node?: (node: SpecQuery<C2>) => SpecQuery<C2>;\n /** Visit a join entry after its inner query has been traversed. */\n joinEntry?: (entry: SpecQueryJoinEntry<C2>) => SpecQueryJoinEntry<C2>;\n },\n): SpecQuery<C2> {\n const traverseEntry = (entry: SpecQueryJoinEntry<C1>): SpecQueryJoinEntry<C2> => {\n const traversed: SpecQueryJoinEntry<C2> = {\n ...entry,\n entry: traverseQuerySpec(entry.entry, visitor),\n };\n return visitor.joinEntry ? visitor.joinEntry(traversed) : traversed;\n };\n\n let result: SpecQuery<C2>;\n switch (query.type) {\n case \"column\":\n result = { type: \"column\", column: visitor.column(query.column) };\n break;\n case \"sparseToDenseColumn\":\n result = { ...query, column: visitor.column(query.column) };\n break;\n case \"inlineColumn\":\n result = query;\n break;\n case \"innerJoin\":\n case \"fullJoin\":\n result = { ...query, entries: query.entries.map(traverseEntry) };\n break;\n case \"outerJoin\":\n result = {\n ...query,\n primary: traverseEntry(query.primary),\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"linkerJoin\":\n result = {\n ...query,\n linker: { ...query.linker, column: visitor.column(query.linker.column) },\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"filter\":\n case \"sort\":\n case \"sliceAxes\":\n result = { ...query, input: traverseQuerySpec(query.input, visitor) };\n break;\n default:\n assertNever(query);\n }\n\n return visitor.node ? visitor.node(result) : result;\n}\n\n/** Recursively maps all column references in a SpecQuery tree. */\nexport function mapSpecQueryColumns<C1, C2>(\n query: SpecQuery<C1>,\n cb: (c: C1) => C2,\n): SpecQuery<C2> {\n return traverseQuerySpec(query, { column: cb });\n}\n\n/** Collects all column references from a SpecQuery tree. */\nexport function collectSpecQueryColumns<C>(query: SpecQuery<C>): C[] {\n const result: C[] = [];\n traverseQuerySpec(query, {\n column: (c: C) => {\n result.push(c);\n return c;\n },\n });\n return result;\n}\n\nexport function sortSpecQuery(query: SpecQuery): SpecQuery {\n return traverseQuerySpec(query, {\n column: (c) => c,\n node: (node) => {\n switch (node.type) {\n case \"sparseToDenseColumn\":\n return { ...node, axesIndices: node.axesIndices.toSorted((a, b) => a - b) };\n case \"innerJoin\":\n case \"fullJoin\": {\n const sorted = [...node.entries].sort(cmpQueryJoinEntrySpec);\n return { ...node, entries: sorted };\n }\n case \"outerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"linkerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"sliceAxes\":\n return {\n ...node,\n axisFilters: node.axisFilters.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axisSelector);\n const bk = canonicalizeJson(b.axisSelector);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n };\n default:\n return node;\n }\n },\n joinEntry: (entry) => {\n // Preserve absence of `qualifications` (absent == `[]`) so canonical\n // output is stable across both input forms.\n if (entry.qualifications === undefined || entry.qualifications.length === 0) {\n return entry;\n }\n return {\n ...entry,\n qualifications: entry.qualifications.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axis);\n const bk = canonicalizeJson(b.axis);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n };\n },\n });\n}\n\nfunction cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {\n if (lhs.type !== rhs.type) {\n return lhs.type < rhs.type ? -1 : 1;\n }\n switch (lhs.type) {\n case \"column\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"inlineColumn\":\n return lhs.spec.columnId < (rhs as typeof lhs).spec.columnId\n ? -1\n : lhs.spec.columnId === (rhs as typeof lhs).spec.columnId\n ? 0\n : 1;\n case \"sparseToDenseColumn\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"innerJoin\":\n case \"fullJoin\": {\n const rhsJoin = rhs as typeof lhs;\n if (lhs.entries.length !== rhsJoin.entries.length) {\n return lhs.entries.length - rhsJoin.entries.length;\n }\n for (let i = 0; i < lhs.entries.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.entries[i], rhsJoin.entries[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"outerJoin\": {\n const rhsOuter = rhs as typeof lhs;\n const cmp = cmpQueryJoinEntrySpec(lhs.primary, rhsOuter.primary);\n if (cmp !== 0) return cmp;\n if (lhs.secondary.length !== rhsOuter.secondary.length) {\n return lhs.secondary.length - rhsOuter.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsOuter.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"linkerJoin\": {\n const rhsLinker = rhs as typeof lhs;\n if (lhs.linker.column !== rhsLinker.linker.column) {\n return lhs.linker.column < rhsLinker.linker.column ? -1 : 1;\n }\n if (lhs.secondary.length !== rhsLinker.secondary.length) {\n return lhs.secondary.length - rhsLinker.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsLinker.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"sliceAxes\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"sort\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"filter\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n default:\n assertNever(lhs);\n }\n}\n\nfunction cmpQueryJoinEntrySpec(lhs: SpecQueryJoinEntry, rhs: SpecQueryJoinEntry): number {\n const cmp = cmpQuerySpec(lhs.entry, rhs.entry);\n if (cmp !== 0) return cmp;\n // Absent `qualifications` is equivalent to an empty list.\n const lhsQ = lhs.qualifications ?? [];\n const rhsQ = rhs.qualifications ?? [];\n if (lhsQ.length !== rhsQ.length) {\n return lhsQ.length - rhsQ.length;\n }\n for (let i = 0; i < lhsQ.length; i++) {\n const l = canonicalizeJson(lhsQ[i]);\n const r = canonicalizeJson(rhsQ[i]);\n if (l !== r) return l < r ? -1 : 1;\n }\n return 0;\n}\n"],"mappings":"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"}
|
|
@@ -142,21 +142,24 @@ function sortSpecQuery(query) {
|
|
|
142
142
|
default: return node;
|
|
143
143
|
}
|
|
144
144
|
},
|
|
145
|
-
joinEntry: (entry) =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
joinEntry: (entry) => {
|
|
146
|
+
if (entry.qualifications === void 0 || entry.qualifications.length === 0) return entry;
|
|
147
|
+
return {
|
|
148
|
+
...entry,
|
|
149
|
+
qualifications: entry.qualifications.toSorted((a, b) => {
|
|
150
|
+
const ak = canonicalizeJson(a.axis);
|
|
151
|
+
const bk = canonicalizeJson(b.axis);
|
|
152
|
+
return ak < bk ? -1 : ak === bk ? 0 : 1;
|
|
153
|
+
})
|
|
154
|
+
};
|
|
155
|
+
}
|
|
153
156
|
});
|
|
154
157
|
}
|
|
155
158
|
function cmpQuerySpec(lhs, rhs) {
|
|
156
159
|
if (lhs.type !== rhs.type) return lhs.type < rhs.type ? -1 : 1;
|
|
157
160
|
switch (lhs.type) {
|
|
158
161
|
case "column": return lhs.column < rhs.column ? -1 : lhs.column === rhs.column ? 0 : 1;
|
|
159
|
-
case "inlineColumn": return lhs.spec.
|
|
162
|
+
case "inlineColumn": return lhs.spec.columnId < rhs.spec.columnId ? -1 : lhs.spec.columnId === rhs.spec.columnId ? 0 : 1;
|
|
160
163
|
case "sparseToDenseColumn": return lhs.column < rhs.column ? -1 : lhs.column === rhs.column ? 0 : 1;
|
|
161
164
|
case "innerJoin":
|
|
162
165
|
case "fullJoin": {
|
|
@@ -198,11 +201,13 @@ function cmpQuerySpec(lhs, rhs) {
|
|
|
198
201
|
function cmpQueryJoinEntrySpec(lhs, rhs) {
|
|
199
202
|
const cmp = cmpQuerySpec(lhs.entry, rhs.entry);
|
|
200
203
|
if (cmp !== 0) return cmp;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
const lhsQ = lhs.qualifications ?? [];
|
|
205
|
+
const rhsQ = rhs.qualifications ?? [];
|
|
206
|
+
if (lhsQ.length !== rhsQ.length) return lhsQ.length - rhsQ.length;
|
|
207
|
+
for (let i = 0; i < lhsQ.length; i++) {
|
|
208
|
+
const l = canonicalizeJson(lhsQ[i]);
|
|
209
|
+
const r = canonicalizeJson(rhsQ[i]);
|
|
210
|
+
if (l !== r) return l < r ? -1 : 1;
|
|
206
211
|
}
|
|
207
212
|
return 0;
|
|
208
213
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../../../src/drivers/pframe/query/utils.ts"],"sourcesContent":["import { canonicalizeJson } from \"../../../json\";\nimport { assertNever } from \"../../../util\";\nimport type {\n SpecQueryBooleanExpression,\n SpecQueryExpression,\n SpecQuery,\n SpecQueryJoinEntry,\n} from \"./query_spec\";\n\nconst booleanTypesSet = new Set<SpecQueryBooleanExpression[\"type\"]>([\n \"numericComparison\",\n \"stringEquals\",\n \"stringContains\",\n \"stringContainsFuzzy\",\n \"stringRegex\",\n \"isNull\",\n \"not\",\n \"and\",\n \"or\",\n \"isIn\",\n]);\n\nexport function isBooleanExpression(expr: SpecQueryExpression): expr is SpecQueryBooleanExpression {\n return booleanTypesSet.has(\n // @ts-expect-error -- TypeScript doesn't understand the discriminated union here, but we do at runtime\n expr.type,\n );\n}\n\n/**\n * Recursively traverses a SpecQuery tree bottom-up, applying visitor callbacks.\n *\n * Traversal order:\n * 1. Recurse into child queries\n * 2. Apply `column` to transform column references in leaf nodes\n * 3. Apply `joinEntry` to each join entry (with inner query already traversed)\n * 4. Assemble node with transformed children\n * 5. Apply `node` to the assembled node\n */\nexport function traverseQuerySpec<C1, C2>(\n query: SpecQuery<C1>,\n visitor: {\n /** Transform column references in leaf nodes (column, sparseToDenseColumn). */\n column: (c: C1) => C2;\n /** Visit a node after its children have been traversed. */\n node?: (node: SpecQuery<C2>) => SpecQuery<C2>;\n /** Visit a join entry after its inner query has been traversed. */\n joinEntry?: (entry: SpecQueryJoinEntry<C2>) => SpecQueryJoinEntry<C2>;\n },\n): SpecQuery<C2> {\n const traverseEntry = (entry: SpecQueryJoinEntry<C1>): SpecQueryJoinEntry<C2> => {\n const traversed: SpecQueryJoinEntry<C2> = {\n ...entry,\n entry: traverseQuerySpec(entry.entry, visitor),\n };\n return visitor.joinEntry ? visitor.joinEntry(traversed) : traversed;\n };\n\n let result: SpecQuery<C2>;\n switch (query.type) {\n case \"column\":\n result = { type: \"column\", column: visitor.column(query.column) };\n break;\n case \"sparseToDenseColumn\":\n result = { ...query, column: visitor.column(query.column) };\n break;\n case \"inlineColumn\":\n result = query;\n break;\n case \"innerJoin\":\n case \"fullJoin\":\n result = { ...query, entries: query.entries.map(traverseEntry) };\n break;\n case \"outerJoin\":\n result = {\n ...query,\n primary: traverseEntry(query.primary),\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"linkerJoin\":\n result = {\n ...query,\n linker: { ...query.linker, column: visitor.column(query.linker.column) },\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"filter\":\n case \"sort\":\n case \"sliceAxes\":\n result = { ...query, input: traverseQuerySpec(query.input, visitor) };\n break;\n default:\n assertNever(query);\n }\n\n return visitor.node ? visitor.node(result) : result;\n}\n\n/** Recursively maps all column references in a SpecQuery tree. */\nexport function mapSpecQueryColumns<C1, C2>(\n query: SpecQuery<C1>,\n cb: (c: C1) => C2,\n): SpecQuery<C2> {\n return traverseQuerySpec(query, { column: cb });\n}\n\n/** Collects all column references from a SpecQuery tree. */\nexport function collectSpecQueryColumns<C>(query: SpecQuery<C>): C[] {\n const result: C[] = [];\n traverseQuerySpec(query, {\n column: (c: C) => {\n result.push(c);\n return c;\n },\n });\n return result;\n}\n\nexport function sortSpecQuery(query: SpecQuery): SpecQuery {\n return traverseQuerySpec(query, {\n column: (c) => c,\n node: (node) => {\n switch (node.type) {\n case \"sparseToDenseColumn\":\n return { ...node, axesIndices: node.axesIndices.toSorted((a, b) => a - b) };\n case \"innerJoin\":\n case \"fullJoin\": {\n const sorted = [...node.entries].sort(cmpQueryJoinEntrySpec);\n return { ...node, entries: sorted };\n }\n case \"outerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"linkerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"sliceAxes\":\n return {\n ...node,\n axisFilters: node.axisFilters.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axisSelector);\n const bk = canonicalizeJson(b.axisSelector);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n };\n default:\n return node;\n }\n },\n joinEntry: (entry) => ({\n ...entry,\n qualifications: entry.qualifications.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axis);\n const bk = canonicalizeJson(b.axis);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n }),\n });\n}\n\nfunction cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {\n if (lhs.type !== rhs.type) {\n return lhs.type < rhs.type ? -1 : 1;\n }\n switch (lhs.type) {\n case \"column\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"inlineColumn\":\n return lhs.spec.id < (rhs as typeof lhs).spec.id\n ? -1\n : lhs.spec.id === (rhs as typeof lhs).spec.id\n ? 0\n : 1;\n case \"sparseToDenseColumn\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"innerJoin\":\n case \"fullJoin\": {\n const rhsJoin = rhs as typeof lhs;\n if (lhs.entries.length !== rhsJoin.entries.length) {\n return lhs.entries.length - rhsJoin.entries.length;\n }\n for (let i = 0; i < lhs.entries.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.entries[i], rhsJoin.entries[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"outerJoin\": {\n const rhsOuter = rhs as typeof lhs;\n const cmp = cmpQueryJoinEntrySpec(lhs.primary, rhsOuter.primary);\n if (cmp !== 0) return cmp;\n if (lhs.secondary.length !== rhsOuter.secondary.length) {\n return lhs.secondary.length - rhsOuter.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsOuter.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"linkerJoin\": {\n const rhsLinker = rhs as typeof lhs;\n if (lhs.linker.column !== rhsLinker.linker.column) {\n return lhs.linker.column < rhsLinker.linker.column ? -1 : 1;\n }\n if (lhs.secondary.length !== rhsLinker.secondary.length) {\n return lhs.secondary.length - rhsLinker.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsLinker.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"sliceAxes\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"sort\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"filter\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n default:\n assertNever(lhs);\n }\n}\n\nfunction cmpQueryJoinEntrySpec(lhs: SpecQueryJoinEntry, rhs: SpecQueryJoinEntry): number {\n const cmp = cmpQuerySpec(lhs.entry, rhs.entry);\n if (cmp !== 0) return cmp;\n if (lhs.qualifications.length !== rhs.qualifications.length) {\n return lhs.qualifications.length - rhs.qualifications.length;\n }\n for (let i = 0; i < lhs.qualifications.length; i++) {\n const lhsQ = canonicalizeJson(lhs.qualifications[i]);\n const rhsQ = canonicalizeJson(rhs.qualifications[i]);\n if (lhsQ !== rhsQ) return lhsQ < rhsQ ? -1 : 1;\n }\n return 0;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../../../src/drivers/pframe/query/utils.ts"],"sourcesContent":["import { canonicalizeJson } from \"../../../json\";\nimport { assertNever } from \"../../../util\";\nimport type {\n SpecQueryBooleanExpression,\n SpecQueryExpression,\n SpecQuery,\n SpecQueryJoinEntry,\n} from \"./query_spec\";\n\nconst booleanTypesSet = new Set<SpecQueryBooleanExpression[\"type\"]>([\n \"numericComparison\",\n \"stringEquals\",\n \"stringContains\",\n \"stringContainsFuzzy\",\n \"stringRegex\",\n \"isNull\",\n \"not\",\n \"and\",\n \"or\",\n \"isIn\",\n]);\n\nexport function isBooleanExpression(expr: SpecQueryExpression): expr is SpecQueryBooleanExpression {\n return booleanTypesSet.has(\n // @ts-expect-error -- TypeScript doesn't understand the discriminated union here, but we do at runtime\n expr.type,\n );\n}\n\n/**\n * Recursively traverses a SpecQuery tree bottom-up, applying visitor callbacks.\n *\n * Traversal order:\n * 1. Recurse into child queries\n * 2. Apply `column` to transform column references in leaf nodes\n * 3. Apply `joinEntry` to each join entry (with inner query already traversed)\n * 4. Assemble node with transformed children\n * 5. Apply `node` to the assembled node\n */\nexport function traverseQuerySpec<C1, C2>(\n query: SpecQuery<C1>,\n visitor: {\n /** Transform column references in leaf nodes (column, sparseToDenseColumn). */\n column: (c: C1) => C2;\n /** Visit a node after its children have been traversed. */\n node?: (node: SpecQuery<C2>) => SpecQuery<C2>;\n /** Visit a join entry after its inner query has been traversed. */\n joinEntry?: (entry: SpecQueryJoinEntry<C2>) => SpecQueryJoinEntry<C2>;\n },\n): SpecQuery<C2> {\n const traverseEntry = (entry: SpecQueryJoinEntry<C1>): SpecQueryJoinEntry<C2> => {\n const traversed: SpecQueryJoinEntry<C2> = {\n ...entry,\n entry: traverseQuerySpec(entry.entry, visitor),\n };\n return visitor.joinEntry ? visitor.joinEntry(traversed) : traversed;\n };\n\n let result: SpecQuery<C2>;\n switch (query.type) {\n case \"column\":\n result = { type: \"column\", column: visitor.column(query.column) };\n break;\n case \"sparseToDenseColumn\":\n result = { ...query, column: visitor.column(query.column) };\n break;\n case \"inlineColumn\":\n result = query;\n break;\n case \"innerJoin\":\n case \"fullJoin\":\n result = { ...query, entries: query.entries.map(traverseEntry) };\n break;\n case \"outerJoin\":\n result = {\n ...query,\n primary: traverseEntry(query.primary),\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"linkerJoin\":\n result = {\n ...query,\n linker: { ...query.linker, column: visitor.column(query.linker.column) },\n secondary: query.secondary.map(traverseEntry),\n };\n break;\n case \"filter\":\n case \"sort\":\n case \"sliceAxes\":\n result = { ...query, input: traverseQuerySpec(query.input, visitor) };\n break;\n default:\n assertNever(query);\n }\n\n return visitor.node ? visitor.node(result) : result;\n}\n\n/** Recursively maps all column references in a SpecQuery tree. */\nexport function mapSpecQueryColumns<C1, C2>(\n query: SpecQuery<C1>,\n cb: (c: C1) => C2,\n): SpecQuery<C2> {\n return traverseQuerySpec(query, { column: cb });\n}\n\n/** Collects all column references from a SpecQuery tree. */\nexport function collectSpecQueryColumns<C>(query: SpecQuery<C>): C[] {\n const result: C[] = [];\n traverseQuerySpec(query, {\n column: (c: C) => {\n result.push(c);\n return c;\n },\n });\n return result;\n}\n\nexport function sortSpecQuery(query: SpecQuery): SpecQuery {\n return traverseQuerySpec(query, {\n column: (c) => c,\n node: (node) => {\n switch (node.type) {\n case \"sparseToDenseColumn\":\n return { ...node, axesIndices: node.axesIndices.toSorted((a, b) => a - b) };\n case \"innerJoin\":\n case \"fullJoin\": {\n const sorted = [...node.entries].sort(cmpQueryJoinEntrySpec);\n return { ...node, entries: sorted };\n }\n case \"outerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"linkerJoin\": {\n const sorted = [...node.secondary].sort(cmpQueryJoinEntrySpec);\n return { ...node, secondary: sorted };\n }\n case \"sliceAxes\":\n return {\n ...node,\n axisFilters: node.axisFilters.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axisSelector);\n const bk = canonicalizeJson(b.axisSelector);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n };\n default:\n return node;\n }\n },\n joinEntry: (entry) => {\n // Preserve absence of `qualifications` (absent == `[]`) so canonical\n // output is stable across both input forms.\n if (entry.qualifications === undefined || entry.qualifications.length === 0) {\n return entry;\n }\n return {\n ...entry,\n qualifications: entry.qualifications.toSorted((a, b) => {\n const ak = canonicalizeJson(a.axis);\n const bk = canonicalizeJson(b.axis);\n return ak < bk ? -1 : ak === bk ? 0 : 1;\n }),\n };\n },\n });\n}\n\nfunction cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {\n if (lhs.type !== rhs.type) {\n return lhs.type < rhs.type ? -1 : 1;\n }\n switch (lhs.type) {\n case \"column\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"inlineColumn\":\n return lhs.spec.columnId < (rhs as typeof lhs).spec.columnId\n ? -1\n : lhs.spec.columnId === (rhs as typeof lhs).spec.columnId\n ? 0\n : 1;\n case \"sparseToDenseColumn\":\n return lhs.column < (rhs as typeof lhs).column\n ? -1\n : lhs.column === (rhs as typeof lhs).column\n ? 0\n : 1;\n case \"innerJoin\":\n case \"fullJoin\": {\n const rhsJoin = rhs as typeof lhs;\n if (lhs.entries.length !== rhsJoin.entries.length) {\n return lhs.entries.length - rhsJoin.entries.length;\n }\n for (let i = 0; i < lhs.entries.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.entries[i], rhsJoin.entries[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"outerJoin\": {\n const rhsOuter = rhs as typeof lhs;\n const cmp = cmpQueryJoinEntrySpec(lhs.primary, rhsOuter.primary);\n if (cmp !== 0) return cmp;\n if (lhs.secondary.length !== rhsOuter.secondary.length) {\n return lhs.secondary.length - rhsOuter.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsOuter.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"linkerJoin\": {\n const rhsLinker = rhs as typeof lhs;\n if (lhs.linker.column !== rhsLinker.linker.column) {\n return lhs.linker.column < rhsLinker.linker.column ? -1 : 1;\n }\n if (lhs.secondary.length !== rhsLinker.secondary.length) {\n return lhs.secondary.length - rhsLinker.secondary.length;\n }\n for (let i = 0; i < lhs.secondary.length; i++) {\n const cmp = cmpQueryJoinEntrySpec(lhs.secondary[i], rhsLinker.secondary[i]);\n if (cmp !== 0) return cmp;\n }\n return 0;\n }\n case \"sliceAxes\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"sort\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n case \"filter\":\n return cmpQuerySpec(lhs.input, (rhs as typeof lhs).input);\n default:\n assertNever(lhs);\n }\n}\n\nfunction cmpQueryJoinEntrySpec(lhs: SpecQueryJoinEntry, rhs: SpecQueryJoinEntry): number {\n const cmp = cmpQuerySpec(lhs.entry, rhs.entry);\n if (cmp !== 0) return cmp;\n // Absent `qualifications` is equivalent to an empty list.\n const lhsQ = lhs.qualifications ?? [];\n const rhsQ = rhs.qualifications ?? [];\n if (lhsQ.length !== rhsQ.length) {\n return lhsQ.length - rhsQ.length;\n }\n for (let i = 0; i < lhsQ.length; i++) {\n const l = canonicalizeJson(lhsQ[i]);\n const r = canonicalizeJson(rhsQ[i]);\n if (l !== r) return l < r ? -1 : 1;\n }\n return 0;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-model-common",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.2",
|
|
4
4
|
"description": "Platforma SDK Model",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist/**/*",
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
"canonicalize": "~2.1.0",
|
|
21
21
|
"zod": "~3.25.76",
|
|
22
22
|
"@milaboratories/helpers": "1.14.1",
|
|
23
|
-
"@milaboratories/pl-error-like": "1.12.
|
|
23
|
+
"@milaboratories/pl-error-like": "1.12.10"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@vitest/coverage-istanbul": "^4.1.3",
|
|
27
27
|
"typescript": "~5.9.3",
|
|
28
28
|
"vitest": "^4.1.3",
|
|
29
29
|
"@milaboratories/build-configs": "2.0.0",
|
|
30
|
-
"@milaboratories/ts-
|
|
31
|
-
"@milaboratories/ts-
|
|
30
|
+
"@milaboratories/ts-builder": "1.3.2",
|
|
31
|
+
"@milaboratories/ts-configs": "1.2.3"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "ts-builder build --target node",
|
|
@@ -632,23 +632,31 @@ export interface QueryOuterJoin<JE extends QueryJoinEntry<unknown>> {
|
|
|
632
632
|
* @template A - Axis selector type
|
|
633
633
|
*
|
|
634
634
|
* @example
|
|
635
|
-
* //
|
|
635
|
+
* // Spec layer: axisSelector is a SingleAxisSelector.
|
|
636
636
|
* {
|
|
637
637
|
* type: 'sliceAxes',
|
|
638
638
|
* input: fullDataQuery,
|
|
639
639
|
* axisFilters: [
|
|
640
|
-
* {
|
|
641
|
-
* {
|
|
640
|
+
* { axisSelector: { name: 'sample' }, constant: 'Sample1' },
|
|
641
|
+
* { axisSelector: { name: 'condition' }, constant: 'Treatment' }
|
|
642
642
|
* ]
|
|
643
643
|
* }
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* // Data layer: axisSelector is the axis index.
|
|
647
|
+
* {
|
|
648
|
+
* type: 'sliceAxes',
|
|
649
|
+
* input: fullDataQuery,
|
|
650
|
+
* axisFilters: [{ axisSelector: 0, constant: 'Sample1' }]
|
|
651
|
+
* }
|
|
644
652
|
*/
|
|
645
|
-
export interface QuerySliceAxes<Q, A
|
|
653
|
+
export interface QuerySliceAxes<Q, A> {
|
|
646
654
|
type: "sliceAxes";
|
|
647
655
|
/** Input query to slice */
|
|
648
656
|
input: Q;
|
|
649
657
|
/** List of axis filters to apply (at least one required) */
|
|
650
658
|
axisFilters: {
|
|
651
|
-
/**
|
|
659
|
+
/** Axis to filter. `SingleAxisSelector` at the spec layer; axis index at the data layer. */
|
|
652
660
|
axisSelector: A;
|
|
653
661
|
/** The constant value to filter the axis to */
|
|
654
662
|
constant: string | number;
|
|
@@ -15,7 +15,6 @@ import type {
|
|
|
15
15
|
ExprStringEquals,
|
|
16
16
|
ExprStringRegex,
|
|
17
17
|
ExprNumericUnary,
|
|
18
|
-
QueryAxisSelector,
|
|
19
18
|
QueryColumn,
|
|
20
19
|
QuerySparseToDenseColumn,
|
|
21
20
|
QueryFilter,
|
|
@@ -40,7 +39,7 @@ type ColumnIdAndTypeSpec = {
|
|
|
40
39
|
/** Unique identifier of the column */
|
|
41
40
|
id: PObjectId;
|
|
42
41
|
/** Type specification defining axes and column types */
|
|
43
|
-
|
|
42
|
+
spec: TypeSpec;
|
|
44
43
|
};
|
|
45
44
|
|
|
46
45
|
/**
|
|
@@ -89,7 +88,7 @@ export type DataQueryLinkerJoinLinker = {
|
|
|
89
88
|
/** @see QueryLinkerJoin */
|
|
90
89
|
export type DataQueryLinkerJoin = QueryLinkerJoin<DataQueryLinkerJoinLinker, DataQueryJoinEntry>;
|
|
91
90
|
/** @see QuerySliceAxes */
|
|
92
|
-
export type DataQuerySliceAxes = QuerySliceAxes<DataQuery,
|
|
91
|
+
export type DataQuerySliceAxes = QuerySliceAxes<DataQuery, number>;
|
|
93
92
|
/** @see QuerySort */
|
|
94
93
|
export type DataQuerySort = QuerySort<DataQuery, DataQueryExpression>;
|
|
95
94
|
/** @see QueryFilter */
|
|
@@ -15,7 +15,6 @@ import type {
|
|
|
15
15
|
ExprStringEquals,
|
|
16
16
|
ExprStringRegex,
|
|
17
17
|
ExprNumericUnary,
|
|
18
|
-
QueryAxisSelector,
|
|
19
18
|
QueryColumn,
|
|
20
19
|
QuerySparseToDenseColumn,
|
|
21
20
|
QueryFilter,
|
|
@@ -28,43 +27,23 @@ import type {
|
|
|
28
27
|
QuerySymmetricJoin,
|
|
29
28
|
InferBooleanExpressionUnion,
|
|
30
29
|
} from "./query_common";
|
|
31
|
-
import type { Domain, PColumnIdAndSpec,
|
|
30
|
+
import type { Domain, PColumnIdAndSpec, SingleAxisSelector } from "../spec";
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* Pairs a column ID with its full PColumnSpec.
|
|
37
|
-
* Used in spec layer to carry column specification alongside references.
|
|
38
|
-
*/
|
|
39
|
-
type ColumnIdAndSpec = {
|
|
40
|
-
/** Unique identifier of the column */
|
|
41
|
-
id: PObjectId;
|
|
42
|
-
/** Full column specification including axes and value type */
|
|
43
|
-
spec: PColumnSpec;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Join entry for spec layer queries.
|
|
48
|
-
*
|
|
49
|
-
* Extends the base join entry with axis qualifications.
|
|
50
|
-
* Qualifications specify additional domain constraints for each axis,
|
|
51
|
-
* enabling more precise control over how columns are joined.
|
|
33
|
+
* Join entry for spec-layer queries — the base join entry extended with
|
|
34
|
+
* per-axis domain constraints. Absent `qualifications` is equivalent to `[]`.
|
|
52
35
|
*
|
|
53
36
|
* @example
|
|
54
|
-
* // Join entry with axis qualifications
|
|
55
37
|
* {
|
|
56
38
|
* entry: querySpec,
|
|
57
|
-
* qualifications: [
|
|
58
|
-
* { axis: { name: 'sample' }, contextDomain: { ... } }
|
|
59
|
-
* ]
|
|
39
|
+
* qualifications: [{ axis: { name: 'sample' }, contextDomain: { ... } }]
|
|
60
40
|
* }
|
|
61
41
|
*/
|
|
62
42
|
export type SpecQueryJoinEntry<C = PObjectId> = QueryJoinEntry<SpecQuery<C>> & {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
/** Axis selector identifying which axis to qualify */
|
|
43
|
+
qualifications?: {
|
|
44
|
+
/** Axis to qualify. */
|
|
66
45
|
axis: SingleAxisSelector;
|
|
67
|
-
/** Additional domain constraints for this axis */
|
|
46
|
+
/** Additional domain constraints for this axis. */
|
|
68
47
|
contextDomain: Domain;
|
|
69
48
|
}[];
|
|
70
49
|
};
|
|
@@ -72,7 +51,7 @@ export type SpecQueryJoinEntry<C = PObjectId> = QueryJoinEntry<SpecQuery<C>> & {
|
|
|
72
51
|
/** @see QueryColumn */
|
|
73
52
|
export type SpecQueryColumn<C = PObjectId> = QueryColumn<C>;
|
|
74
53
|
/** @see QueryInlineColumn */
|
|
75
|
-
export type SpecQueryInlineColumn = QueryInlineColumn<
|
|
54
|
+
export type SpecQueryInlineColumn = QueryInlineColumn<PColumnIdAndSpec>;
|
|
76
55
|
/** @see QuerySparseToDenseColumn */
|
|
77
56
|
export type SpecQuerySparseToDenseColumn<C = PObjectId> = QuerySparseToDenseColumn<
|
|
78
57
|
C,
|
|
@@ -98,10 +77,7 @@ export type SpecQueryLinkerJoin<C = PObjectId> = QueryLinkerJoin<
|
|
|
98
77
|
SpecQueryJoinEntry<C>
|
|
99
78
|
>;
|
|
100
79
|
/** @see QuerySliceAxes */
|
|
101
|
-
export type SpecQuerySliceAxes<C = PObjectId> = QuerySliceAxes<
|
|
102
|
-
SpecQuery<C>,
|
|
103
|
-
QueryAxisSelector<SingleAxisSelector>
|
|
104
|
-
>;
|
|
80
|
+
export type SpecQuerySliceAxes<C = PObjectId> = QuerySliceAxes<SpecQuery<C>, SingleAxisSelector>;
|
|
105
81
|
/** @see QuerySort */
|
|
106
82
|
export type SpecQuerySort<C = PObjectId> = QuerySort<SpecQuery<C>, SpecQueryExpression>;
|
|
107
83
|
/** @see QueryFilter */
|
|
@@ -150,14 +150,21 @@ export function sortSpecQuery(query: SpecQuery): SpecQuery {
|
|
|
150
150
|
return node;
|
|
151
151
|
}
|
|
152
152
|
},
|
|
153
|
-
joinEntry: (entry) =>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
joinEntry: (entry) => {
|
|
154
|
+
// Preserve absence of `qualifications` (absent == `[]`) so canonical
|
|
155
|
+
// output is stable across both input forms.
|
|
156
|
+
if (entry.qualifications === undefined || entry.qualifications.length === 0) {
|
|
157
|
+
return entry;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
...entry,
|
|
161
|
+
qualifications: entry.qualifications.toSorted((a, b) => {
|
|
162
|
+
const ak = canonicalizeJson(a.axis);
|
|
163
|
+
const bk = canonicalizeJson(b.axis);
|
|
164
|
+
return ak < bk ? -1 : ak === bk ? 0 : 1;
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
},
|
|
161
168
|
});
|
|
162
169
|
}
|
|
163
170
|
|
|
@@ -173,9 +180,9 @@ function cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {
|
|
|
173
180
|
? 0
|
|
174
181
|
: 1;
|
|
175
182
|
case "inlineColumn":
|
|
176
|
-
return lhs.spec.
|
|
183
|
+
return lhs.spec.columnId < (rhs as typeof lhs).spec.columnId
|
|
177
184
|
? -1
|
|
178
|
-
: lhs.spec.
|
|
185
|
+
: lhs.spec.columnId === (rhs as typeof lhs).spec.columnId
|
|
179
186
|
? 0
|
|
180
187
|
: 1;
|
|
181
188
|
case "sparseToDenseColumn":
|
|
@@ -237,13 +244,16 @@ function cmpQuerySpec(lhs: SpecQuery, rhs: SpecQuery): number {
|
|
|
237
244
|
function cmpQueryJoinEntrySpec(lhs: SpecQueryJoinEntry, rhs: SpecQueryJoinEntry): number {
|
|
238
245
|
const cmp = cmpQuerySpec(lhs.entry, rhs.entry);
|
|
239
246
|
if (cmp !== 0) return cmp;
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
// Absent `qualifications` is equivalent to an empty list.
|
|
248
|
+
const lhsQ = lhs.qualifications ?? [];
|
|
249
|
+
const rhsQ = rhs.qualifications ?? [];
|
|
250
|
+
if (lhsQ.length !== rhsQ.length) {
|
|
251
|
+
return lhsQ.length - rhsQ.length;
|
|
242
252
|
}
|
|
243
|
-
for (let i = 0; i <
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
253
|
+
for (let i = 0; i < lhsQ.length; i++) {
|
|
254
|
+
const l = canonicalizeJson(lhsQ[i]);
|
|
255
|
+
const r = canonicalizeJson(rhsQ[i]);
|
|
256
|
+
if (l !== r) return l < r ? -1 : 1;
|
|
247
257
|
}
|
|
248
258
|
return 0;
|
|
249
259
|
}
|