@noy-db/hub 0.1.0-pre.8 → 0.2.0-pre.1
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/aggregate/index.cjs +91 -36
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +2 -2
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/aggregate/index.js +16 -9
- package/dist/aggregate/index.js.map +1 -1
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +6 -6
- package/dist/blobs/index.d.ts +6 -6
- package/dist/blobs/index.js +4 -4
- package/dist/bundle/index.cjs +298 -7
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +6 -6
- package/dist/bundle/index.d.ts +6 -6
- package/dist/bundle/index.js +15 -4
- package/dist/{chunk-GOUT6DND.js → chunk-23TTQXVO.js} +173 -91
- package/dist/chunk-23TTQXVO.js.map +1 -0
- package/dist/{chunk-CIMZBAZB.js → chunk-2AXFIYHT.js} +1 -1
- package/dist/chunk-2AXFIYHT.js.map +1 -0
- package/dist/chunk-34YSDCDP.js +73 -0
- package/dist/chunk-34YSDCDP.js.map +1 -0
- package/dist/{chunk-HC7Z5EQZ.js → chunk-4TFSM22V.js} +4 -4
- package/dist/{chunk-7XBQS42M.js → chunk-537VFZTR.js} +4 -4
- package/dist/{chunk-M62XNWRA.js → chunk-5DWL3JBF.js} +2 -2
- package/dist/{chunk-RSPLI376.js → chunk-5SCJ5UEF.js} +3 -3
- package/dist/chunk-5ZGZ6HIZ.js +100 -0
- package/dist/chunk-5ZGZ6HIZ.js.map +1 -0
- package/dist/chunk-6HPZY4ON.js +291 -0
- package/dist/chunk-6HPZY4ON.js.map +1 -0
- package/dist/{chunk-WN6UK7PM.js → chunk-7H6DOO3E.js} +239 -11
- package/dist/chunk-7H6DOO3E.js.map +1 -0
- package/dist/{chunk-ACLDOTNQ.js → chunk-ADQ5MQ54.js} +275 -3
- package/dist/chunk-ADQ5MQ54.js.map +1 -0
- package/dist/chunk-CBAHB2BF.js +893 -0
- package/dist/chunk-CBAHB2BF.js.map +1 -0
- package/dist/chunk-DPMFBCV6.js +296 -0
- package/dist/chunk-DPMFBCV6.js.map +1 -0
- package/dist/chunk-DYBQG5PQ.js +34 -0
- package/dist/chunk-DYBQG5PQ.js.map +1 -0
- package/dist/{chunk-ZFKD4QMV.js → chunk-DYECX3IX.js} +3 -3
- package/dist/chunk-EGQYGYIU.js +51 -0
- package/dist/chunk-EGQYGYIU.js.map +1 -0
- package/dist/chunk-FCXOFQAJ.js +79 -0
- package/dist/chunk-FCXOFQAJ.js.map +1 -0
- package/dist/chunk-HB3Z2GCR.js +124 -0
- package/dist/chunk-HB3Z2GCR.js.map +1 -0
- package/dist/{chunk-SCZXXXU4.js → chunk-I6MX32UC.js} +7 -32
- package/dist/chunk-I6MX32UC.js.map +1 -0
- package/dist/{chunk-VQBTTTUN.js → chunk-KESP7GOK.js} +4 -4
- package/dist/{chunk-VQBTTTUN.js.map → chunk-KESP7GOK.js.map} +1 -1
- package/dist/{chunk-NXFEYLVG.js → chunk-MIQHZESA.js} +4 -3
- package/dist/{chunk-NXFEYLVG.js.map → chunk-MIQHZESA.js.map} +1 -1
- package/dist/chunk-MKSA2V7A.js +19 -0
- package/dist/chunk-MKSA2V7A.js.map +1 -0
- package/dist/{chunk-M5INGEFC.js → chunk-MRIBLZL3.js} +3 -1
- package/dist/chunk-MRIBLZL3.js.map +1 -0
- package/dist/{chunk-2WGMYBYS.js → chunk-NIOHFJPJ.js} +6 -6
- package/dist/chunk-OMLIZL2P.js +61 -0
- package/dist/chunk-OMLIZL2P.js.map +1 -0
- package/dist/{chunk-USKYUS74.js → chunk-P7EQ2S5O.js} +2 -2
- package/dist/{chunk-YVFTBQHL.js → chunk-PA6R5ZCI.js} +217 -10
- package/dist/chunk-PA6R5ZCI.js.map +1 -0
- package/dist/chunk-PEULZC6M.js +118 -0
- package/dist/chunk-PEULZC6M.js.map +1 -0
- package/dist/chunk-RD5LYKD6.js +82 -0
- package/dist/chunk-RD5LYKD6.js.map +1 -0
- package/dist/chunk-SIZWEV2Y.js +145 -0
- package/dist/chunk-SIZWEV2Y.js.map +1 -0
- package/dist/{chunk-Y4CMTMUW.js → chunk-UA4RI7OT.js} +12 -6
- package/dist/chunk-UA4RI7OT.js.map +1 -0
- package/dist/chunk-UMLVJTYV.js +20 -0
- package/dist/chunk-UMLVJTYV.js.map +1 -0
- package/dist/chunk-UZXLQCHP.js +53 -0
- package/dist/chunk-UZXLQCHP.js.map +1 -0
- package/dist/{chunk-R2ZTGEVP.js → chunk-VMIO4IXG.js} +5 -5
- package/dist/{chunk-MR4424N3.js → chunk-WCA2NROQ.js} +2 -2
- package/dist/{chunk-TDR6T5CJ.js → chunk-XGSOTWYX.js} +91 -132
- package/dist/chunk-XGSOTWYX.js.map +1 -0
- package/dist/{chunk-NPC4LFV5.js → chunk-YMYK7US4.js} +2 -2
- package/dist/{chunk-PJK6IOBC.js → chunk-YS3POABP.js} +1 -1
- package/dist/chunk-YS3POABP.js.map +1 -0
- package/dist/chunk-Z72JH4KG.js +209 -0
- package/dist/chunk-Z72JH4KG.js.map +1 -0
- package/dist/{chunk-R36SIKES.js → chunk-ZNOEIM6Y.js} +2 -2
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +6 -6
- package/dist/consent/index.d.ts +6 -6
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-IVKU7YTT.js → crypto-A7FRXYHC.js} +3 -3
- package/dist/{delegation-2DBS2EOH.js → delegation-YBA4X4JN.js} +5 -4
- package/dist/derivations/index.cjs +351 -0
- package/dist/derivations/index.cjs.map +1 -0
- package/dist/derivations/index.d.cts +71 -0
- package/dist/derivations/index.d.ts +71 -0
- package/dist/derivations/index.js +27 -0
- package/dist/{dev-unlock-BygpnIWe.d.ts → dev-unlock-D9s-loPr.d.ts} +1 -1
- package/dist/{dev-unlock-BZKx666y.d.cts → dev-unlock-DRwVSy2S.d.cts} +1 -1
- package/dist/executor-7E3VFGW7.js +11 -0
- package/dist/executor-CEWX2FQI.js +8 -0
- package/dist/executor-CEWX2FQI.js.map +1 -0
- package/dist/executor-X4SQ3ZLC.js +8 -0
- package/dist/executor-X4SQ3ZLC.js.map +1 -0
- package/dist/fanout-sidecar-VJ52RIEY.js +51 -0
- package/dist/fanout-sidecar-VJ52RIEY.js.map +1 -0
- package/dist/guards/index.cjs +315 -0
- package/dist/guards/index.cjs.map +1 -0
- package/dist/guards/index.d.cts +30 -0
- package/dist/guards/index.d.ts +30 -0
- package/dist/guards/index.js +29 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/{hash-B0eU2Qv9.d.ts → hash-DXXXusyk.d.ts} +1 -1
- package/dist/{hash-CIyfmKsg.d.cts → hash-DtRih9MQ.d.cts} +1 -1
- package/dist/history/index.cjs +8 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +7 -7
- package/dist/history/index.d.ts +7 -7
- package/dist/history/index.js +6 -6
- package/dist/i18n/index.cjs +81 -0
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +6 -6
- package/dist/i18n/index.d.ts +6 -6
- package/dist/i18n/index.js +19 -6
- package/dist/i18n/index.js.map +1 -1
- package/dist/{index-Dp4tKCjX.d.ts → index-4agOpzqd.d.ts} +174 -3
- package/dist/{index-6xNpPsxR.d.cts → index-CNwA-B6-.d.ts} +303 -5
- package/dist/{index-DJTf9yxn.d.ts → index-CmVgTkqk.d.cts} +303 -5
- package/dist/{index-DsVbTDZI.d.cts → index-hdFvZkBP.d.cts} +174 -3
- package/dist/index.cjs +5929 -1089
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +207 -16
- package/dist/index.d.ts +207 -16
- package/dist/index.js +2402 -672
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +2 -0
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.d.cts +3 -3
- package/dist/indexing/index.d.ts +3 -3
- package/dist/indexing/index.js +4 -4
- package/dist/{lazy-builder-CZVLKh0Z.d.cts → lazy-builder-C-rPfWG0.d.cts} +1 -1
- package/dist/{lazy-builder-BwEoBQZ9.d.ts → lazy-builder-Rpd-V3jP.d.ts} +1 -1
- package/dist/{ledger-UQIMMKO5.js → ledger-3TXNP47J.js} +6 -6
- package/dist/ledger-3TXNP47J.js.map +1 -0
- package/dist/materialized-views/index.cjs +837 -0
- package/dist/materialized-views/index.cjs.map +1 -0
- package/dist/materialized-views/index.d.cts +183 -0
- package/dist/materialized-views/index.d.ts +183 -0
- package/dist/materialized-views/index.js +45 -0
- package/dist/materialized-views/index.js.map +1 -0
- package/dist/overlay-views/index.cjs +359 -0
- package/dist/overlay-views/index.cjs.map +1 -0
- package/dist/overlay-views/index.d.cts +81 -0
- package/dist/overlay-views/index.d.ts +81 -0
- package/dist/overlay-views/index.js +23 -0
- package/dist/overlay-views/index.js.map +1 -0
- package/dist/periods/index.cjs +7 -1
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +6 -6
- package/dist/periods/index.d.ts +6 -6
- package/dist/periods/index.js +6 -6
- package/dist/{predicate-SBHmi6D0.d.cts → predicate-Dnu81tsS.d.cts} +25 -1
- package/dist/{predicate-SBHmi6D0.d.ts → predicate-Dnu81tsS.d.ts} +25 -1
- package/dist/{public-envelope-3QTQADDW.js → public-envelope-PY6NKFLI.js} +4 -4
- package/dist/public-envelope-PY6NKFLI.js.map +1 -0
- package/dist/query/index.cjs +302 -124
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +3 -3
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +26 -11
- package/dist/read-only-facade-ITU6L7BL.js +7 -0
- package/dist/read-only-facade-ITU6L7BL.js.map +1 -0
- package/dist/registry-3L3N3PTG.js +10 -0
- package/dist/registry-3L3N3PTG.js.map +1 -0
- package/dist/registry-O47PUPSY.js +8 -0
- package/dist/registry-O47PUPSY.js.map +1 -0
- package/dist/registry-RFGGMVNJ.js +7 -0
- package/dist/registry-RFGGMVNJ.js.map +1 -0
- package/dist/registry-WLLMODKN.js +8 -0
- package/dist/registry-WLLMODKN.js.map +1 -0
- package/dist/session/index.cjs +7 -1
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +7 -7
- package/dist/session/index.d.ts +7 -7
- package/dist/session/index.js +10 -3
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +6 -6
- package/dist/shadow/index.d.ts +6 -6
- package/dist/shadow/index.js +2 -2
- package/dist/stale-HSC5YO2O.js +13 -0
- package/dist/stale-HSC5YO2O.js.map +1 -0
- package/dist/store/index.cjs +14 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +6 -6
- package/dist/store/index.d.ts +6 -6
- package/dist/store/index.js +5 -2
- package/dist/{strategy-D-SrOLCl.d.cts → strategy-DSTrsZ8t.d.cts} +72 -19
- package/dist/{strategy-D-SrOLCl.d.ts → strategy-DSTrsZ8t.d.ts} +72 -19
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +5 -5
- package/dist/sync/index.d.ts +5 -5
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs +1554 -2
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +6 -6
- package/dist/team/index.d.ts +6 -6
- package/dist/team/index.js +76 -9
- package/dist/tx/index.cjs +296 -44
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +6 -6
- package/dist/tx/index.d.ts +6 -6
- package/dist/tx/index.js +2 -2
- package/dist/{types-DD9eKKNc.d.ts → types-C4lwMKKF.d.cts} +2771 -322
- package/dist/{types-arFMsCtn.d.cts → types-DW9RGSSs.d.ts} +2771 -322
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/with-derivation-C8LDlV7t.d.cts +13 -0
- package/dist/with-derivation-g-pGoMzL.d.ts +13 -0
- package/dist/with-guard-DWOCK4Ca.d.ts +18 -0
- package/dist/with-guard-jI1x9Z3k.d.cts +18 -0
- package/dist/with-materialized-view-DaKR-N6J.d.ts +27 -0
- package/dist/with-materialized-view-DcTx4H3j.d.cts +27 -0
- package/dist/with-overlayed-view-D-6oWAgM.d.cts +13 -0
- package/dist/with-overlayed-view-N7jYuNOS.d.ts +13 -0
- package/package.json +53 -2
- package/dist/chunk-ACLDOTNQ.js.map +0 -1
- package/dist/chunk-BTDCBVJW.js +0 -160
- package/dist/chunk-BTDCBVJW.js.map +0 -1
- package/dist/chunk-CIMZBAZB.js.map +0 -1
- package/dist/chunk-GOUT6DND.js.map +0 -1
- package/dist/chunk-M5INGEFC.js.map +0 -1
- package/dist/chunk-PJK6IOBC.js.map +0 -1
- package/dist/chunk-SCZXXXU4.js.map +0 -1
- package/dist/chunk-TDR6T5CJ.js.map +0 -1
- package/dist/chunk-TOQK4KAN.js +0 -79
- package/dist/chunk-TOQK4KAN.js.map +0 -1
- package/dist/chunk-WN6UK7PM.js.map +0 -1
- package/dist/chunk-Y4CMTMUW.js.map +0 -1
- package/dist/chunk-YVFTBQHL.js.map +0 -1
- /package/dist/{chunk-HC7Z5EQZ.js.map → chunk-4TFSM22V.js.map} +0 -0
- /package/dist/{chunk-7XBQS42M.js.map → chunk-537VFZTR.js.map} +0 -0
- /package/dist/{chunk-M62XNWRA.js.map → chunk-5DWL3JBF.js.map} +0 -0
- /package/dist/{chunk-RSPLI376.js.map → chunk-5SCJ5UEF.js.map} +0 -0
- /package/dist/{chunk-ZFKD4QMV.js.map → chunk-DYECX3IX.js.map} +0 -0
- /package/dist/{chunk-2WGMYBYS.js.map → chunk-NIOHFJPJ.js.map} +0 -0
- /package/dist/{chunk-USKYUS74.js.map → chunk-P7EQ2S5O.js.map} +0 -0
- /package/dist/{chunk-R2ZTGEVP.js.map → chunk-VMIO4IXG.js.map} +0 -0
- /package/dist/{chunk-MR4424N3.js.map → chunk-WCA2NROQ.js.map} +0 -0
- /package/dist/{chunk-NPC4LFV5.js.map → chunk-YMYK7US4.js.map} +0 -0
- /package/dist/{chunk-R36SIKES.js.map → chunk-ZNOEIM6Y.js.map} +0 -0
- /package/dist/{crypto-IVKU7YTT.js.map → crypto-A7FRXYHC.js.map} +0 -0
- /package/dist/{delegation-2DBS2EOH.js.map → delegation-YBA4X4JN.js.map} +0 -0
- /package/dist/{ledger-UQIMMKO5.js.map → derivations/index.js.map} +0 -0
- /package/dist/{public-envelope-3QTQADDW.js.map → executor-7E3VFGW7.js.map} +0 -0
package/dist/query/index.cjs
CHANGED
|
@@ -23,10 +23,16 @@ __export(query_exports, {
|
|
|
23
23
|
Aggregation: () => Aggregation,
|
|
24
24
|
CollectionIndexes: () => CollectionIndexes,
|
|
25
25
|
DEFAULT_JOIN_MAX_ROWS: () => DEFAULT_JOIN_MAX_ROWS,
|
|
26
|
+
DanglingReferenceError: () => DanglingReferenceError,
|
|
26
27
|
GROUPBY_MAX_CARDINALITY: () => GROUPBY_MAX_CARDINALITY,
|
|
27
28
|
GROUPBY_WARN_CARDINALITY: () => GROUPBY_WARN_CARDINALITY,
|
|
29
|
+
GroupCardinalityError: () => GroupCardinalityError,
|
|
28
30
|
GroupedAggregation: () => GroupedAggregation,
|
|
29
31
|
GroupedQuery: () => GroupedQuery,
|
|
32
|
+
GroupedQueryN: () => GroupedQueryN,
|
|
33
|
+
IndexRequiredError: () => IndexRequiredError,
|
|
34
|
+
IndexWriteFailureError: () => IndexWriteFailureError,
|
|
35
|
+
JoinTooLargeError: () => JoinTooLargeError,
|
|
30
36
|
Query: () => Query,
|
|
31
37
|
ScanBuilder: () => ScanBuilder,
|
|
32
38
|
applyJoins: () => applyJoins,
|
|
@@ -111,6 +117,8 @@ function evaluateClause(record, clause) {
|
|
|
111
117
|
return evaluateFieldClause(record, clause);
|
|
112
118
|
case "filter":
|
|
113
119
|
return clause.fn(record);
|
|
120
|
+
case "wherePredicate":
|
|
121
|
+
return clause.fn(record, clause.ctx);
|
|
114
122
|
case "group":
|
|
115
123
|
if (clause.op === "and") {
|
|
116
124
|
for (const child of clause.clauses) {
|
|
@@ -154,6 +162,38 @@ var GroupCardinalityError = class extends NoydbError {
|
|
|
154
162
|
this.maxGroups = maxGroups;
|
|
155
163
|
}
|
|
156
164
|
};
|
|
165
|
+
var IndexRequiredError = class extends NoydbError {
|
|
166
|
+
collection;
|
|
167
|
+
touchedFields;
|
|
168
|
+
missingFields;
|
|
169
|
+
constructor(args) {
|
|
170
|
+
super(
|
|
171
|
+
"INDEX_REQUIRED",
|
|
172
|
+
`Collection "${args.collection}": query references unindexed fields in lazy mode (missing: ${args.missingFields.join(", ")}). Declare an index on each field, or use collection.scan() for non-indexed iteration.`
|
|
173
|
+
);
|
|
174
|
+
this.name = "IndexRequiredError";
|
|
175
|
+
this.collection = args.collection;
|
|
176
|
+
this.touchedFields = [...args.touchedFields];
|
|
177
|
+
this.missingFields = [...args.missingFields];
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
var IndexWriteFailureError = class extends NoydbError {
|
|
181
|
+
recordId;
|
|
182
|
+
field;
|
|
183
|
+
op;
|
|
184
|
+
cause;
|
|
185
|
+
constructor(args) {
|
|
186
|
+
super(
|
|
187
|
+
"INDEX_WRITE_FAILURE",
|
|
188
|
+
`Index side-car ${args.op} failed for field "${args.field}" on record "${args.recordId}"`
|
|
189
|
+
);
|
|
190
|
+
this.name = "IndexWriteFailureError";
|
|
191
|
+
this.recordId = args.recordId;
|
|
192
|
+
this.field = args.field;
|
|
193
|
+
this.op = args.op;
|
|
194
|
+
this.cause = args.cause;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
157
197
|
var JoinTooLargeError = class extends NoydbError {
|
|
158
198
|
leftRows;
|
|
159
199
|
rightRows;
|
|
@@ -418,6 +458,9 @@ var NO_AGGREGATE = {
|
|
|
418
458
|
groupBy() {
|
|
419
459
|
throw NOT_ENABLED;
|
|
420
460
|
},
|
|
461
|
+
groupByN() {
|
|
462
|
+
throw NOT_ENABLED;
|
|
463
|
+
},
|
|
421
464
|
scanAggregate() {
|
|
422
465
|
throw NOT_ENABLED;
|
|
423
466
|
}
|
|
@@ -436,11 +479,83 @@ var Query = class _Query {
|
|
|
436
479
|
plan;
|
|
437
480
|
joinContext;
|
|
438
481
|
aggregateStrategy;
|
|
439
|
-
|
|
482
|
+
predicates;
|
|
483
|
+
constructor(source, plan = EMPTY_PLAN, joinContext, aggregateStrategy = NO_AGGREGATE, predicates) {
|
|
440
484
|
this.source = source;
|
|
441
485
|
this.plan = plan;
|
|
442
486
|
this.joinContext = joinContext;
|
|
443
487
|
this.aggregateStrategy = aggregateStrategy;
|
|
488
|
+
this.predicates = predicates;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* @internal — accessor for the materialized-view dependency
|
|
492
|
+
* analyzer. Not part of the public API; consumers should use the
|
|
493
|
+
* builder methods, not inspect the plan directly.
|
|
494
|
+
*/
|
|
495
|
+
_plan() {
|
|
496
|
+
return this.plan;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* @internal — accessor for the materialized-view dependency
|
|
500
|
+
* analyzer. Returns the join resolution context (or `undefined` for
|
|
501
|
+
* queries constructed without a Collection backing).
|
|
502
|
+
*/
|
|
503
|
+
_joinContext() {
|
|
504
|
+
return this.joinContext;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* @internal — clone this Query with a declared-predicate map
|
|
508
|
+
* attached. Used by the materialized-view registry to enable
|
|
509
|
+
* `.wherePredicate(name, ctx?)` for the MV's query callback (#153).
|
|
510
|
+
* Consumers don't call this directly.
|
|
511
|
+
*/
|
|
512
|
+
_withPredicates(predicates) {
|
|
513
|
+
return new _Query(
|
|
514
|
+
this.source,
|
|
515
|
+
this.plan,
|
|
516
|
+
this.joinContext,
|
|
517
|
+
this.aggregateStrategy,
|
|
518
|
+
predicates
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Filter by a registered deterministic predicate (#153). Requires
|
|
523
|
+
* the Query to have been augmented with a predicates map (typically
|
|
524
|
+
* via the materialized-view registry — bare Queries constructed
|
|
525
|
+
* outside an MV throw on `.wherePredicate()`).
|
|
526
|
+
*
|
|
527
|
+
* `ctx` is an optional opaque value passed verbatim to the predicate
|
|
528
|
+
* function. Both `predicateHash` (from the registration) and a
|
|
529
|
+
* canonical-JSON hash of `ctx` fold into the MV's `queryHash`, so
|
|
530
|
+
* either changing forces refresh on next visit.
|
|
531
|
+
*/
|
|
532
|
+
wherePredicate(name, ctx) {
|
|
533
|
+
if (!this.predicates) {
|
|
534
|
+
throw new Error(
|
|
535
|
+
`.wherePredicate("${name}"): no predicates registered on this Query. Function-based predicates require the Query to be obtained from inside a materialized-view query() callback whose strategy declares \`predicates: { ${name}: { hash, fn } }\`.`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
const decl = this.predicates.get(name);
|
|
539
|
+
if (!decl) {
|
|
540
|
+
throw new Error(
|
|
541
|
+
`.wherePredicate("${name}"): predicate not registered. Available: ${[...this.predicates.keys()].join(", ") || "(none)"}.`
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const clause = {
|
|
545
|
+
type: "wherePredicate",
|
|
546
|
+
name,
|
|
547
|
+
ctx,
|
|
548
|
+
predicateHash: decl.hash,
|
|
549
|
+
ctxHash: canonicalCtxHash(ctx),
|
|
550
|
+
fn: decl.fn
|
|
551
|
+
};
|
|
552
|
+
return new _Query(
|
|
553
|
+
this.source,
|
|
554
|
+
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
555
|
+
this.joinContext,
|
|
556
|
+
this.aggregateStrategy,
|
|
557
|
+
this.predicates
|
|
558
|
+
);
|
|
444
559
|
}
|
|
445
560
|
/** Add a field comparison. Multiple where() calls are AND-combined. */
|
|
446
561
|
where(field, op, value) {
|
|
@@ -449,7 +564,8 @@ var Query = class _Query {
|
|
|
449
564
|
this.source,
|
|
450
565
|
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
451
566
|
this.joinContext,
|
|
452
|
-
this.aggregateStrategy
|
|
567
|
+
this.aggregateStrategy,
|
|
568
|
+
this.predicates
|
|
453
569
|
);
|
|
454
570
|
}
|
|
455
571
|
/**
|
|
@@ -459,7 +575,7 @@ var Query = class _Query {
|
|
|
459
575
|
*/
|
|
460
576
|
or(builder) {
|
|
461
577
|
const sub = builder(
|
|
462
|
-
new _Query(this.source, EMPTY_PLAN, this.joinContext, this.aggregateStrategy)
|
|
578
|
+
new _Query(this.source, EMPTY_PLAN, this.joinContext, this.aggregateStrategy, this.predicates)
|
|
463
579
|
);
|
|
464
580
|
const group = {
|
|
465
581
|
type: "group",
|
|
@@ -470,7 +586,8 @@ var Query = class _Query {
|
|
|
470
586
|
this.source,
|
|
471
587
|
{ ...this.plan, clauses: [...this.plan.clauses, group] },
|
|
472
588
|
this.joinContext,
|
|
473
|
-
this.aggregateStrategy
|
|
589
|
+
this.aggregateStrategy,
|
|
590
|
+
this.predicates
|
|
474
591
|
);
|
|
475
592
|
}
|
|
476
593
|
/**
|
|
@@ -479,7 +596,7 @@ var Query = class _Query {
|
|
|
479
596
|
*/
|
|
480
597
|
and(builder) {
|
|
481
598
|
const sub = builder(
|
|
482
|
-
new _Query(this.source, EMPTY_PLAN, this.joinContext, this.aggregateStrategy)
|
|
599
|
+
new _Query(this.source, EMPTY_PLAN, this.joinContext, this.aggregateStrategy, this.predicates)
|
|
483
600
|
);
|
|
484
601
|
const group = {
|
|
485
602
|
type: "group",
|
|
@@ -490,7 +607,8 @@ var Query = class _Query {
|
|
|
490
607
|
this.source,
|
|
491
608
|
{ ...this.plan, clauses: [...this.plan.clauses, group] },
|
|
492
609
|
this.joinContext,
|
|
493
|
-
this.aggregateStrategy
|
|
610
|
+
this.aggregateStrategy,
|
|
611
|
+
this.predicates
|
|
494
612
|
);
|
|
495
613
|
}
|
|
496
614
|
/** Escape hatch: add an arbitrary predicate function. Not serializable. */
|
|
@@ -503,7 +621,8 @@ var Query = class _Query {
|
|
|
503
621
|
this.source,
|
|
504
622
|
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
505
623
|
this.joinContext,
|
|
506
|
-
this.aggregateStrategy
|
|
624
|
+
this.aggregateStrategy,
|
|
625
|
+
this.predicates
|
|
507
626
|
);
|
|
508
627
|
}
|
|
509
628
|
/** Sort by a field. Subsequent calls are tie-breakers. */
|
|
@@ -512,7 +631,8 @@ var Query = class _Query {
|
|
|
512
631
|
this.source,
|
|
513
632
|
{ ...this.plan, orderBy: [...this.plan.orderBy, { field, direction }] },
|
|
514
633
|
this.joinContext,
|
|
515
|
-
this.aggregateStrategy
|
|
634
|
+
this.aggregateStrategy,
|
|
635
|
+
this.predicates
|
|
516
636
|
);
|
|
517
637
|
}
|
|
518
638
|
/** Cap the result size. */
|
|
@@ -521,7 +641,8 @@ var Query = class _Query {
|
|
|
521
641
|
this.source,
|
|
522
642
|
{ ...this.plan, limit: n },
|
|
523
643
|
this.joinContext,
|
|
524
|
-
this.aggregateStrategy
|
|
644
|
+
this.aggregateStrategy,
|
|
645
|
+
this.predicates
|
|
525
646
|
);
|
|
526
647
|
}
|
|
527
648
|
/** Skip the first N matching records (after ordering). */
|
|
@@ -530,7 +651,8 @@ var Query = class _Query {
|
|
|
530
651
|
this.source,
|
|
531
652
|
{ ...this.plan, offset: n },
|
|
532
653
|
this.joinContext,
|
|
533
|
-
this.aggregateStrategy
|
|
654
|
+
this.aggregateStrategy,
|
|
655
|
+
this.predicates
|
|
534
656
|
);
|
|
535
657
|
}
|
|
536
658
|
/**
|
|
@@ -629,7 +751,8 @@ var Query = class _Query {
|
|
|
629
751
|
this.source,
|
|
630
752
|
{ ...this.plan, joins: [...this.plan.joins, leg] },
|
|
631
753
|
this.joinContext,
|
|
632
|
-
this.aggregateStrategy
|
|
754
|
+
this.aggregateStrategy,
|
|
755
|
+
this.predicates
|
|
633
756
|
);
|
|
634
757
|
}
|
|
635
758
|
/**
|
|
@@ -721,53 +844,10 @@ var Query = class _Query {
|
|
|
721
844
|
}
|
|
722
845
|
return this.aggregateStrategy.aggregate(executeRecords, spec, upstreams);
|
|
723
846
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
*
|
|
729
|
-
* ```ts
|
|
730
|
-
* const byClient = invoices.query()
|
|
731
|
-
* .where('status', '==', 'open')
|
|
732
|
-
* .groupBy('clientId')
|
|
733
|
-
* .aggregate({ total: sum('amount'), n: count() })
|
|
734
|
-
* .run()
|
|
735
|
-
* // → [ { clientId: 'c1', total: 5250, n: 3 }, … ]
|
|
736
|
-
* ```
|
|
737
|
-
*
|
|
738
|
-
* Result rows carry the group key value under the grouping field
|
|
739
|
-
* name plus every reducer output from the spec. Buckets are
|
|
740
|
-
* emitted in first-seen order — consumers who want a specific
|
|
741
|
-
* ordering should `.sort()` downstream.
|
|
742
|
-
*
|
|
743
|
-
* **Cardinality caps:** a one-shot warning fires at 10_000
|
|
744
|
-
* distinct groups; `GroupCardinalityError` throws at 100_000.
|
|
745
|
-
* Grouping on a high-uniqueness field like `id` or `createdAt` is
|
|
746
|
-
* almost always a query mistake — the error message names the
|
|
747
|
-
* field and observed cardinality and suggests narrowing with
|
|
748
|
-
* `.where()` first.
|
|
749
|
-
*
|
|
750
|
-
* **Null / undefined keys:** records with a missing or explicitly
|
|
751
|
-
* `null` group field get their own buckets. `Map`-based
|
|
752
|
-
* partitioning distinguishes `undefined` from `null`, so the two
|
|
753
|
-
* cases do NOT merge. Consumers who want them merged should
|
|
754
|
-
* coalesce upstream with `.filter()`.
|
|
755
|
-
*
|
|
756
|
-
* **Joins are not applied** — same rationale as `.count()` and
|
|
757
|
-
* `.aggregate()`. Joined fields in are projection-only, so
|
|
758
|
-
* running a join inside a grouping pipeline would be wasteful and
|
|
759
|
-
* could trigger `DanglingReferenceError` in strict mode for a
|
|
760
|
-
* call whose intent is purely to bucket-and-reduce. Grouping by
|
|
761
|
-
* a joined field is explicitly out of scope for — file an
|
|
762
|
-
* issue if a real consumer needs it.
|
|
763
|
-
*
|
|
764
|
-
* **Filter clauses (`.filter(fn)`):** grouped queries still
|
|
765
|
-
* support filter clauses in the underlying plan — they run in
|
|
766
|
-
* the same candidate/filter pipeline that `.aggregate()` uses.
|
|
767
|
-
* The performance caveat is the same: filter clauses cost O(N)
|
|
768
|
-
* per record and can't be index-accelerated.
|
|
769
|
-
*/
|
|
770
|
-
groupBy(field) {
|
|
847
|
+
groupBy(...fields) {
|
|
848
|
+
if (fields.length === 0) {
|
|
849
|
+
throw new Error(".groupBy() requires at least one field");
|
|
850
|
+
}
|
|
771
851
|
const source = this.source;
|
|
772
852
|
const clauses = this.plan.clauses;
|
|
773
853
|
const executeRecords = () => {
|
|
@@ -779,36 +859,21 @@ var Query = class _Query {
|
|
|
779
859
|
const subscribe = source.subscribe.bind(source);
|
|
780
860
|
upstreams.push({ subscribe: (cb) => subscribe(cb) });
|
|
781
861
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (!labels) return void 0;
|
|
798
|
-
if (labels[locale] !== void 0) return labels[locale];
|
|
799
|
-
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
800
|
-
for (const fb of chain) {
|
|
801
|
-
if (fb === "any") {
|
|
802
|
-
const any = Object.values(labels)[0];
|
|
803
|
-
if (any !== void 0) return any;
|
|
804
|
-
} else if (labels[fb] !== void 0) {
|
|
805
|
-
return labels[fb];
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
return void 0;
|
|
809
|
-
};
|
|
810
|
-
})() : void 0;
|
|
811
|
-
return this.aggregateStrategy.groupBy(executeRecords, field, upstreams, dictLabelResolver);
|
|
862
|
+
if (fields.length === 1) {
|
|
863
|
+
const field = fields[0];
|
|
864
|
+
const dictLabelResolver = buildDictLabelResolver(this.joinContext, field);
|
|
865
|
+
return this.aggregateStrategy.groupBy(
|
|
866
|
+
executeRecords,
|
|
867
|
+
field,
|
|
868
|
+
upstreams,
|
|
869
|
+
dictLabelResolver
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
return this.aggregateStrategy.groupByN(
|
|
873
|
+
executeRecords,
|
|
874
|
+
fields,
|
|
875
|
+
upstreams
|
|
876
|
+
);
|
|
812
877
|
}
|
|
813
878
|
/**
|
|
814
879
|
* Re-run the query whenever the source notifies of changes.
|
|
@@ -1034,6 +1099,16 @@ function serializeClause(clause) {
|
|
|
1034
1099
|
if (clause.type === "filter") {
|
|
1035
1100
|
return { type: "filter", fn: "[function]" };
|
|
1036
1101
|
}
|
|
1102
|
+
if (clause.type === "wherePredicate") {
|
|
1103
|
+
return {
|
|
1104
|
+
type: "wherePredicate",
|
|
1105
|
+
name: clause.name,
|
|
1106
|
+
ctx: clause.ctx,
|
|
1107
|
+
predicateHash: clause.predicateHash,
|
|
1108
|
+
ctxHash: clause.ctxHash,
|
|
1109
|
+
fn: "[function]"
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1037
1112
|
if (clause.type === "group") {
|
|
1038
1113
|
return {
|
|
1039
1114
|
type: "group",
|
|
@@ -1043,6 +1118,53 @@ function serializeClause(clause) {
|
|
|
1043
1118
|
}
|
|
1044
1119
|
return clause;
|
|
1045
1120
|
}
|
|
1121
|
+
function canonicalCtxHash(ctx) {
|
|
1122
|
+
if (ctx === void 0) return "";
|
|
1123
|
+
const canonical = JSON.stringify(ctx, (_key, value) => {
|
|
1124
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1125
|
+
const sorted = {};
|
|
1126
|
+
for (const k of Object.keys(value).sort()) {
|
|
1127
|
+
sorted[k] = value[k];
|
|
1128
|
+
}
|
|
1129
|
+
return sorted;
|
|
1130
|
+
}
|
|
1131
|
+
return value;
|
|
1132
|
+
});
|
|
1133
|
+
let h = 5381;
|
|
1134
|
+
for (let i = 0; i < canonical.length; i++) {
|
|
1135
|
+
h = (h << 5) + h ^ canonical.charCodeAt(i);
|
|
1136
|
+
}
|
|
1137
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
1138
|
+
}
|
|
1139
|
+
function buildDictLabelResolver(joinCtx, field) {
|
|
1140
|
+
if (!joinCtx?.resolveDictSource) return void 0;
|
|
1141
|
+
const dictSource = joinCtx.resolveDictSource(field);
|
|
1142
|
+
if (!dictSource) return void 0;
|
|
1143
|
+
const snapshot = dictSource.snapshot();
|
|
1144
|
+
const dictMap = /* @__PURE__ */ new Map();
|
|
1145
|
+
for (const entry of snapshot) {
|
|
1146
|
+
const k = entry["key"];
|
|
1147
|
+
const labels = entry["labels"];
|
|
1148
|
+
if (typeof k === "string" && labels && typeof labels === "object") {
|
|
1149
|
+
dictMap.set(k, labels);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return async (key, locale, fallback) => {
|
|
1153
|
+
const labels = dictMap.get(key);
|
|
1154
|
+
if (!labels) return void 0;
|
|
1155
|
+
if (labels[locale] !== void 0) return labels[locale];
|
|
1156
|
+
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
1157
|
+
for (const fb of chain) {
|
|
1158
|
+
if (fb === "any") {
|
|
1159
|
+
const any = Object.values(labels)[0];
|
|
1160
|
+
if (any !== void 0) return any;
|
|
1161
|
+
} else if (labels[fb] !== void 0) {
|
|
1162
|
+
return labels[fb];
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return void 0;
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1046
1168
|
|
|
1047
1169
|
// src/indexing/eager-indexes.ts
|
|
1048
1170
|
var CollectionIndexes = class {
|
|
@@ -1379,31 +1501,53 @@ function buildLiveAggregation(recompute, upstreams) {
|
|
|
1379
1501
|
return new LiveAggregationImpl(recompute, upstreams);
|
|
1380
1502
|
}
|
|
1381
1503
|
|
|
1504
|
+
// src/aggregate/canonical-key.ts
|
|
1505
|
+
function canonicalGroupKey(fields, row) {
|
|
1506
|
+
const sorted = [...fields].sort();
|
|
1507
|
+
const parts = [];
|
|
1508
|
+
for (const name of sorted) {
|
|
1509
|
+
const v = row[name];
|
|
1510
|
+
const serialised = v === void 0 ? "undefined" : JSON.stringify(v);
|
|
1511
|
+
parts.push(`${name}=${serialised}`);
|
|
1512
|
+
}
|
|
1513
|
+
return parts.join("|");
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1382
1516
|
// src/aggregate/groupby.ts
|
|
1383
1517
|
var GROUPBY_WARN_CARDINALITY = 1e4;
|
|
1384
1518
|
var GROUPBY_MAX_CARDINALITY = 1e5;
|
|
1385
1519
|
var warnedCardinalityFields = /* @__PURE__ */ new Set();
|
|
1386
|
-
function warnCardinalityApproaching(
|
|
1387
|
-
|
|
1388
|
-
warnedCardinalityFields.
|
|
1520
|
+
function warnCardinalityApproaching(fields, observed) {
|
|
1521
|
+
const key = JSON.stringify([...fields].sort());
|
|
1522
|
+
if (warnedCardinalityFields.has(key)) return;
|
|
1523
|
+
warnedCardinalityFields.add(key);
|
|
1524
|
+
const label = `[${fields.join(", ")}]`;
|
|
1389
1525
|
console.warn(
|
|
1390
|
-
`[noy-db] .groupBy(
|
|
1526
|
+
`[noy-db] .groupBy(${label}) produced ${observed} distinct groups, ${Math.round(observed / GROUPBY_MAX_CARDINALITY * 100)}% of the ${GROUPBY_MAX_CARDINALITY}-group ceiling. Narrow the query with .where() before grouping, or switch to a lower-cardinality field.`
|
|
1391
1527
|
);
|
|
1392
1528
|
}
|
|
1393
1529
|
function resetGroupByWarnings() {
|
|
1394
1530
|
warnedCardinalityFields.clear();
|
|
1395
1531
|
}
|
|
1396
|
-
var
|
|
1397
|
-
constructor(executeRecords,
|
|
1532
|
+
var GroupedQueryBase = class {
|
|
1533
|
+
constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver) {
|
|
1398
1534
|
this.executeRecords = executeRecords;
|
|
1399
|
-
this.field = field;
|
|
1400
1535
|
this.upstreams = upstreams;
|
|
1401
1536
|
this.dictLabelResolver = dictLabelResolver;
|
|
1537
|
+
this.fields = typeof fieldOrFields === "string" ? [fieldOrFields] : [...fieldOrFields];
|
|
1402
1538
|
}
|
|
1403
1539
|
executeRecords;
|
|
1404
|
-
field;
|
|
1405
1540
|
upstreams;
|
|
1406
1541
|
dictLabelResolver;
|
|
1542
|
+
/**
|
|
1543
|
+
* Field set this grouped query buckets on. Stored in declaration
|
|
1544
|
+
* order — the same order is preserved on every result row by
|
|
1545
|
+
* `groupAndReduce`. For the single-field constructor, this is
|
|
1546
|
+
* `[field]`.
|
|
1547
|
+
*/
|
|
1548
|
+
fields;
|
|
1549
|
+
};
|
|
1550
|
+
var GroupedQuery = class extends GroupedQueryBase {
|
|
1407
1551
|
/**
|
|
1408
1552
|
* Build a grouped aggregation. Returns a `GroupedAggregation`
|
|
1409
1553
|
* with `.run()`, `.runAsync()`, and `.live()` terminals — same shape
|
|
@@ -1413,70 +1557,93 @@ var GroupedQuery = class {
|
|
|
1413
1557
|
aggregate(spec) {
|
|
1414
1558
|
return new GroupedAggregation(
|
|
1415
1559
|
this.executeRecords,
|
|
1416
|
-
this.
|
|
1560
|
+
this.fields,
|
|
1561
|
+
spec,
|
|
1562
|
+
this.upstreams,
|
|
1563
|
+
this.dictLabelResolver
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
var GroupedQueryN = class extends GroupedQueryBase {
|
|
1568
|
+
aggregate(spec) {
|
|
1569
|
+
return new GroupedAggregation(
|
|
1570
|
+
this.executeRecords,
|
|
1571
|
+
this.fields,
|
|
1417
1572
|
spec,
|
|
1418
1573
|
this.upstreams,
|
|
1419
1574
|
this.dictLabelResolver
|
|
1420
1575
|
);
|
|
1421
1576
|
}
|
|
1422
1577
|
};
|
|
1423
|
-
function groupAndReduce(records,
|
|
1578
|
+
function groupAndReduce(records, fieldOrFields, spec) {
|
|
1579
|
+
const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
|
|
1580
|
+
if (fields.length === 0) {
|
|
1581
|
+
throw new Error(".groupBy() requires at least one field");
|
|
1582
|
+
}
|
|
1424
1583
|
const buckets = /* @__PURE__ */ new Map();
|
|
1584
|
+
const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
|
|
1425
1585
|
for (const record of records) {
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1586
|
+
const keyValues = {};
|
|
1587
|
+
for (const f of fields) {
|
|
1588
|
+
keyValues[f] = readPath(record, f);
|
|
1589
|
+
}
|
|
1590
|
+
const dedupKey = canonicalGroupKey(fields, keyValues);
|
|
1591
|
+
let bucket = buckets.get(dedupKey);
|
|
1428
1592
|
if (bucket === void 0) {
|
|
1429
1593
|
if (buckets.size >= GROUPBY_MAX_CARDINALITY) {
|
|
1430
1594
|
throw new GroupCardinalityError(
|
|
1431
|
-
|
|
1595
|
+
fieldLabel,
|
|
1432
1596
|
buckets.size + 1,
|
|
1433
1597
|
GROUPBY_MAX_CARDINALITY
|
|
1434
1598
|
);
|
|
1435
1599
|
}
|
|
1436
|
-
bucket = [];
|
|
1437
|
-
buckets.set(
|
|
1600
|
+
bucket = { keyValues, records: [] };
|
|
1601
|
+
buckets.set(dedupKey, bucket);
|
|
1438
1602
|
}
|
|
1439
|
-
bucket.push(record);
|
|
1603
|
+
bucket.records.push(record);
|
|
1440
1604
|
}
|
|
1441
1605
|
if (buckets.size >= GROUPBY_WARN_CARDINALITY) {
|
|
1442
|
-
warnCardinalityApproaching(
|
|
1606
|
+
warnCardinalityApproaching(fields, buckets.size);
|
|
1443
1607
|
}
|
|
1444
|
-
const
|
|
1608
|
+
const reducerKeys = Object.keys(spec);
|
|
1445
1609
|
const out = [];
|
|
1446
|
-
for (const
|
|
1610
|
+
for (const bucket of buckets.values()) {
|
|
1447
1611
|
const state = {};
|
|
1448
|
-
for (const
|
|
1449
|
-
state[
|
|
1612
|
+
for (const rk of reducerKeys) {
|
|
1613
|
+
state[rk] = spec[rk].init();
|
|
1450
1614
|
}
|
|
1451
|
-
for (const record of
|
|
1452
|
-
for (const
|
|
1453
|
-
state[
|
|
1615
|
+
for (const record of bucket.records) {
|
|
1616
|
+
for (const rk of reducerKeys) {
|
|
1617
|
+
state[rk] = spec[rk].step(state[rk], record);
|
|
1454
1618
|
}
|
|
1455
1619
|
}
|
|
1456
|
-
const row = {
|
|
1457
|
-
for (const
|
|
1458
|
-
row[
|
|
1620
|
+
const row = {};
|
|
1621
|
+
for (const f of fields) {
|
|
1622
|
+
row[f] = bucket.keyValues[f];
|
|
1623
|
+
}
|
|
1624
|
+
for (const rk of reducerKeys) {
|
|
1625
|
+
row[rk] = spec[rk].finalize(state[rk]);
|
|
1459
1626
|
}
|
|
1460
1627
|
out.push(row);
|
|
1461
1628
|
}
|
|
1462
1629
|
return out;
|
|
1463
1630
|
}
|
|
1464
1631
|
var GroupedAggregation = class {
|
|
1465
|
-
constructor(executeRecords,
|
|
1632
|
+
constructor(executeRecords, fields, spec, upstreams, dictLabelResolver) {
|
|
1466
1633
|
this.executeRecords = executeRecords;
|
|
1467
|
-
this.field = field;
|
|
1468
1634
|
this.spec = spec;
|
|
1469
1635
|
this.upstreams = upstreams;
|
|
1470
1636
|
this.dictLabelResolver = dictLabelResolver;
|
|
1637
|
+
this.fields = typeof fields === "string" ? [fields] : [...fields];
|
|
1471
1638
|
}
|
|
1472
1639
|
executeRecords;
|
|
1473
|
-
field;
|
|
1474
1640
|
spec;
|
|
1475
1641
|
upstreams;
|
|
1476
1642
|
dictLabelResolver;
|
|
1643
|
+
fields;
|
|
1477
1644
|
/** Execute the query, group, reduce, and return an array of rows. */
|
|
1478
1645
|
run() {
|
|
1479
|
-
return groupAndReduce(this.executeRecords(), this.
|
|
1646
|
+
return groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
1480
1647
|
}
|
|
1481
1648
|
/**
|
|
1482
1649
|
* Execute the query, group, reduce, and resolve `<field>Label` for
|
|
@@ -1486,17 +1653,22 @@ var GroupedAggregation = class {
|
|
|
1486
1653
|
*
|
|
1487
1654
|
* The `<field>Label` field is appended to each row. Rows whose group
|
|
1488
1655
|
* key has no dictionary entry get `<field>Label: undefined`.
|
|
1656
|
+
*
|
|
1657
|
+
* Dict-label resolution is single-field only — multi-key groupings
|
|
1658
|
+
* do not produce a `<field>Label`. The resolver is only attached
|
|
1659
|
+
* by the builder when `fields.length === 1`.
|
|
1489
1660
|
*/
|
|
1490
1661
|
async runAsync(opts) {
|
|
1491
|
-
const rows = groupAndReduce(this.executeRecords(), this.
|
|
1492
|
-
if (!opts?.locale || !this.dictLabelResolver) return rows;
|
|
1662
|
+
const rows = groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
1663
|
+
if (!opts?.locale || !this.dictLabelResolver || this.fields.length !== 1) return rows;
|
|
1493
1664
|
const resolve = this.dictLabelResolver;
|
|
1494
1665
|
const locale = opts.locale;
|
|
1495
1666
|
const fallback = opts.fallback;
|
|
1496
|
-
const
|
|
1667
|
+
const field = this.fields[0];
|
|
1668
|
+
const labelKey = `${field}Label`;
|
|
1497
1669
|
return Promise.all(
|
|
1498
1670
|
rows.map(async (row) => {
|
|
1499
|
-
const key = row[
|
|
1671
|
+
const key = row[field];
|
|
1500
1672
|
if (typeof key !== "string") return row;
|
|
1501
1673
|
const label = await resolve(key, locale, fallback);
|
|
1502
1674
|
return { ...row, [labelKey]: label };
|
|
@@ -1520,7 +1692,7 @@ var GroupedAggregation = class {
|
|
|
1520
1692
|
* Always call `live.stop()` when finished.
|
|
1521
1693
|
*/
|
|
1522
1694
|
live() {
|
|
1523
|
-
const recompute = () => groupAndReduce(this.executeRecords(), this.
|
|
1695
|
+
const recompute = () => groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
1524
1696
|
return buildLiveAggregation(recompute, this.upstreams);
|
|
1525
1697
|
}
|
|
1526
1698
|
};
|
|
@@ -1931,10 +2103,16 @@ function coerceRefKey2(value) {
|
|
|
1931
2103
|
Aggregation,
|
|
1932
2104
|
CollectionIndexes,
|
|
1933
2105
|
DEFAULT_JOIN_MAX_ROWS,
|
|
2106
|
+
DanglingReferenceError,
|
|
1934
2107
|
GROUPBY_MAX_CARDINALITY,
|
|
1935
2108
|
GROUPBY_WARN_CARDINALITY,
|
|
2109
|
+
GroupCardinalityError,
|
|
1936
2110
|
GroupedAggregation,
|
|
1937
2111
|
GroupedQuery,
|
|
2112
|
+
GroupedQueryN,
|
|
2113
|
+
IndexRequiredError,
|
|
2114
|
+
IndexWriteFailureError,
|
|
2115
|
+
JoinTooLargeError,
|
|
1938
2116
|
Query,
|
|
1939
2117
|
ScanBuilder,
|
|
1940
2118
|
applyJoins,
|