@prisma-next/mongo-orm 0.3.0-dev.135 → 0.3.0-dev.162
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/index.d.mts +240 -20
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +581 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -5
- package/src/collection-state.ts +29 -0
- package/src/collection.ts +821 -0
- package/src/compile.ts +86 -0
- package/src/executor.ts +6 -0
- package/src/exports/index.ts +26 -5
- package/src/field-accessor.ts +265 -0
- package/src/mongo-orm.ts +19 -124
- package/src/mongo-raw.ts +33 -0
- package/src/raw-collection.ts +95 -0
- package/src/types.ts +126 -41
package/dist/index.mjs
CHANGED
|
@@ -1,77 +1,608 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AsyncIterableResult } from "@prisma-next/framework-components/runtime";
|
|
2
|
+
import { AggregateCommand, DeleteManyCommand, FindOneAndDeleteCommand, FindOneAndUpdateCommand, InsertManyCommand, InsertOneCommand, MongoAndExpr, MongoFieldFilter, MongoLimitStage, MongoLookupStage, MongoMatchStage, MongoProjectStage, MongoSkipStage, MongoSortStage, MongoUnwindStage, RawAggregateCommand, RawDeleteManyCommand, RawDeleteOneCommand, RawFindOneAndDeleteCommand, RawFindOneAndUpdateCommand, RawInsertManyCommand, RawInsertOneCommand, RawUpdateManyCommand, RawUpdateOneCommand, UpdateManyCommand, isMongoFilterExpr } from "@prisma-next/mongo-query-ast/execution";
|
|
3
|
+
import { MongoParamRef } from "@prisma-next/mongo-value";
|
|
2
4
|
|
|
3
|
-
//#region src/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
//#region src/collection-state.ts
|
|
6
|
+
function emptyCollectionState() {
|
|
7
|
+
return {
|
|
8
|
+
filters: [],
|
|
9
|
+
includes: [],
|
|
10
|
+
orderBy: void 0,
|
|
11
|
+
selectedFields: void 0,
|
|
12
|
+
limit: void 0,
|
|
13
|
+
offset: void 0
|
|
14
|
+
};
|
|
12
15
|
}
|
|
13
|
-
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/compile.ts
|
|
19
|
+
function compileIncludes(includes) {
|
|
14
20
|
const stages = [];
|
|
15
|
-
for (const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
const targetCollection = resolveCollection(targetModel, ref.to);
|
|
24
|
-
stages.push({ $lookup: {
|
|
25
|
-
from: targetCollection,
|
|
26
|
-
localField: ref.on.localFields[0],
|
|
27
|
-
foreignField: ref.on.targetFields[0],
|
|
28
|
-
as: relName
|
|
29
|
-
} });
|
|
30
|
-
if (ref.cardinality === "N:1" || ref.cardinality === "1:1") stages.push({ $unwind: {
|
|
31
|
-
path: `$${relName}`,
|
|
32
|
-
preserveNullAndEmptyArrays: true
|
|
33
|
-
} });
|
|
21
|
+
for (const inc of includes) {
|
|
22
|
+
stages.push(new MongoLookupStage({
|
|
23
|
+
from: inc.from,
|
|
24
|
+
localField: inc.localField,
|
|
25
|
+
foreignField: inc.foreignField,
|
|
26
|
+
as: inc.relationName
|
|
27
|
+
}));
|
|
28
|
+
if (inc.cardinality === "N:1" || inc.cardinality === "1:1") stages.push(new MongoUnwindStage(`$${inc.relationName}`, true));
|
|
34
29
|
}
|
|
35
30
|
return stages;
|
|
36
31
|
}
|
|
37
|
-
|
|
32
|
+
function compileMongoQuery(collection, state, storageHash) {
|
|
33
|
+
const stages = [];
|
|
34
|
+
const singleFilter = state.filters.length === 1 ? state.filters[0] : void 0;
|
|
35
|
+
if (singleFilter) stages.push(new MongoMatchStage(singleFilter));
|
|
36
|
+
else if (state.filters.length > 1) stages.push(new MongoMatchStage(MongoAndExpr.of([...state.filters])));
|
|
37
|
+
if (state.includes.length > 0) stages.push(...compileIncludes(state.includes));
|
|
38
|
+
if (state.orderBy) stages.push(new MongoSortStage(state.orderBy));
|
|
39
|
+
if (state.offset !== void 0) stages.push(new MongoSkipStage(state.offset));
|
|
40
|
+
if (state.limit !== void 0) stages.push(new MongoLimitStage(state.limit));
|
|
41
|
+
if (state.selectedFields && state.selectedFields.length > 0) {
|
|
42
|
+
const projection = {};
|
|
43
|
+
for (const field of state.selectedFields) projection[field] = 1;
|
|
44
|
+
if (!Object.hasOwn(projection, "_id")) projection["_id"] = 0;
|
|
45
|
+
stages.push(new MongoProjectStage(projection));
|
|
46
|
+
}
|
|
47
|
+
const meta = {
|
|
48
|
+
target: "mongo",
|
|
49
|
+
storageHash,
|
|
50
|
+
lane: "mongo-orm",
|
|
51
|
+
paramDescriptors: []
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
collection,
|
|
55
|
+
command: new AggregateCommand(collection, stages),
|
|
56
|
+
meta
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/field-accessor.ts
|
|
62
|
+
function createFieldExpression(fieldPath) {
|
|
63
|
+
return {
|
|
64
|
+
set(value) {
|
|
65
|
+
return {
|
|
66
|
+
operator: "$set",
|
|
67
|
+
field: fieldPath,
|
|
68
|
+
value: new MongoParamRef(value)
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
unset() {
|
|
72
|
+
return {
|
|
73
|
+
operator: "$unset",
|
|
74
|
+
field: fieldPath,
|
|
75
|
+
value: new MongoParamRef("")
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
inc(value) {
|
|
79
|
+
return {
|
|
80
|
+
operator: "$inc",
|
|
81
|
+
field: fieldPath,
|
|
82
|
+
value: new MongoParamRef(value)
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
mul(value) {
|
|
86
|
+
return {
|
|
87
|
+
operator: "$mul",
|
|
88
|
+
field: fieldPath,
|
|
89
|
+
value: new MongoParamRef(value)
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
push(value) {
|
|
93
|
+
return {
|
|
94
|
+
operator: "$push",
|
|
95
|
+
field: fieldPath,
|
|
96
|
+
value: new MongoParamRef(value)
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
pull(match) {
|
|
100
|
+
return {
|
|
101
|
+
operator: "$pull",
|
|
102
|
+
field: fieldPath,
|
|
103
|
+
value: new MongoParamRef(match)
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
addToSet(value) {
|
|
107
|
+
return {
|
|
108
|
+
operator: "$addToSet",
|
|
109
|
+
field: fieldPath,
|
|
110
|
+
value: new MongoParamRef(value)
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
pop(end) {
|
|
114
|
+
return {
|
|
115
|
+
operator: "$pop",
|
|
116
|
+
field: fieldPath,
|
|
117
|
+
value: new MongoParamRef(end)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function createFieldAccessor() {
|
|
123
|
+
return new Proxy((() => {}), {
|
|
124
|
+
get(_target, prop) {
|
|
125
|
+
return createFieldExpression(prop);
|
|
126
|
+
},
|
|
127
|
+
apply(_target, _thisArg, args) {
|
|
128
|
+
return createFieldExpression(args[0]);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function compileFieldOperations(ops, wrapValue) {
|
|
133
|
+
const grouped = {};
|
|
134
|
+
for (const op of ops) {
|
|
135
|
+
let group = grouped[op.operator];
|
|
136
|
+
if (!group) {
|
|
137
|
+
group = {};
|
|
138
|
+
grouped[op.operator] = group;
|
|
139
|
+
}
|
|
140
|
+
group[op.field] = wrapValue(op.field, op.value, op.operator);
|
|
141
|
+
}
|
|
142
|
+
return grouped;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/collection.ts
|
|
147
|
+
function resolveCollectionName(model, modelName) {
|
|
148
|
+
return model.storage.collection ?? modelName;
|
|
149
|
+
}
|
|
150
|
+
var MongoCollectionImpl = class MongoCollectionImpl {
|
|
38
151
|
#contract;
|
|
39
152
|
#modelName;
|
|
40
153
|
#executor;
|
|
154
|
+
#collectionName;
|
|
155
|
+
#state;
|
|
156
|
+
#variantName;
|
|
41
157
|
constructor(contract, modelName, executor) {
|
|
42
158
|
this.#contract = contract;
|
|
43
159
|
this.#modelName = modelName;
|
|
44
160
|
this.#executor = executor;
|
|
161
|
+
const model = contract.models[modelName];
|
|
162
|
+
this.#collectionName = resolveCollectionName(model, modelName);
|
|
163
|
+
this.#state = emptyCollectionState();
|
|
45
164
|
}
|
|
46
|
-
|
|
165
|
+
variant(variantName) {
|
|
47
166
|
const model = this.#contract.models[this.#modelName];
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
167
|
+
if (!model?.discriminator || !model.variants) return this;
|
|
168
|
+
const variantEntry = model.variants[variantName];
|
|
169
|
+
if (!variantEntry) return this;
|
|
170
|
+
const filter = MongoFieldFilter.eq(model.discriminator.field, new MongoParamRef(variantEntry.value));
|
|
171
|
+
return this.#cloneWithVariant({ filters: [...this.#state.filters, filter] }, variantName);
|
|
172
|
+
}
|
|
173
|
+
where(filter) {
|
|
174
|
+
if (isMongoFilterExpr(filter)) return this.#clone({ filters: [...this.#state.filters, filter] });
|
|
175
|
+
const compiled = this.#compileWhereObject(filter);
|
|
176
|
+
return this.#clone({ filters: [...this.#state.filters, ...compiled] });
|
|
177
|
+
}
|
|
178
|
+
select(...fields) {
|
|
179
|
+
return this.#clone({ selectedFields: [...this.#state.selectedFields ?? [], ...fields] });
|
|
180
|
+
}
|
|
181
|
+
include(relationName) {
|
|
182
|
+
const relation = this.#contract.models[this.#modelName].relations?.[relationName];
|
|
183
|
+
if (!relation) throw new Error(`Unknown relation "${relationName}" on model "${this.#modelName}"`);
|
|
184
|
+
if (!("on" in relation)) throw new Error(`Relation "${relationName}" is an embed relation — only reference relations can be included`);
|
|
185
|
+
const ref = relation;
|
|
186
|
+
const localField = ref.on.localFields[0];
|
|
187
|
+
const foreignField = ref.on.targetFields[0];
|
|
188
|
+
if (!localField || !foreignField || ref.on.localFields.length !== 1 || ref.on.targetFields.length !== 1) throw new Error(`Compound references are not yet supported: relation "${relationName}"`);
|
|
189
|
+
const targetModel = this.#contract.models[ref.to];
|
|
190
|
+
if (!targetModel) throw new Error(`Target model "${ref.to}" not found for relation "${relationName}"`);
|
|
191
|
+
const includeExpr = {
|
|
192
|
+
relationName,
|
|
193
|
+
from: resolveCollectionName(targetModel, ref.to),
|
|
194
|
+
localField,
|
|
195
|
+
foreignField,
|
|
196
|
+
cardinality: ref.cardinality
|
|
64
197
|
};
|
|
198
|
+
return this.#clone({ includes: [...this.#state.includes, includeExpr] });
|
|
199
|
+
}
|
|
200
|
+
orderBy(spec) {
|
|
201
|
+
const merged = {
|
|
202
|
+
...this.#state.orderBy,
|
|
203
|
+
...spec
|
|
204
|
+
};
|
|
205
|
+
return this.#clone({ orderBy: merged });
|
|
206
|
+
}
|
|
207
|
+
take(n) {
|
|
208
|
+
return this.#clone({ limit: n });
|
|
209
|
+
}
|
|
210
|
+
skip(n) {
|
|
211
|
+
return this.#clone({ offset: n });
|
|
212
|
+
}
|
|
213
|
+
all() {
|
|
214
|
+
return this.#execute();
|
|
215
|
+
}
|
|
216
|
+
async first() {
|
|
217
|
+
const result = this.#clone({ limit: 1 }).#execute();
|
|
218
|
+
for await (const row of result) return row;
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
async create(data) {
|
|
222
|
+
this.#rejectIncludes("create");
|
|
223
|
+
const normalized = this.#injectDiscriminator(this.#stripUndefined(data));
|
|
224
|
+
const document = this.#toDocument(normalized);
|
|
225
|
+
const command = new InsertOneCommand(this.#collectionName, document);
|
|
226
|
+
return {
|
|
227
|
+
_id: (await this.#drainPlan(command))[0].insertedId,
|
|
228
|
+
...normalized
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
createAll(data) {
|
|
232
|
+
this.#rejectIncludes("createAll");
|
|
233
|
+
const self = this;
|
|
234
|
+
async function* gen() {
|
|
235
|
+
const normalizedRows = data.map((d) => self.#injectDiscriminator(self.#stripUndefined(d)));
|
|
236
|
+
const documents = normalizedRows.map((d) => self.#toDocument(d));
|
|
237
|
+
const command = new InsertManyCommand(self.#collectionName, documents);
|
|
238
|
+
const insertedIds = (await self.#drainPlan(command))[0].insertedIds;
|
|
239
|
+
for (let i = 0; i < normalizedRows.length; i++) yield {
|
|
240
|
+
_id: insertedIds[i],
|
|
241
|
+
...normalizedRows[i]
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return new AsyncIterableResult(gen());
|
|
245
|
+
}
|
|
246
|
+
async createCount(data) {
|
|
247
|
+
this.#rejectIncludes("createCount");
|
|
248
|
+
const documents = data.map((d) => this.#toDocument(this.#injectDiscriminator(d)));
|
|
249
|
+
const command = new InsertManyCommand(this.#collectionName, documents);
|
|
250
|
+
return (await this.#drainPlan(command))[0].insertedCount;
|
|
251
|
+
}
|
|
252
|
+
async update(dataOrCallback) {
|
|
253
|
+
this.#requireFilters("update");
|
|
254
|
+
this.#rejectWindowing("update");
|
|
255
|
+
this.#rejectIncludes("update");
|
|
256
|
+
const filter = this.#mergeFilters();
|
|
257
|
+
const updateDoc = this.#resolveUpdateDoc(dataOrCallback);
|
|
258
|
+
const command = new FindOneAndUpdateCommand(this.#collectionName, filter, updateDoc, false);
|
|
259
|
+
return (await this.#drainPlan(command))[0] ?? null;
|
|
260
|
+
}
|
|
261
|
+
updateAll(dataOrCallback) {
|
|
262
|
+
this.#requireFilters("updateAll");
|
|
263
|
+
this.#rejectWindowing("updateAll");
|
|
264
|
+
const self = this;
|
|
265
|
+
async function* gen() {
|
|
266
|
+
const ids = await self.#readMatchingIds();
|
|
267
|
+
if (ids.length === 0) return;
|
|
268
|
+
const filter = self.#mergeFilters();
|
|
269
|
+
const updateDoc = self.#resolveUpdateDoc(dataOrCallback);
|
|
270
|
+
const command = new UpdateManyCommand(self.#collectionName, filter, updateDoc);
|
|
271
|
+
await self.#drainPlan(command);
|
|
272
|
+
const idFilter = MongoFieldFilter.in("_id", ids.map((id) => new MongoParamRef(id)));
|
|
273
|
+
yield* self.#clone({ filters: [idFilter] }).#execute();
|
|
274
|
+
}
|
|
275
|
+
return new AsyncIterableResult(gen());
|
|
276
|
+
}
|
|
277
|
+
async updateCount(dataOrCallback) {
|
|
278
|
+
this.#requireFilters("updateCount");
|
|
279
|
+
this.#rejectWindowing("updateCount");
|
|
280
|
+
this.#rejectIncludes("updateCount");
|
|
281
|
+
const filter = this.#mergeFilters();
|
|
282
|
+
const updateDoc = this.#resolveUpdateDoc(dataOrCallback);
|
|
283
|
+
const command = new UpdateManyCommand(this.#collectionName, filter, updateDoc);
|
|
284
|
+
return (await this.#drainPlan(command))[0].modifiedCount;
|
|
285
|
+
}
|
|
286
|
+
async delete() {
|
|
287
|
+
this.#requireFilters("delete");
|
|
288
|
+
this.#rejectWindowing("delete");
|
|
289
|
+
this.#rejectIncludes("delete");
|
|
290
|
+
const filter = this.#mergeFilters();
|
|
291
|
+
const command = new FindOneAndDeleteCommand(this.#collectionName, filter);
|
|
292
|
+
return (await this.#drainPlan(command))[0] ?? null;
|
|
293
|
+
}
|
|
294
|
+
deleteAll() {
|
|
295
|
+
this.#requireFilters("deleteAll");
|
|
296
|
+
this.#rejectWindowing("deleteAll");
|
|
297
|
+
const self = this;
|
|
298
|
+
async function* gen() {
|
|
299
|
+
const docs = [];
|
|
300
|
+
for await (const row of self.#execute()) docs.push(row);
|
|
301
|
+
const filter = self.#mergeFilters();
|
|
302
|
+
const command = new DeleteManyCommand(self.#collectionName, filter);
|
|
303
|
+
await self.#drainPlan(command);
|
|
304
|
+
yield* docs;
|
|
305
|
+
}
|
|
306
|
+
return new AsyncIterableResult(gen());
|
|
307
|
+
}
|
|
308
|
+
async deleteCount() {
|
|
309
|
+
this.#requireFilters("deleteCount");
|
|
310
|
+
this.#rejectWindowing("deleteCount");
|
|
311
|
+
this.#rejectIncludes("deleteCount");
|
|
312
|
+
const filter = this.#mergeFilters();
|
|
313
|
+
const command = new DeleteManyCommand(this.#collectionName, filter);
|
|
314
|
+
return (await this.#drainPlan(command))[0].deletedCount;
|
|
315
|
+
}
|
|
316
|
+
async upsert(input) {
|
|
317
|
+
this.#requireFilters("upsert");
|
|
318
|
+
this.#rejectWindowing("upsert");
|
|
319
|
+
this.#rejectIncludes("upsert");
|
|
320
|
+
const filter = this.#mergeFilters();
|
|
321
|
+
const allCreateFields = this.#toDocument(this.#injectDiscriminator(input.create));
|
|
322
|
+
let updateDoc;
|
|
323
|
+
if (typeof input.update === "function") {
|
|
324
|
+
const accessor = createFieldAccessor();
|
|
325
|
+
const ops = input.update(accessor);
|
|
326
|
+
if (ops.find((op) => op.field === "_id")) throw new Error("Mutation payloads cannot modify `_id`");
|
|
327
|
+
const dotPathOp = ops.find((op) => op.field.includes("."));
|
|
328
|
+
if (dotPathOp) throw new Error(`upsert() does not support dot-path field operations (found "${dotPathOp.field}"). Dot-path updates conflict with \$setOnInsert on the insert path, producing incomplete documents. Use top-level field operations instead.`);
|
|
329
|
+
updateDoc = compileFieldOperations(ops, (field, value, operator) => this.#wrapFieldOpValue(field, value, operator));
|
|
330
|
+
} else {
|
|
331
|
+
const setFields = this.#toSetFields(input.update);
|
|
332
|
+
updateDoc = {};
|
|
333
|
+
if (Object.keys(setFields).length > 0) updateDoc["$set"] = setFields;
|
|
334
|
+
}
|
|
335
|
+
const updatedFields = /* @__PURE__ */ new Set();
|
|
336
|
+
for (const operatorGroup of Object.values(updateDoc)) for (const fieldPath of Object.keys(operatorGroup)) updatedFields.add(fieldPath.split(".")[0] ?? fieldPath);
|
|
337
|
+
const insertOnlyFields = {};
|
|
338
|
+
for (const [key, value] of Object.entries(allCreateFields)) if (!updatedFields.has(key)) insertOnlyFields[key] = value;
|
|
339
|
+
if (Object.keys(insertOnlyFields).length > 0) updateDoc["$setOnInsert"] = insertOnlyFields;
|
|
340
|
+
const command = new FindOneAndUpdateCommand(this.#collectionName, filter, updateDoc, true);
|
|
341
|
+
return (await this.#drainPlan(command))[0];
|
|
342
|
+
}
|
|
343
|
+
async #readMatchingIds() {
|
|
344
|
+
const idQuery = this.#clone({
|
|
345
|
+
includes: [],
|
|
346
|
+
selectedFields: ["_id"],
|
|
347
|
+
orderBy: void 0,
|
|
348
|
+
limit: void 0,
|
|
349
|
+
offset: void 0
|
|
350
|
+
});
|
|
351
|
+
const ids = [];
|
|
352
|
+
for await (const row of idQuery.#execute()) ids.push(row["_id"]);
|
|
353
|
+
return ids;
|
|
354
|
+
}
|
|
355
|
+
#execute() {
|
|
356
|
+
const plan = this.#compile();
|
|
65
357
|
return this.#executor.execute(plan);
|
|
66
358
|
}
|
|
359
|
+
#compile() {
|
|
360
|
+
return compileMongoQuery(this.#collectionName, this.#state, this.#contract.storage.storageHash);
|
|
361
|
+
}
|
|
362
|
+
#wrapCommand(command) {
|
|
363
|
+
return {
|
|
364
|
+
collection: this.#collectionName,
|
|
365
|
+
command,
|
|
366
|
+
meta: this.#planMeta()
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
async #drainPlan(command) {
|
|
370
|
+
const plan = this.#wrapCommand(command);
|
|
371
|
+
const result = this.#executor.execute(plan);
|
|
372
|
+
const rows = [];
|
|
373
|
+
for await (const row of result) rows.push(row);
|
|
374
|
+
return rows;
|
|
375
|
+
}
|
|
376
|
+
#modelFields() {
|
|
377
|
+
return this.#contract.models[this.#modelName]?.fields ?? {};
|
|
378
|
+
}
|
|
379
|
+
#compileWhereObject(data) {
|
|
380
|
+
const fields = this.#modelFields();
|
|
381
|
+
const filters = [];
|
|
382
|
+
for (const [key, value] of Object.entries(data)) {
|
|
383
|
+
if (value === void 0) continue;
|
|
384
|
+
const wrapped = this.#wrapFieldValue(value, fields[key]);
|
|
385
|
+
filters.push(MongoFieldFilter.eq(key, wrapped));
|
|
386
|
+
}
|
|
387
|
+
return filters;
|
|
388
|
+
}
|
|
389
|
+
#wrapFieldValue(value, field) {
|
|
390
|
+
if (field === void 0) return new MongoParamRef(value);
|
|
391
|
+
if (field.type.kind === "scalar") return new MongoParamRef(value, { codecId: field.type.codecId });
|
|
392
|
+
if (field.type.kind === "valueObject") {
|
|
393
|
+
const voName = field.type.name;
|
|
394
|
+
const voDef = this.#contract.valueObjects?.[voName];
|
|
395
|
+
if (!voDef || value === null) return new MongoParamRef(value);
|
|
396
|
+
if (field.many && Array.isArray(value)) return value.map((item) => this.#wrapValueObject(item, voDef));
|
|
397
|
+
return this.#wrapValueObject(value, voDef);
|
|
398
|
+
}
|
|
399
|
+
return new MongoParamRef(value);
|
|
400
|
+
}
|
|
401
|
+
#wrapValueObject(data, voDef) {
|
|
402
|
+
const doc = {};
|
|
403
|
+
for (const [key, value] of Object.entries(data)) {
|
|
404
|
+
if (value === void 0) continue;
|
|
405
|
+
const fieldDef = voDef.fields[key];
|
|
406
|
+
doc[key] = this.#wrapFieldValue(value, fieldDef);
|
|
407
|
+
}
|
|
408
|
+
return doc;
|
|
409
|
+
}
|
|
410
|
+
#toDocument(data) {
|
|
411
|
+
const fields = this.#modelFields();
|
|
412
|
+
const doc = {};
|
|
413
|
+
for (const [key, value] of Object.entries(data)) if (value !== void 0) doc[key] = this.#wrapFieldValue(value, fields[key]);
|
|
414
|
+
return doc;
|
|
415
|
+
}
|
|
416
|
+
#toSetFields(data) {
|
|
417
|
+
const fields = this.#modelFields();
|
|
418
|
+
const result = {};
|
|
419
|
+
for (const [key, value] of Object.entries(data)) {
|
|
420
|
+
if (key === "_id" && value !== void 0) throw new Error("Mutation payloads cannot modify `_id`");
|
|
421
|
+
if (value !== void 0) result[key] = this.#wrapFieldValue(value, fields[key]);
|
|
422
|
+
}
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
#stripUndefined(data) {
|
|
426
|
+
const result = {};
|
|
427
|
+
for (const [key, value] of Object.entries(data)) if (value !== void 0) result[key] = value;
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
#toUpdateDocument(data) {
|
|
431
|
+
return { $set: this.#toSetFields(data) };
|
|
432
|
+
}
|
|
433
|
+
#resolveUpdateDoc(dataOrCallback) {
|
|
434
|
+
if (typeof dataOrCallback === "function") {
|
|
435
|
+
const ops = dataOrCallback(createFieldAccessor());
|
|
436
|
+
if (ops.find((op) => op.field === "_id")) throw new Error("Mutation payloads cannot modify `_id`");
|
|
437
|
+
if (ops.length === 0) return { $set: {} };
|
|
438
|
+
return compileFieldOperations(ops, (field, value, operator) => this.#wrapFieldOpValue(field, value, operator));
|
|
439
|
+
}
|
|
440
|
+
return this.#toUpdateDocument(dataOrCallback);
|
|
441
|
+
}
|
|
442
|
+
#wrapFieldOpValue(field, value, operator) {
|
|
443
|
+
if (operator === "$unset") return value;
|
|
444
|
+
const topLevelField = field.split(".")[0] ?? field;
|
|
445
|
+
const contractField = this.#modelFields()[topLevelField];
|
|
446
|
+
if (!contractField) return value;
|
|
447
|
+
if (field.includes(".")) return this.#wrapDotPathValue(field, value);
|
|
448
|
+
if (value instanceof MongoParamRef && contractField.type.kind === "scalar") return new MongoParamRef(value.value, { codecId: contractField.type.codecId });
|
|
449
|
+
if (contractField.type.kind === "valueObject" && value instanceof MongoParamRef) {
|
|
450
|
+
const raw = value.value;
|
|
451
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
452
|
+
const voName = contractField.type.name;
|
|
453
|
+
const voDef = this.#contract.valueObjects?.[voName];
|
|
454
|
+
if (voDef) return this.#wrapValueObject(raw, voDef);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
#wrapDotPathValue(dotPath, value) {
|
|
460
|
+
const parts = dotPath.split(".");
|
|
461
|
+
const fields = this.#modelFields();
|
|
462
|
+
let currentField = parts[0] ? fields[parts[0]] : void 0;
|
|
463
|
+
for (let i = 1; i < parts.length; i++) {
|
|
464
|
+
if (!currentField || currentField.type.kind !== "valueObject") return value;
|
|
465
|
+
const voName = currentField.type.name;
|
|
466
|
+
const voDef = this.#contract.valueObjects?.[voName];
|
|
467
|
+
if (!voDef) return value;
|
|
468
|
+
const partKey = parts[i];
|
|
469
|
+
currentField = partKey ? voDef.fields[partKey] : void 0;
|
|
470
|
+
}
|
|
471
|
+
if (currentField?.type.kind === "scalar" && value instanceof MongoParamRef) return new MongoParamRef(value.value, { codecId: currentField.type.codecId });
|
|
472
|
+
if (currentField?.type.kind === "valueObject" && value instanceof MongoParamRef) {
|
|
473
|
+
const raw = value.value;
|
|
474
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
475
|
+
const voName = currentField.type.name;
|
|
476
|
+
const voDef = this.#contract.valueObjects?.[voName];
|
|
477
|
+
if (voDef) return this.#wrapValueObject(raw, voDef);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
#mergeFilters() {
|
|
483
|
+
const [single] = this.#state.filters;
|
|
484
|
+
if (this.#state.filters.length === 1 && single) return single;
|
|
485
|
+
return MongoAndExpr.of([...this.#state.filters]);
|
|
486
|
+
}
|
|
487
|
+
#requireFilters(methodName) {
|
|
488
|
+
if (this.#state.filters.length === 0) throw new Error(`${methodName}() requires a .where() filter. Call .where() before .${methodName}()`);
|
|
489
|
+
}
|
|
490
|
+
#rejectWindowing(methodName) {
|
|
491
|
+
if (this.#state.orderBy !== void 0 || this.#state.limit !== void 0 || this.#state.offset !== void 0) throw new Error(`${methodName}() does not support orderBy/skip/take. Remove windowing before calling .${methodName}()`);
|
|
492
|
+
}
|
|
493
|
+
#rejectIncludes(methodName) {
|
|
494
|
+
if (this.#state.includes.length > 0) throw new Error(`${methodName}() does not support .include(). Remove includes before calling .${methodName}()`);
|
|
495
|
+
}
|
|
496
|
+
#planMeta() {
|
|
497
|
+
return {
|
|
498
|
+
target: "mongo",
|
|
499
|
+
storageHash: this.#contract.storage.storageHash,
|
|
500
|
+
lane: "mongo-orm",
|
|
501
|
+
paramDescriptors: []
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
#injectDiscriminator(data) {
|
|
505
|
+
if (!this.#variantName) return data;
|
|
506
|
+
const model = this.#contract.models[this.#modelName];
|
|
507
|
+
if (!model?.discriminator || !model.variants) return data;
|
|
508
|
+
const variantEntry = model.variants[this.#variantName];
|
|
509
|
+
if (!variantEntry) return data;
|
|
510
|
+
return {
|
|
511
|
+
...data,
|
|
512
|
+
[model.discriminator.field]: variantEntry.value
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
#clone(overrides) {
|
|
516
|
+
const instance = new MongoCollectionImpl(this.#contract, this.#modelName, this.#executor);
|
|
517
|
+
instance.#state = {
|
|
518
|
+
...this.#state,
|
|
519
|
+
...overrides
|
|
520
|
+
};
|
|
521
|
+
instance.#collectionName = this.#collectionName;
|
|
522
|
+
instance.#variantName = this.#variantName;
|
|
523
|
+
return instance;
|
|
524
|
+
}
|
|
525
|
+
#cloneWithVariant(overrides, variantName) {
|
|
526
|
+
const instance = new MongoCollectionImpl(this.#contract, this.#modelName, this.#executor);
|
|
527
|
+
instance.#state = {
|
|
528
|
+
...this.#state,
|
|
529
|
+
...overrides
|
|
530
|
+
};
|
|
531
|
+
instance.#collectionName = this.#collectionName;
|
|
532
|
+
instance.#variantName = variantName;
|
|
533
|
+
return instance;
|
|
534
|
+
}
|
|
67
535
|
};
|
|
536
|
+
function createMongoCollection(contract, modelName, executor) {
|
|
537
|
+
return new MongoCollectionImpl(contract, modelName, executor);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/mongo-orm.ts
|
|
68
542
|
function mongoOrm(options) {
|
|
69
543
|
const { contract, executor } = options;
|
|
70
544
|
const client = {};
|
|
71
|
-
for (const [rootName, modelName] of Object.entries(contract.roots)) client[rootName] =
|
|
545
|
+
for (const [rootName, modelName] of Object.entries(contract.roots)) client[rootName] = createMongoCollection(contract, modelName, executor);
|
|
72
546
|
return client;
|
|
73
547
|
}
|
|
74
548
|
|
|
75
549
|
//#endregion
|
|
76
|
-
|
|
550
|
+
//#region src/raw-collection.ts
|
|
551
|
+
function createRawMongoCollection(collectionName, meta) {
|
|
552
|
+
function buildable(command) {
|
|
553
|
+
return { build: () => ({
|
|
554
|
+
collection: collectionName,
|
|
555
|
+
command,
|
|
556
|
+
meta
|
|
557
|
+
}) };
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
aggregate(pipeline) {
|
|
561
|
+
return buildable(new RawAggregateCommand(collectionName, pipeline));
|
|
562
|
+
},
|
|
563
|
+
insertOne(document) {
|
|
564
|
+
return buildable(new RawInsertOneCommand(collectionName, document));
|
|
565
|
+
},
|
|
566
|
+
insertMany(documents) {
|
|
567
|
+
return buildable(new RawInsertManyCommand(collectionName, documents));
|
|
568
|
+
},
|
|
569
|
+
updateOne(filter, update) {
|
|
570
|
+
return buildable(new RawUpdateOneCommand(collectionName, filter, update));
|
|
571
|
+
},
|
|
572
|
+
updateMany(filter, update) {
|
|
573
|
+
return buildable(new RawUpdateManyCommand(collectionName, filter, update));
|
|
574
|
+
},
|
|
575
|
+
deleteOne(filter) {
|
|
576
|
+
return buildable(new RawDeleteOneCommand(collectionName, filter));
|
|
577
|
+
},
|
|
578
|
+
deleteMany(filter) {
|
|
579
|
+
return buildable(new RawDeleteManyCommand(collectionName, filter));
|
|
580
|
+
},
|
|
581
|
+
findOneAndUpdate(filter, update, options) {
|
|
582
|
+
return buildable(new RawFindOneAndUpdateCommand(collectionName, filter, update, options?.upsert ?? false));
|
|
583
|
+
},
|
|
584
|
+
findOneAndDelete(filter) {
|
|
585
|
+
return buildable(new RawFindOneAndDeleteCommand(collectionName, filter));
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
//#endregion
|
|
591
|
+
//#region src/mongo-raw.ts
|
|
592
|
+
function mongoRaw(options) {
|
|
593
|
+
const { contract } = options;
|
|
594
|
+
return { collection(rootName) {
|
|
595
|
+
const modelName = contract.roots[rootName];
|
|
596
|
+
if (!Object.hasOwn(contract.models, modelName)) throw new Error(`Unknown model "${modelName}" for root "${rootName}"`);
|
|
597
|
+
return createRawMongoCollection(contract.models[modelName].storage.collection ?? modelName, {
|
|
598
|
+
target: "mongo",
|
|
599
|
+
storageHash: contract.storage.storageHash,
|
|
600
|
+
lane: "mongo-raw",
|
|
601
|
+
paramDescriptors: []
|
|
602
|
+
});
|
|
603
|
+
} };
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
//#endregion
|
|
607
|
+
export { compileMongoQuery, createFieldAccessor, createMongoCollection, mongoOrm, mongoRaw };
|
|
77
608
|
//# sourceMappingURL=index.mjs.map
|