@prisma-next/adapter-mongo 0.11.0 → 0.12.0-dev.10
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/codec-types.d.mts.map +1 -1
- package/dist/control.d.mts +17 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +46 -2
- package/dist/control.mjs.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{mongo-adapter-DfCmEYHR.mjs → mongo-adapter-DAC5qq3o.mjs} +381 -42
- package/dist/mongo-adapter-DAC5qq3o.mjs.map +1 -0
- package/dist/mongo-adapter-JuKx_-h9.d.mts.map +1 -1
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +4 -2
- package/dist/runtime.mjs.map +1 -1
- package/package.json +38 -25
- package/src/core/marker-ledger.ts +66 -2
- package/src/core/mongo-control-adapter.ts +17 -2
- package/src/core/runner-deps.ts +8 -5
- package/src/exports/control.ts +1 -0
- package/src/exports/runtime.ts +2 -0
- package/src/lowering.ts +261 -5
- package/src/mongo-adapter.ts +200 -71
- package/src/resolve-value.ts +64 -6
- package/dist/mongo-adapter-DfCmEYHR.mjs.map +0 -1
package/src/lowering.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
} from '@prisma-next/mongo-query-ast/execution';
|
|
12
12
|
import { isExprArray, isRecordArgs } from '@prisma-next/mongo-query-ast/execution';
|
|
13
13
|
import type { Document } from '@prisma-next/mongo-value';
|
|
14
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
14
15
|
import { resolveValue } from './resolve-value';
|
|
15
16
|
|
|
16
17
|
// Biome flags `{ then: ... }` as a thenable object (noThenProperty). Build via Object.fromEntries to avoid.
|
|
@@ -138,6 +139,36 @@ export function lowerAggExpr(expr: MongoAggExpr): unknown {
|
|
|
138
139
|
return expr.accept(aggExprLoweringVisitor);
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Structural phase of filter lowering: transforms the filter AST into a
|
|
144
|
+
* plain object without resolving any `MongoParamRef` leaves. Field filter
|
|
145
|
+
* values remain as `MongoValue` (which includes `MongoParamRef`), so the
|
|
146
|
+
* returned document can be passed to `resolveDraftDoc` in the resolve phase.
|
|
147
|
+
* Synchronous — no codec calls.
|
|
148
|
+
*/
|
|
149
|
+
export function structuralLowerFilter(filter: MongoFilterExpr): Record<string, unknown> {
|
|
150
|
+
switch (filter.kind) {
|
|
151
|
+
case 'field':
|
|
152
|
+
return { [filter.field]: { [filter.op]: filter.value } };
|
|
153
|
+
case 'and':
|
|
154
|
+
return { $and: filter.exprs.map((e) => structuralLowerFilter(e)) };
|
|
155
|
+
case 'or':
|
|
156
|
+
return { $or: filter.exprs.map((e) => structuralLowerFilter(e)) };
|
|
157
|
+
case 'not':
|
|
158
|
+
return { $nor: [structuralLowerFilter(filter.expr)] };
|
|
159
|
+
case 'exists':
|
|
160
|
+
return { [filter.field]: { $exists: filter.exists } };
|
|
161
|
+
case 'expr':
|
|
162
|
+
return { $expr: lowerAggExpr(filter.aggExpr) };
|
|
163
|
+
default: {
|
|
164
|
+
const _exhaustive: never = filter;
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Unhandled filter kind: ${blindCast<MongoFilterExpr, 'exhaustive switch fallback for error message'>(_exhaustive).kind}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
141
172
|
export async function lowerFilter(
|
|
142
173
|
filter: MongoFilterExpr,
|
|
143
174
|
codecs: MongoCodecRegistry,
|
|
@@ -158,7 +189,9 @@ export async function lowerFilter(
|
|
|
158
189
|
return { $expr: lowerAggExpr(filter.aggExpr) };
|
|
159
190
|
default: {
|
|
160
191
|
const _exhaustive: never = filter;
|
|
161
|
-
throw new Error(
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Unhandled filter kind: ${blindCast<MongoFilterExpr, 'exhaustive switch fallback for error message'>(_exhaustive).kind}`,
|
|
194
|
+
);
|
|
162
195
|
}
|
|
163
196
|
}
|
|
164
197
|
}
|
|
@@ -167,6 +200,12 @@ function isAggExprNode(value: object): value is MongoAggExpr {
|
|
|
167
200
|
return 'accept' in value && typeof value.accept === 'function';
|
|
168
201
|
}
|
|
169
202
|
|
|
203
|
+
function isAggExprArray(
|
|
204
|
+
val: MongoAggExpr | ReadonlyArray<MongoAggExpr>,
|
|
205
|
+
): val is ReadonlyArray<MongoAggExpr> {
|
|
206
|
+
return Array.isArray(val);
|
|
207
|
+
}
|
|
208
|
+
|
|
170
209
|
function lowerGroupId(groupId: MongoGroupId): unknown {
|
|
171
210
|
if (groupId === null) return null;
|
|
172
211
|
if (isAggExprNode(groupId)) return lowerAggExpr(groupId);
|
|
@@ -178,10 +217,10 @@ function lowerExprRecord(
|
|
|
178
217
|
): Record<string, unknown> {
|
|
179
218
|
const result: Record<string, unknown> = {};
|
|
180
219
|
for (const [key, val] of Object.entries(fields)) {
|
|
181
|
-
if (
|
|
182
|
-
result[key] = val.map((v
|
|
220
|
+
if (isAggExprArray(val)) {
|
|
221
|
+
result[key] = val.map((v) => lowerAggExpr(v));
|
|
183
222
|
} else {
|
|
184
|
-
result[key] = lowerAggExpr(val
|
|
223
|
+
result[key] = lowerAggExpr(val);
|
|
185
224
|
}
|
|
186
225
|
}
|
|
187
226
|
return result;
|
|
@@ -417,7 +456,9 @@ export async function lowerStage(
|
|
|
417
456
|
}
|
|
418
457
|
default: {
|
|
419
458
|
const _exhaustive: never = stage;
|
|
420
|
-
throw new Error(
|
|
459
|
+
throw new Error(
|
|
460
|
+
`Unhandled stage kind: ${blindCast<MongoPipelineStage, 'exhaustive switch fallback for error message'>(_exhaustive).kind}`,
|
|
461
|
+
);
|
|
421
462
|
}
|
|
422
463
|
}
|
|
423
464
|
}
|
|
@@ -429,3 +470,218 @@ export async function lowerPipeline(
|
|
|
429
470
|
): Promise<Array<Record<string, unknown>>> {
|
|
430
471
|
return Promise.all(stages.map((s) => lowerStage(s, codecs, ctx)));
|
|
431
472
|
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Structural phase of stage lowering: mirrors `lowerStage` but defers all
|
|
476
|
+
* `MongoParamRef` resolution. Filter sub-documents within stages (e.g.
|
|
477
|
+
* `$match`, `$geoNear.query`, `$graphLookup.restrictSearchWithMatch`) are
|
|
478
|
+
* produced by `structuralLowerFilter` and therefore retain `MongoParamRef`
|
|
479
|
+
* leaves. Sub-pipelines (e.g. `$lookup.pipeline`, `$facet.*`) recurse via
|
|
480
|
+
* `structuralLowerPipeline`. Synchronous — no codec calls.
|
|
481
|
+
*/
|
|
482
|
+
export function structuralLowerStage(stage: MongoPipelineStage): Record<string, unknown> {
|
|
483
|
+
switch (stage.kind) {
|
|
484
|
+
case 'match':
|
|
485
|
+
return { $match: structuralLowerFilter(stage.filter) };
|
|
486
|
+
case 'project': {
|
|
487
|
+
const projection: Record<string, unknown> = {};
|
|
488
|
+
for (const [key, val] of Object.entries(stage.projection)) {
|
|
489
|
+
projection[key] = lowerProjectionValue(val);
|
|
490
|
+
}
|
|
491
|
+
return { $project: projection };
|
|
492
|
+
}
|
|
493
|
+
case 'sort':
|
|
494
|
+
return { $sort: { ...stage.sort } };
|
|
495
|
+
case 'limit':
|
|
496
|
+
return { $limit: stage.limit };
|
|
497
|
+
case 'skip':
|
|
498
|
+
return { $skip: stage.skip };
|
|
499
|
+
case 'lookup': {
|
|
500
|
+
const lookup: Record<string, unknown> = {
|
|
501
|
+
from: stage.from,
|
|
502
|
+
as: stage.as,
|
|
503
|
+
};
|
|
504
|
+
if (stage.localField !== undefined) lookup['localField'] = stage.localField;
|
|
505
|
+
if (stage.foreignField !== undefined) lookup['foreignField'] = stage.foreignField;
|
|
506
|
+
if (stage.pipeline) {
|
|
507
|
+
lookup['pipeline'] = structuralLowerPipeline(stage.pipeline);
|
|
508
|
+
}
|
|
509
|
+
if (stage.let_) {
|
|
510
|
+
lookup['let'] = lowerExprRecord(stage.let_);
|
|
511
|
+
}
|
|
512
|
+
return { $lookup: lookup };
|
|
513
|
+
}
|
|
514
|
+
case 'unwind': {
|
|
515
|
+
const unwind: Record<string, unknown> = {
|
|
516
|
+
path: stage.path,
|
|
517
|
+
preserveNullAndEmptyArrays: stage.preserveNullAndEmptyArrays,
|
|
518
|
+
};
|
|
519
|
+
if (stage.includeArrayIndex !== undefined) {
|
|
520
|
+
unwind['includeArrayIndex'] = stage.includeArrayIndex;
|
|
521
|
+
}
|
|
522
|
+
return { $unwind: unwind };
|
|
523
|
+
}
|
|
524
|
+
case 'group': {
|
|
525
|
+
const group: Record<string, unknown> = { _id: lowerGroupId(stage.groupId) };
|
|
526
|
+
for (const [key, acc] of Object.entries(stage.accumulators)) {
|
|
527
|
+
group[key] = lowerAggExpr(acc);
|
|
528
|
+
}
|
|
529
|
+
return { $group: group };
|
|
530
|
+
}
|
|
531
|
+
case 'addFields':
|
|
532
|
+
return { $addFields: lowerExprRecord(stage.fields) };
|
|
533
|
+
case 'replaceRoot':
|
|
534
|
+
return { $replaceRoot: { newRoot: lowerAggExpr(stage.newRoot) } };
|
|
535
|
+
case 'count':
|
|
536
|
+
return { $count: stage.field };
|
|
537
|
+
case 'sortByCount':
|
|
538
|
+
return { $sortByCount: lowerAggExpr(stage.expr) };
|
|
539
|
+
case 'sample':
|
|
540
|
+
return { $sample: { size: stage.size } };
|
|
541
|
+
case 'redact':
|
|
542
|
+
return { $redact: lowerAggExpr(stage.expr) };
|
|
543
|
+
case 'out':
|
|
544
|
+
return { $out: stage.db ? { db: stage.db, coll: stage.collection } : stage.collection };
|
|
545
|
+
case 'unionWith': {
|
|
546
|
+
const unionWith: Record<string, unknown> = { coll: stage.collection };
|
|
547
|
+
if (stage.pipeline) {
|
|
548
|
+
unionWith['pipeline'] = structuralLowerPipeline(stage.pipeline);
|
|
549
|
+
}
|
|
550
|
+
return { $unionWith: unionWith };
|
|
551
|
+
}
|
|
552
|
+
case 'bucket': {
|
|
553
|
+
const bucket: Record<string, unknown> = {
|
|
554
|
+
groupBy: lowerAggExpr(stage.groupBy),
|
|
555
|
+
boundaries: [...stage.boundaries],
|
|
556
|
+
};
|
|
557
|
+
if (stage.default_ !== undefined) bucket['default'] = stage.default_;
|
|
558
|
+
if (stage.output) bucket['output'] = lowerExprRecord(stage.output);
|
|
559
|
+
return { $bucket: bucket };
|
|
560
|
+
}
|
|
561
|
+
case 'bucketAuto': {
|
|
562
|
+
const bucketAuto: Record<string, unknown> = {
|
|
563
|
+
groupBy: lowerAggExpr(stage.groupBy),
|
|
564
|
+
buckets: stage.buckets,
|
|
565
|
+
};
|
|
566
|
+
if (stage.output) bucketAuto['output'] = lowerExprRecord(stage.output);
|
|
567
|
+
if (stage.granularity !== undefined) bucketAuto['granularity'] = stage.granularity;
|
|
568
|
+
return { $bucketAuto: bucketAuto };
|
|
569
|
+
}
|
|
570
|
+
case 'geoNear': {
|
|
571
|
+
const geoNear: Record<string, unknown> = {
|
|
572
|
+
near: stage.near,
|
|
573
|
+
distanceField: stage.distanceField,
|
|
574
|
+
};
|
|
575
|
+
if (stage.spherical !== undefined) geoNear['spherical'] = stage.spherical;
|
|
576
|
+
if (stage.maxDistance !== undefined) geoNear['maxDistance'] = stage.maxDistance;
|
|
577
|
+
if (stage.minDistance !== undefined) geoNear['minDistance'] = stage.minDistance;
|
|
578
|
+
if (stage.query) geoNear['query'] = structuralLowerFilter(stage.query);
|
|
579
|
+
if (stage.key !== undefined) geoNear['key'] = stage.key;
|
|
580
|
+
if (stage.distanceMultiplier !== undefined)
|
|
581
|
+
geoNear['distanceMultiplier'] = stage.distanceMultiplier;
|
|
582
|
+
if (stage.includeLocs !== undefined) geoNear['includeLocs'] = stage.includeLocs;
|
|
583
|
+
return { $geoNear: geoNear };
|
|
584
|
+
}
|
|
585
|
+
case 'facet': {
|
|
586
|
+
const facet: Record<string, unknown> = {};
|
|
587
|
+
for (const [key, pipeline] of Object.entries(stage.facets)) {
|
|
588
|
+
facet[key] = structuralLowerPipeline(pipeline);
|
|
589
|
+
}
|
|
590
|
+
return { $facet: facet };
|
|
591
|
+
}
|
|
592
|
+
case 'graphLookup': {
|
|
593
|
+
const graphLookup: Record<string, unknown> = {
|
|
594
|
+
from: stage.from,
|
|
595
|
+
startWith: lowerAggExpr(stage.startWith),
|
|
596
|
+
connectFromField: stage.connectFromField,
|
|
597
|
+
connectToField: stage.connectToField,
|
|
598
|
+
as: stage.as,
|
|
599
|
+
};
|
|
600
|
+
if (stage.maxDepth !== undefined) graphLookup['maxDepth'] = stage.maxDepth;
|
|
601
|
+
if (stage.depthField !== undefined) graphLookup['depthField'] = stage.depthField;
|
|
602
|
+
if (stage.restrictSearchWithMatch)
|
|
603
|
+
graphLookup['restrictSearchWithMatch'] = structuralLowerFilter(
|
|
604
|
+
stage.restrictSearchWithMatch,
|
|
605
|
+
);
|
|
606
|
+
return { $graphLookup: graphLookup };
|
|
607
|
+
}
|
|
608
|
+
case 'merge': {
|
|
609
|
+
const merge: Record<string, unknown> = { into: stage.into };
|
|
610
|
+
if (stage.on !== undefined) merge['on'] = stage.on;
|
|
611
|
+
if (stage.whenMatched !== undefined) {
|
|
612
|
+
merge['whenMatched'] = Array.isArray(stage.whenMatched)
|
|
613
|
+
? structuralLowerPipeline(stage.whenMatched)
|
|
614
|
+
: stage.whenMatched;
|
|
615
|
+
}
|
|
616
|
+
if (stage.whenNotMatched !== undefined) merge['whenNotMatched'] = stage.whenNotMatched;
|
|
617
|
+
return { $merge: merge };
|
|
618
|
+
}
|
|
619
|
+
case 'setWindowFields': {
|
|
620
|
+
const swf: Record<string, unknown> = {};
|
|
621
|
+
if (stage.partitionBy) swf['partitionBy'] = lowerAggExpr(stage.partitionBy);
|
|
622
|
+
if (stage.sortBy) swf['sortBy'] = { ...stage.sortBy };
|
|
623
|
+
const output: Record<string, unknown> = {};
|
|
624
|
+
for (const [key, wf] of Object.entries(stage.output)) {
|
|
625
|
+
output[key] = lowerWindowField(wf);
|
|
626
|
+
}
|
|
627
|
+
swf['output'] = output;
|
|
628
|
+
return { $setWindowFields: swf };
|
|
629
|
+
}
|
|
630
|
+
case 'densify': {
|
|
631
|
+
const densify: Record<string, unknown> = {
|
|
632
|
+
field: stage.field,
|
|
633
|
+
range: { ...stage.range },
|
|
634
|
+
};
|
|
635
|
+
if (stage.partitionByFields) densify['partitionByFields'] = [...stage.partitionByFields];
|
|
636
|
+
return { $densify: densify };
|
|
637
|
+
}
|
|
638
|
+
case 'fill': {
|
|
639
|
+
const fill: Record<string, unknown> = {};
|
|
640
|
+
if (stage.partitionBy) fill['partitionBy'] = lowerAggExpr(stage.partitionBy);
|
|
641
|
+
if (stage.partitionByFields) fill['partitionByFields'] = [...stage.partitionByFields];
|
|
642
|
+
if (stage.sortBy) fill['sortBy'] = { ...stage.sortBy };
|
|
643
|
+
const output: Record<string, unknown> = {};
|
|
644
|
+
for (const [key, fo] of Object.entries(stage.output)) {
|
|
645
|
+
const entry: Record<string, unknown> = {};
|
|
646
|
+
if (fo.method !== undefined) entry['method'] = fo.method;
|
|
647
|
+
if (fo.value !== undefined) entry['value'] = lowerAggExpr(fo.value);
|
|
648
|
+
output[key] = entry;
|
|
649
|
+
}
|
|
650
|
+
fill['output'] = output;
|
|
651
|
+
return { $fill: fill };
|
|
652
|
+
}
|
|
653
|
+
case 'search': {
|
|
654
|
+
const search: Record<string, unknown> = { ...stage.config };
|
|
655
|
+
if (stage.index !== undefined) search['index'] = stage.index;
|
|
656
|
+
return { $search: search };
|
|
657
|
+
}
|
|
658
|
+
case 'searchMeta': {
|
|
659
|
+
const searchMeta: Record<string, unknown> = { ...stage.config };
|
|
660
|
+
if (stage.index !== undefined) searchMeta['index'] = stage.index;
|
|
661
|
+
return { $searchMeta: searchMeta };
|
|
662
|
+
}
|
|
663
|
+
case 'vectorSearch': {
|
|
664
|
+
const vs: Record<string, unknown> = {
|
|
665
|
+
index: stage.index,
|
|
666
|
+
path: stage.path,
|
|
667
|
+
queryVector: [...stage.queryVector],
|
|
668
|
+
numCandidates: stage.numCandidates,
|
|
669
|
+
limit: stage.limit,
|
|
670
|
+
};
|
|
671
|
+
if (stage.filter) vs['filter'] = { ...stage.filter };
|
|
672
|
+
return { $vectorSearch: vs };
|
|
673
|
+
}
|
|
674
|
+
default: {
|
|
675
|
+
const _exhaustive: never = stage;
|
|
676
|
+
throw new Error(
|
|
677
|
+
`Unhandled stage kind: ${blindCast<MongoPipelineStage, 'exhaustive switch fallback for error message'>(_exhaustive).kind}`,
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
export function structuralLowerPipeline(
|
|
684
|
+
stages: ReadonlyArray<MongoPipelineStage>,
|
|
685
|
+
): Array<Record<string, unknown>> {
|
|
686
|
+
return stages.map((s) => structuralLowerStage(s));
|
|
687
|
+
}
|
package/src/mongo-adapter.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { CodecCallContext } from '@prisma-next/framework-components/codec';
|
|
2
2
|
import type { MongoCodecRegistry } from '@prisma-next/mongo-codec';
|
|
3
|
-
import type { MongoAdapter } from '@prisma-next/mongo-lowering';
|
|
3
|
+
import type { MongoAdapter, MongoLoweredDraft } from '@prisma-next/mongo-lowering';
|
|
4
4
|
import type {
|
|
5
5
|
MongoQueryPlan,
|
|
6
6
|
MongoUpdatePipelineStage,
|
|
7
7
|
MongoUpdateSpec,
|
|
8
8
|
} from '@prisma-next/mongo-query-ast/execution';
|
|
9
|
-
import type { Document, MongoExpr } from '@prisma-next/mongo-value';
|
|
10
9
|
import type { AnyMongoWireCommand } from '@prisma-next/mongo-wire';
|
|
11
10
|
import {
|
|
12
11
|
AggregateWireCommand,
|
|
@@ -19,9 +18,10 @@ import {
|
|
|
19
18
|
UpdateManyWireCommand,
|
|
20
19
|
UpdateOneWireCommand,
|
|
21
20
|
} from '@prisma-next/mongo-wire';
|
|
21
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
22
22
|
import { buildStandardCodecRegistry } from './core/codecs';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { structuralLowerFilter, structuralLowerPipeline } from './lowering';
|
|
24
|
+
import { resolveDraftDoc } from './resolve-value';
|
|
25
25
|
|
|
26
26
|
function isUpdatePipeline(
|
|
27
27
|
update: MongoUpdateSpec,
|
|
@@ -29,6 +29,23 @@ function isUpdatePipeline(
|
|
|
29
29
|
return Array.isArray(update);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function isDraftUpdatePipeline(
|
|
33
|
+
update: Record<string, unknown> | ReadonlyArray<Record<string, unknown>>,
|
|
34
|
+
): update is ReadonlyArray<Record<string, unknown>> {
|
|
35
|
+
return Array.isArray(update);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function resolveUpdate(
|
|
39
|
+
update: Record<string, unknown> | ReadonlyArray<Record<string, unknown>>,
|
|
40
|
+
codecs: MongoCodecRegistry,
|
|
41
|
+
ctx: CodecCallContext,
|
|
42
|
+
): Promise<Record<string, unknown> | ReadonlyArray<Record<string, unknown>>> {
|
|
43
|
+
if (isDraftUpdatePipeline(update)) {
|
|
44
|
+
return Promise.all(update.map((stage) => resolveDraftDoc(stage, codecs, ctx)));
|
|
45
|
+
}
|
|
46
|
+
return resolveDraftDoc(update, codecs, ctx);
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
class MongoAdapterImpl implements MongoAdapter {
|
|
33
50
|
readonly #codecs: MongoCodecRegistry;
|
|
34
51
|
|
|
@@ -36,125 +53,237 @@ class MongoAdapterImpl implements MongoAdapter {
|
|
|
36
53
|
this.#codecs = codecs;
|
|
37
54
|
}
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
structuralLower(plan: MongoQueryPlan): MongoLoweredDraft {
|
|
57
|
+
const { command } = plan;
|
|
58
|
+
switch (command.kind) {
|
|
59
|
+
case 'insertOne':
|
|
60
|
+
return { kind: 'insertOne', collection: command.collection, document: command.document };
|
|
61
|
+
case 'insertMany':
|
|
62
|
+
return {
|
|
63
|
+
kind: 'insertMany',
|
|
64
|
+
collection: command.collection,
|
|
65
|
+
documents: command.documents,
|
|
66
|
+
};
|
|
67
|
+
case 'updateOne':
|
|
68
|
+
return {
|
|
69
|
+
kind: 'updateOne',
|
|
70
|
+
collection: command.collection,
|
|
71
|
+
filter: structuralLowerFilter(command.filter),
|
|
72
|
+
update: isUpdatePipeline(command.update)
|
|
73
|
+
? structuralLowerPipeline(command.update)
|
|
74
|
+
: command.update,
|
|
75
|
+
upsert: command.upsert,
|
|
76
|
+
};
|
|
77
|
+
case 'updateMany':
|
|
78
|
+
return {
|
|
79
|
+
kind: 'updateMany',
|
|
80
|
+
collection: command.collection,
|
|
81
|
+
filter: structuralLowerFilter(command.filter),
|
|
82
|
+
update: isUpdatePipeline(command.update)
|
|
83
|
+
? structuralLowerPipeline(command.update)
|
|
84
|
+
: command.update,
|
|
85
|
+
upsert: command.upsert,
|
|
86
|
+
};
|
|
87
|
+
case 'deleteOne':
|
|
88
|
+
return {
|
|
89
|
+
kind: 'deleteOne',
|
|
90
|
+
collection: command.collection,
|
|
91
|
+
filter: structuralLowerFilter(command.filter),
|
|
92
|
+
};
|
|
93
|
+
case 'deleteMany':
|
|
94
|
+
return {
|
|
95
|
+
kind: 'deleteMany',
|
|
96
|
+
collection: command.collection,
|
|
97
|
+
filter: structuralLowerFilter(command.filter),
|
|
98
|
+
};
|
|
99
|
+
case 'findOneAndUpdate':
|
|
100
|
+
return {
|
|
101
|
+
kind: 'findOneAndUpdate',
|
|
102
|
+
collection: command.collection,
|
|
103
|
+
filter: structuralLowerFilter(command.filter),
|
|
104
|
+
update: isUpdatePipeline(command.update)
|
|
105
|
+
? structuralLowerPipeline(command.update)
|
|
106
|
+
: command.update,
|
|
107
|
+
upsert: command.upsert,
|
|
108
|
+
sort: command.sort,
|
|
109
|
+
returnDocument: command.returnDocument,
|
|
110
|
+
};
|
|
111
|
+
case 'findOneAndDelete':
|
|
112
|
+
return {
|
|
113
|
+
kind: 'findOneAndDelete',
|
|
114
|
+
collection: command.collection,
|
|
115
|
+
filter: structuralLowerFilter(command.filter),
|
|
116
|
+
sort: command.sort,
|
|
117
|
+
};
|
|
118
|
+
case 'aggregate':
|
|
119
|
+
return {
|
|
120
|
+
kind: 'aggregate',
|
|
121
|
+
collection: command.collection,
|
|
122
|
+
pipeline: structuralLowerPipeline(command.pipeline),
|
|
123
|
+
};
|
|
124
|
+
case 'rawAggregate':
|
|
125
|
+
return { kind: 'rawAggregate', collection: command.collection, pipeline: command.pipeline };
|
|
126
|
+
case 'rawInsertOne':
|
|
127
|
+
return {
|
|
128
|
+
kind: 'rawInsertOne',
|
|
129
|
+
collection: command.collection,
|
|
130
|
+
document: command.document,
|
|
131
|
+
};
|
|
132
|
+
case 'rawInsertMany':
|
|
133
|
+
return {
|
|
134
|
+
kind: 'rawInsertMany',
|
|
135
|
+
collection: command.collection,
|
|
136
|
+
documents: command.documents,
|
|
137
|
+
};
|
|
138
|
+
case 'rawUpdateOne':
|
|
139
|
+
return {
|
|
140
|
+
kind: 'rawUpdateOne',
|
|
141
|
+
collection: command.collection,
|
|
142
|
+
filter: command.filter,
|
|
143
|
+
update: command.update,
|
|
144
|
+
};
|
|
145
|
+
case 'rawUpdateMany':
|
|
146
|
+
return {
|
|
147
|
+
kind: 'rawUpdateMany',
|
|
148
|
+
collection: command.collection,
|
|
149
|
+
filter: command.filter,
|
|
150
|
+
update: command.update,
|
|
151
|
+
};
|
|
152
|
+
case 'rawDeleteOne':
|
|
153
|
+
return { kind: 'rawDeleteOne', collection: command.collection, filter: command.filter };
|
|
154
|
+
case 'rawDeleteMany':
|
|
155
|
+
return { kind: 'rawDeleteMany', collection: command.collection, filter: command.filter };
|
|
156
|
+
case 'rawFindOneAndUpdate':
|
|
157
|
+
return {
|
|
158
|
+
kind: 'rawFindOneAndUpdate',
|
|
159
|
+
collection: command.collection,
|
|
160
|
+
filter: command.filter,
|
|
161
|
+
update: command.update,
|
|
162
|
+
upsert: command.upsert,
|
|
163
|
+
sort: command.sort,
|
|
164
|
+
returnDocument: command.returnDocument,
|
|
165
|
+
};
|
|
166
|
+
case 'rawFindOneAndDelete':
|
|
167
|
+
return {
|
|
168
|
+
kind: 'rawFindOneAndDelete',
|
|
169
|
+
collection: command.collection,
|
|
170
|
+
filter: command.filter,
|
|
171
|
+
sort: command.sort,
|
|
172
|
+
};
|
|
173
|
+
// v8 ignore next 4
|
|
174
|
+
default: {
|
|
175
|
+
const _exhaustive: never = command;
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Unknown command kind: ${blindCast<{ kind: string }, 'exhaustive switch fallback for error message'>(_exhaustive).kind}`,
|
|
178
|
+
);
|
|
49
179
|
}
|
|
50
180
|
}
|
|
51
|
-
return result;
|
|
52
181
|
}
|
|
53
182
|
|
|
54
|
-
async
|
|
55
|
-
|
|
183
|
+
async resolveParams(
|
|
184
|
+
draft: MongoLoweredDraft,
|
|
56
185
|
ctx: CodecCallContext,
|
|
57
|
-
): Promise<
|
|
58
|
-
|
|
59
|
-
return Promise.all(update.map((stage) => lowerStage(stage, this.#codecs, ctx)));
|
|
60
|
-
}
|
|
61
|
-
return this.#resolveDocument(update, ctx);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async lower(plan: MongoQueryPlan, ctx: CodecCallContext): Promise<AnyMongoWireCommand> {
|
|
65
|
-
const { command } = plan;
|
|
66
|
-
switch (command.kind) {
|
|
186
|
+
): Promise<AnyMongoWireCommand> {
|
|
187
|
+
switch (draft.kind) {
|
|
67
188
|
case 'insertOne':
|
|
68
189
|
return new InsertOneWireCommand(
|
|
69
|
-
|
|
70
|
-
await
|
|
190
|
+
draft.collection,
|
|
191
|
+
await resolveDraftDoc(draft.document, this.#codecs, ctx),
|
|
192
|
+
);
|
|
193
|
+
case 'insertMany':
|
|
194
|
+
return new InsertManyWireCommand(
|
|
195
|
+
draft.collection,
|
|
196
|
+
await Promise.all(draft.documents.map((doc) => resolveDraftDoc(doc, this.#codecs, ctx))),
|
|
71
197
|
);
|
|
72
198
|
case 'updateOne': {
|
|
73
199
|
const [filter, update] = await Promise.all([
|
|
74
|
-
|
|
75
|
-
|
|
200
|
+
resolveDraftDoc(draft.filter, this.#codecs, ctx),
|
|
201
|
+
resolveUpdate(draft.update, this.#codecs, ctx),
|
|
76
202
|
]);
|
|
77
|
-
return new UpdateOneWireCommand(
|
|
203
|
+
return new UpdateOneWireCommand(draft.collection, filter, update, draft.upsert);
|
|
78
204
|
}
|
|
79
|
-
case 'insertMany':
|
|
80
|
-
return new InsertManyWireCommand(
|
|
81
|
-
command.collection,
|
|
82
|
-
await Promise.all(command.documents.map((doc) => this.#resolveDocument(doc, ctx))),
|
|
83
|
-
);
|
|
84
205
|
case 'updateMany': {
|
|
85
206
|
const [filter, update] = await Promise.all([
|
|
86
|
-
|
|
87
|
-
|
|
207
|
+
resolveDraftDoc(draft.filter, this.#codecs, ctx),
|
|
208
|
+
resolveUpdate(draft.update, this.#codecs, ctx),
|
|
88
209
|
]);
|
|
89
|
-
return new UpdateManyWireCommand(
|
|
210
|
+
return new UpdateManyWireCommand(draft.collection, filter, update, draft.upsert);
|
|
90
211
|
}
|
|
91
212
|
case 'deleteOne':
|
|
92
213
|
return new DeleteOneWireCommand(
|
|
93
|
-
|
|
94
|
-
await
|
|
214
|
+
draft.collection,
|
|
215
|
+
await resolveDraftDoc(draft.filter, this.#codecs, ctx),
|
|
95
216
|
);
|
|
96
217
|
case 'deleteMany':
|
|
97
218
|
return new DeleteManyWireCommand(
|
|
98
|
-
|
|
99
|
-
await
|
|
219
|
+
draft.collection,
|
|
220
|
+
await resolveDraftDoc(draft.filter, this.#codecs, ctx),
|
|
100
221
|
);
|
|
101
222
|
case 'findOneAndUpdate': {
|
|
102
223
|
const [filter, update] = await Promise.all([
|
|
103
|
-
|
|
104
|
-
|
|
224
|
+
resolveDraftDoc(draft.filter, this.#codecs, ctx),
|
|
225
|
+
resolveUpdate(draft.update, this.#codecs, ctx),
|
|
105
226
|
]);
|
|
106
227
|
return new FindOneAndUpdateWireCommand(
|
|
107
|
-
|
|
228
|
+
draft.collection,
|
|
108
229
|
filter,
|
|
109
230
|
update,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
231
|
+
draft.upsert,
|
|
232
|
+
draft.sort,
|
|
233
|
+
draft.returnDocument,
|
|
113
234
|
);
|
|
114
235
|
}
|
|
115
236
|
case 'findOneAndDelete':
|
|
116
237
|
return new FindOneAndDeleteWireCommand(
|
|
117
|
-
|
|
118
|
-
await
|
|
119
|
-
|
|
238
|
+
draft.collection,
|
|
239
|
+
await resolveDraftDoc(draft.filter, this.#codecs, ctx),
|
|
240
|
+
draft.sort,
|
|
120
241
|
);
|
|
121
242
|
case 'aggregate':
|
|
122
243
|
return new AggregateWireCommand(
|
|
123
|
-
|
|
124
|
-
await
|
|
244
|
+
draft.collection,
|
|
245
|
+
await Promise.all(
|
|
246
|
+
draft.pipeline.map((stage) => resolveDraftDoc(stage, this.#codecs, ctx)),
|
|
247
|
+
),
|
|
125
248
|
);
|
|
126
249
|
case 'rawAggregate':
|
|
127
|
-
return new AggregateWireCommand(
|
|
250
|
+
return new AggregateWireCommand(draft.collection, draft.pipeline);
|
|
128
251
|
case 'rawInsertOne':
|
|
129
|
-
return new InsertOneWireCommand(
|
|
252
|
+
return new InsertOneWireCommand(draft.collection, draft.document);
|
|
130
253
|
case 'rawInsertMany':
|
|
131
|
-
return new InsertManyWireCommand(
|
|
254
|
+
return new InsertManyWireCommand(draft.collection, draft.documents);
|
|
132
255
|
case 'rawUpdateOne':
|
|
133
|
-
return new UpdateOneWireCommand(
|
|
256
|
+
return new UpdateOneWireCommand(draft.collection, draft.filter, draft.update);
|
|
134
257
|
case 'rawUpdateMany':
|
|
135
|
-
return new UpdateManyWireCommand(
|
|
258
|
+
return new UpdateManyWireCommand(draft.collection, draft.filter, draft.update);
|
|
136
259
|
case 'rawDeleteOne':
|
|
137
|
-
return new DeleteOneWireCommand(
|
|
260
|
+
return new DeleteOneWireCommand(draft.collection, draft.filter);
|
|
138
261
|
case 'rawDeleteMany':
|
|
139
|
-
return new DeleteManyWireCommand(
|
|
262
|
+
return new DeleteManyWireCommand(draft.collection, draft.filter);
|
|
140
263
|
case 'rawFindOneAndUpdate':
|
|
141
264
|
return new FindOneAndUpdateWireCommand(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
265
|
+
draft.collection,
|
|
266
|
+
draft.filter,
|
|
267
|
+
draft.update,
|
|
268
|
+
draft.upsert,
|
|
269
|
+
draft.sort,
|
|
270
|
+
draft.returnDocument,
|
|
148
271
|
);
|
|
149
272
|
case 'rawFindOneAndDelete':
|
|
150
|
-
return new FindOneAndDeleteWireCommand(
|
|
273
|
+
return new FindOneAndDeleteWireCommand(draft.collection, draft.filter, draft.sort);
|
|
151
274
|
// v8 ignore next 4
|
|
152
275
|
default: {
|
|
153
|
-
const _exhaustive: never =
|
|
154
|
-
throw new Error(
|
|
276
|
+
const _exhaustive: never = draft;
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Unknown draft kind: ${blindCast<{ kind: string }, 'exhaustive switch fallback for error message'>(_exhaustive).kind}`,
|
|
279
|
+
);
|
|
155
280
|
}
|
|
156
281
|
}
|
|
157
282
|
}
|
|
283
|
+
|
|
284
|
+
lower(plan: MongoQueryPlan, ctx: CodecCallContext): Promise<AnyMongoWireCommand> {
|
|
285
|
+
return this.resolveParams(this.structuralLower(plan), ctx);
|
|
286
|
+
}
|
|
158
287
|
}
|
|
159
288
|
|
|
160
289
|
/**
|