@zodmon/core 0.7.0 → 0.9.0
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.cjs +1020 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2412 -841
- package/dist/index.d.ts +2412 -841
- package/dist/index.js +1000 -60
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,30 +21,45 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
$: () => $,
|
|
24
|
+
$addToSet: () => $addToSet,
|
|
24
25
|
$and: () => $and,
|
|
26
|
+
$avg: () => $avg,
|
|
27
|
+
$count: () => $count,
|
|
25
28
|
$eq: () => $eq,
|
|
26
29
|
$exists: () => $exists,
|
|
30
|
+
$first: () => $first,
|
|
27
31
|
$gt: () => $gt,
|
|
28
32
|
$gte: () => $gte,
|
|
29
33
|
$in: () => $in,
|
|
34
|
+
$last: () => $last,
|
|
30
35
|
$lt: () => $lt,
|
|
31
36
|
$lte: () => $lte,
|
|
37
|
+
$max: () => $max,
|
|
38
|
+
$min: () => $min,
|
|
32
39
|
$ne: () => $ne,
|
|
33
40
|
$nin: () => $nin,
|
|
34
41
|
$nor: () => $nor,
|
|
35
42
|
$not: () => $not,
|
|
36
43
|
$or: () => $or,
|
|
44
|
+
$push: () => $push,
|
|
37
45
|
$regex: () => $regex,
|
|
46
|
+
$sum: () => $sum,
|
|
47
|
+
AggregatePipeline: () => AggregatePipeline,
|
|
38
48
|
CollectionHandle: () => CollectionHandle,
|
|
39
49
|
Database: () => Database,
|
|
40
50
|
IndexBuilder: () => IndexBuilder,
|
|
41
51
|
TypedFindCursor: () => TypedFindCursor,
|
|
42
52
|
ZodmonNotFoundError: () => ZodmonNotFoundError,
|
|
43
53
|
ZodmonValidationError: () => ZodmonValidationError,
|
|
54
|
+
aggregate: () => aggregate,
|
|
55
|
+
checkUnindexedFields: () => checkUnindexedFields,
|
|
44
56
|
collection: () => collection,
|
|
57
|
+
createAccumulatorBuilder: () => createAccumulatorBuilder,
|
|
45
58
|
createClient: () => createClient,
|
|
59
|
+
createExpressionBuilder: () => createExpressionBuilder,
|
|
46
60
|
deleteMany: () => deleteMany,
|
|
47
61
|
deleteOne: () => deleteOne,
|
|
62
|
+
extractComparableOptions: () => extractComparableOptions,
|
|
48
63
|
extractDbName: () => extractDbName,
|
|
49
64
|
extractFieldIndexes: () => extractFieldIndexes,
|
|
50
65
|
find: () => find,
|
|
@@ -52,6 +67,7 @@ __export(index_exports, {
|
|
|
52
67
|
findOneAndDelete: () => findOneAndDelete,
|
|
53
68
|
findOneAndUpdate: () => findOneAndUpdate,
|
|
54
69
|
findOneOrThrow: () => findOneOrThrow,
|
|
70
|
+
generateIndexName: () => generateIndexName,
|
|
55
71
|
getIndexMetadata: () => getIndexMetadata,
|
|
56
72
|
getRefMetadata: () => getRefMetadata,
|
|
57
73
|
index: () => index,
|
|
@@ -61,16 +77,831 @@ __export(index_exports, {
|
|
|
61
77
|
objectId: () => objectId,
|
|
62
78
|
oid: () => oid,
|
|
63
79
|
raw: () => raw,
|
|
80
|
+
serializeIndexKey: () => serializeIndexKey,
|
|
81
|
+
syncIndexes: () => syncIndexes,
|
|
82
|
+
toCompoundIndexSpec: () => toCompoundIndexSpec,
|
|
83
|
+
toFieldIndexSpec: () => toFieldIndexSpec,
|
|
64
84
|
updateMany: () => updateMany,
|
|
65
85
|
updateOne: () => updateOne
|
|
66
86
|
});
|
|
67
87
|
module.exports = __toCommonJS(index_exports);
|
|
68
88
|
|
|
89
|
+
// src/aggregate/expressions.ts
|
|
90
|
+
var $count = () => ({
|
|
91
|
+
__accum: true,
|
|
92
|
+
expr: { $sum: 1 }
|
|
93
|
+
});
|
|
94
|
+
var $sum = (field) => ({
|
|
95
|
+
__accum: true,
|
|
96
|
+
expr: { $sum: field }
|
|
97
|
+
});
|
|
98
|
+
var $avg = (field) => ({
|
|
99
|
+
__accum: true,
|
|
100
|
+
expr: { $avg: field }
|
|
101
|
+
});
|
|
102
|
+
var $min = (field) => ({
|
|
103
|
+
__accum: true,
|
|
104
|
+
expr: { $min: field }
|
|
105
|
+
});
|
|
106
|
+
var $max = (field) => ({
|
|
107
|
+
__accum: true,
|
|
108
|
+
expr: { $max: field }
|
|
109
|
+
});
|
|
110
|
+
var $first = (field) => ({
|
|
111
|
+
__accum: true,
|
|
112
|
+
expr: { $first: field }
|
|
113
|
+
});
|
|
114
|
+
var $last = (field) => ({
|
|
115
|
+
__accum: true,
|
|
116
|
+
expr: { $last: field }
|
|
117
|
+
});
|
|
118
|
+
var $push = (field) => ({
|
|
119
|
+
__accum: true,
|
|
120
|
+
expr: { $push: field }
|
|
121
|
+
});
|
|
122
|
+
var $addToSet = (field) => ({
|
|
123
|
+
__accum: true,
|
|
124
|
+
expr: { $addToSet: field }
|
|
125
|
+
});
|
|
126
|
+
function createAccumulatorBuilder() {
|
|
127
|
+
return {
|
|
128
|
+
count: () => ({ __accum: true, expr: { $sum: 1 } }),
|
|
129
|
+
sum: (field) => ({
|
|
130
|
+
__accum: true,
|
|
131
|
+
expr: { $sum: typeof field === "number" ? field : `$${field}` }
|
|
132
|
+
}),
|
|
133
|
+
avg: (field) => ({ __accum: true, expr: { $avg: `$${field}` } }),
|
|
134
|
+
min: (field) => ({ __accum: true, expr: { $min: `$${field}` } }),
|
|
135
|
+
max: (field) => ({ __accum: true, expr: { $max: `$${field}` } }),
|
|
136
|
+
first: (field) => ({ __accum: true, expr: { $first: `$${field}` } }),
|
|
137
|
+
last: (field) => ({ __accum: true, expr: { $last: `$${field}` } }),
|
|
138
|
+
push: (field) => ({ __accum: true, expr: { $push: `$${field}` } }),
|
|
139
|
+
addToSet: (field) => ({ __accum: true, expr: { $addToSet: `$${field}` } })
|
|
140
|
+
// biome-ignore lint/suspicious/noExplicitAny: Runtime implementation uses string field names and returns plain objects — TypeScript cannot verify that the runtime Accumulator objects match the generic AccumulatorBuilder<T> return types. Safe because type resolution happens at compile time via AccumulatorBuilder<T>, and runtime values are identical to what the standalone $min/$max/etc. produce.
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function createExpressionBuilder() {
|
|
144
|
+
const $2 = (field) => `$${field}`;
|
|
145
|
+
const val = (v) => typeof v === "number" ? v : `$${v}`;
|
|
146
|
+
const expr = (value) => ({ __expr: true, value });
|
|
147
|
+
return {
|
|
148
|
+
// Arithmetic
|
|
149
|
+
add: (field, value) => expr({ $add: [$2(field), val(value)] }),
|
|
150
|
+
subtract: (field, value) => expr({ $subtract: [$2(field), val(value)] }),
|
|
151
|
+
multiply: (field, value) => expr({ $multiply: [$2(field), val(value)] }),
|
|
152
|
+
divide: (field, value) => expr({ $divide: [$2(field), val(value)] }),
|
|
153
|
+
mod: (field, value) => expr({ $mod: [$2(field), val(value)] }),
|
|
154
|
+
abs: (field) => expr({ $abs: $2(field) }),
|
|
155
|
+
ceil: (field) => expr({ $ceil: $2(field) }),
|
|
156
|
+
floor: (field) => expr({ $floor: $2(field) }),
|
|
157
|
+
round: (field, place = 0) => expr({ $round: [$2(field), place] }),
|
|
158
|
+
// String
|
|
159
|
+
concat: (...parts) => {
|
|
160
|
+
const resolved = parts.map((p) => {
|
|
161
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(p)) return $2(p);
|
|
162
|
+
return p;
|
|
163
|
+
});
|
|
164
|
+
return expr({ $concat: resolved });
|
|
165
|
+
},
|
|
166
|
+
toLower: (field) => expr({ $toLower: $2(field) }),
|
|
167
|
+
toUpper: (field) => expr({ $toUpper: $2(field) }),
|
|
168
|
+
trim: (field) => expr({ $trim: { input: $2(field) } }),
|
|
169
|
+
substr: (field, start, length) => expr({ $substrBytes: [$2(field), start, length] }),
|
|
170
|
+
// Comparison
|
|
171
|
+
eq: (field, value) => expr({ $eq: [$2(field), value] }),
|
|
172
|
+
gt: (field, value) => expr({ $gt: [$2(field), value] }),
|
|
173
|
+
gte: (field, value) => expr({ $gte: [$2(field), value] }),
|
|
174
|
+
lt: (field, value) => expr({ $lt: [$2(field), value] }),
|
|
175
|
+
lte: (field, value) => expr({ $lte: [$2(field), value] }),
|
|
176
|
+
ne: (field, value) => expr({ $ne: [$2(field), value] }),
|
|
177
|
+
// Date
|
|
178
|
+
year: (field) => expr({ $year: $2(field) }),
|
|
179
|
+
month: (field) => expr({ $month: $2(field) }),
|
|
180
|
+
dayOfMonth: (field) => expr({ $dayOfMonth: $2(field) }),
|
|
181
|
+
// Array
|
|
182
|
+
size: (field) => expr({ $size: $2(field) }),
|
|
183
|
+
// Conditional
|
|
184
|
+
cond: (condition, thenValue, elseValue) => expr({ $cond: [condition.value, thenValue, elseValue] }),
|
|
185
|
+
ifNull: (field, fallback) => expr({ $ifNull: [$2(field), fallback] })
|
|
186
|
+
// biome-ignore lint/suspicious/noExplicitAny: Runtime implementation uses string field names — TypeScript cannot verify generic ExpressionBuilder<T> return types match. Safe because type resolution happens at compile time.
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/schema/ref.ts
|
|
191
|
+
var import_zod = require("zod");
|
|
192
|
+
var refMetadata = /* @__PURE__ */ new WeakMap();
|
|
193
|
+
function getRefMetadata(schema) {
|
|
194
|
+
if (typeof schema !== "object" || schema === null) return void 0;
|
|
195
|
+
return refMetadata.get(schema);
|
|
196
|
+
}
|
|
197
|
+
var REF_GUARD = /* @__PURE__ */ Symbol.for("zodmon_ref");
|
|
198
|
+
function installRefExtension() {
|
|
199
|
+
const proto = import_zod.z.ZodType.prototype;
|
|
200
|
+
if (REF_GUARD in proto) return;
|
|
201
|
+
Object.defineProperty(proto, "ref", {
|
|
202
|
+
value(collection2) {
|
|
203
|
+
refMetadata.set(this, { collection: collection2 });
|
|
204
|
+
return this;
|
|
205
|
+
},
|
|
206
|
+
enumerable: true,
|
|
207
|
+
configurable: true,
|
|
208
|
+
writable: true
|
|
209
|
+
});
|
|
210
|
+
Object.defineProperty(proto, REF_GUARD, {
|
|
211
|
+
value: true,
|
|
212
|
+
enumerable: false,
|
|
213
|
+
configurable: false,
|
|
214
|
+
writable: false
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/aggregate/pipeline.ts
|
|
219
|
+
var AggregatePipeline = class _AggregatePipeline {
|
|
220
|
+
definition;
|
|
221
|
+
nativeCollection;
|
|
222
|
+
stages;
|
|
223
|
+
constructor(definition, nativeCollection, stages) {
|
|
224
|
+
this.definition = definition;
|
|
225
|
+
this.nativeCollection = nativeCollection;
|
|
226
|
+
this.stages = stages;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Append an arbitrary aggregation stage to the pipeline (escape hatch).
|
|
230
|
+
*
|
|
231
|
+
* Returns a new pipeline instance with the stage appended — the
|
|
232
|
+
* original pipeline is not modified.
|
|
233
|
+
*
|
|
234
|
+
* Optionally accepts a type parameter `TNew` to change the output
|
|
235
|
+
* type when the stage transforms the document shape.
|
|
236
|
+
*
|
|
237
|
+
* @typeParam TNew - The output type after this stage. Defaults to the current output type.
|
|
238
|
+
* @param stage - A raw MongoDB aggregation stage document (e.g. `{ $match: { ... } }`).
|
|
239
|
+
* @returns A new pipeline with the stage appended.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* const admins = aggregate(users)
|
|
244
|
+
* .raw({ $match: { role: 'admin' } })
|
|
245
|
+
* .toArray()
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* // Change output type with a $project stage
|
|
251
|
+
* const names = aggregate(users)
|
|
252
|
+
* .raw<{ name: string }>({ $project: { name: 1, _id: 0 } })
|
|
253
|
+
* .toArray()
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
raw(stage) {
|
|
257
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
258
|
+
...this.stages,
|
|
259
|
+
stage
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Execute the pipeline and return all results as an array.
|
|
264
|
+
*
|
|
265
|
+
* @returns A promise resolving to the array of output documents.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const results = await aggregate(users)
|
|
270
|
+
* .raw({ $match: { age: { $gte: 18 } } })
|
|
271
|
+
* .toArray()
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
async toArray() {
|
|
275
|
+
const cursor = this.nativeCollection.aggregate(this.stages);
|
|
276
|
+
return await cursor.toArray();
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Stream pipeline results one document at a time via `for await...of`.
|
|
280
|
+
*
|
|
281
|
+
* @returns An async generator yielding output documents.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```ts
|
|
285
|
+
* for await (const user of aggregate(users).raw({ $match: { role: 'admin' } })) {
|
|
286
|
+
* console.log(user.name)
|
|
287
|
+
* }
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
async *[Symbol.asyncIterator]() {
|
|
291
|
+
const cursor = this.nativeCollection.aggregate(this.stages);
|
|
292
|
+
for await (const doc of cursor) {
|
|
293
|
+
yield doc;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Return the query execution plan without running the pipeline.
|
|
298
|
+
*
|
|
299
|
+
* Useful for debugging and understanding how MongoDB will process
|
|
300
|
+
* the pipeline stages.
|
|
301
|
+
*
|
|
302
|
+
* @returns A promise resolving to the explain output document.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```ts
|
|
306
|
+
* const plan = await aggregate(users)
|
|
307
|
+
* .raw({ $match: { role: 'admin' } })
|
|
308
|
+
* .explain()
|
|
309
|
+
* console.log(plan)
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
async explain() {
|
|
313
|
+
const cursor = this.nativeCollection.aggregate(this.stages);
|
|
314
|
+
return await cursor.explain();
|
|
315
|
+
}
|
|
316
|
+
// ── Shape-preserving stages ──────────────────────────────────────
|
|
317
|
+
/**
|
|
318
|
+
* Filter documents using a type-safe match expression.
|
|
319
|
+
*
|
|
320
|
+
* Appends a `$match` stage to the pipeline. The filter is constrained
|
|
321
|
+
* to the current output type, so only valid fields and operators are accepted.
|
|
322
|
+
*
|
|
323
|
+
* Supports two forms of type narrowing:
|
|
324
|
+
*
|
|
325
|
+
* **Tier 1 — Explicit type parameter:**
|
|
326
|
+
* ```ts
|
|
327
|
+
* .match<{ role: 'engineer' | 'designer' }>({ role: { $in: ['engineer', 'designer'] } })
|
|
328
|
+
* // role narrows to 'engineer' | 'designer'
|
|
329
|
+
* ```
|
|
330
|
+
*
|
|
331
|
+
* **Tier 2 — Automatic inference from filter literals:**
|
|
332
|
+
* ```ts
|
|
333
|
+
* .match({ role: 'engineer' }) // role narrows to 'engineer'
|
|
334
|
+
* .match({ role: { $ne: 'intern' } }) // role narrows to Exclude<Role, 'intern'>
|
|
335
|
+
* .match({ role: { $in: ['engineer', 'designer'] as const } }) // needs as const
|
|
336
|
+
* ```
|
|
337
|
+
*
|
|
338
|
+
* When no type parameter is provided and the filter doesn't contain
|
|
339
|
+
* inferrable literals, the output type is unchanged (backward compatible).
|
|
340
|
+
*
|
|
341
|
+
* @typeParam TNarrow - Optional object mapping field names to narrowed types. Must be a subtype of the corresponding fields in TOutput.
|
|
342
|
+
* @typeParam F - Inferred from the filter argument. Do not provide explicitly.
|
|
343
|
+
* @param filter - A type-safe filter for the current output type.
|
|
344
|
+
* @returns A new pipeline with the `$match` stage appended and output type narrowed.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* // Explicit narrowing
|
|
349
|
+
* const filtered = await users.aggregate()
|
|
350
|
+
* .match<{ role: 'engineer' }>({ role: 'engineer' })
|
|
351
|
+
* .toArray()
|
|
352
|
+
* // filtered[0].role → 'engineer'
|
|
353
|
+
*
|
|
354
|
+
* // Automatic narrowing with $in (requires as const)
|
|
355
|
+
* const subset = await users.aggregate()
|
|
356
|
+
* .match({ role: { $in: ['engineer', 'designer'] as const } })
|
|
357
|
+
* .toArray()
|
|
358
|
+
* // subset[0].role → 'engineer' | 'designer'
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
match(filter) {
|
|
362
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
363
|
+
...this.stages,
|
|
364
|
+
{ $match: filter }
|
|
365
|
+
]);
|
|
366
|
+
return pipeline;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Sort documents by one or more fields.
|
|
370
|
+
*
|
|
371
|
+
* Appends a `$sort` stage. Keys are constrained to `keyof TOutput & string`
|
|
372
|
+
* and values must be `1` (ascending) or `-1` (descending).
|
|
373
|
+
*
|
|
374
|
+
* @param spec - A sort specification mapping field names to sort direction.
|
|
375
|
+
* @returns A new pipeline with the `$sort` stage appended.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```ts
|
|
379
|
+
* const sorted = await aggregate(users)
|
|
380
|
+
* .sort({ age: -1, name: 1 })
|
|
381
|
+
* .toArray()
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
sort(spec) {
|
|
385
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
386
|
+
...this.stages,
|
|
387
|
+
{ $sort: spec }
|
|
388
|
+
]);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Skip a number of documents in the pipeline.
|
|
392
|
+
*
|
|
393
|
+
* Appends a `$skip` stage. Commonly used with {@link limit} for pagination.
|
|
394
|
+
*
|
|
395
|
+
* @param n - The number of documents to skip.
|
|
396
|
+
* @returns A new pipeline with the `$skip` stage appended.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```ts
|
|
400
|
+
* // Page 2 (10 items per page)
|
|
401
|
+
* const page2 = await aggregate(users)
|
|
402
|
+
* .sort({ name: 1 })
|
|
403
|
+
* .skip(10)
|
|
404
|
+
* .limit(10)
|
|
405
|
+
* .toArray()
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
skip(n) {
|
|
409
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
410
|
+
...this.stages,
|
|
411
|
+
{ $skip: n }
|
|
412
|
+
]);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Limit the number of documents passing through the pipeline.
|
|
416
|
+
*
|
|
417
|
+
* Appends a `$limit` stage. Commonly used with {@link skip} for pagination,
|
|
418
|
+
* or after {@link sort} to get top/bottom N results.
|
|
419
|
+
*
|
|
420
|
+
* @param n - The maximum number of documents to pass through.
|
|
421
|
+
* @returns A new pipeline with the `$limit` stage appended.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```ts
|
|
425
|
+
* const top5 = await aggregate(users)
|
|
426
|
+
* .sort({ score: -1 })
|
|
427
|
+
* .limit(5)
|
|
428
|
+
* .toArray()
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
limit(n) {
|
|
432
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
433
|
+
...this.stages,
|
|
434
|
+
{ $limit: n }
|
|
435
|
+
]);
|
|
436
|
+
}
|
|
437
|
+
// ── Shape-transforming projection stages ─────────────────────────
|
|
438
|
+
/**
|
|
439
|
+
* Include only specified fields in the output.
|
|
440
|
+
*
|
|
441
|
+
* Appends a `$project` stage with inclusion (`1`) for each key.
|
|
442
|
+
* The `_id` field is always included. The output type narrows to
|
|
443
|
+
* `Pick<TOutput, K | '_id'>`.
|
|
444
|
+
*
|
|
445
|
+
* @param spec - An object mapping field names to `1` for inclusion.
|
|
446
|
+
* @returns A new pipeline with the `$project` stage appended.
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```ts
|
|
450
|
+
* const namesOnly = await aggregate(users)
|
|
451
|
+
* .project({ name: 1 })
|
|
452
|
+
* .toArray()
|
|
453
|
+
* // [{ _id: ..., name: 'Ada' }, ...]
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
project(spec) {
|
|
457
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
458
|
+
...this.stages,
|
|
459
|
+
{ $project: spec }
|
|
460
|
+
]);
|
|
461
|
+
return pipeline;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Variadic shorthand for {@link project} — pick fields to include.
|
|
465
|
+
*
|
|
466
|
+
* Generates a `$project` stage that includes only the listed fields
|
|
467
|
+
* (plus `_id`). Equivalent to `.project({ field1: 1, field2: 1 })`.
|
|
468
|
+
*
|
|
469
|
+
* @param fields - Field names to include in the output.
|
|
470
|
+
* @returns A new pipeline with the `$project` stage appended.
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```ts
|
|
474
|
+
* const namesAndRoles = await aggregate(users)
|
|
475
|
+
* .pick('name', 'role')
|
|
476
|
+
* .toArray()
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
pick(...fields) {
|
|
480
|
+
const spec = Object.fromEntries(fields.map((f) => [f, 1]));
|
|
481
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
482
|
+
...this.stages,
|
|
483
|
+
{ $project: spec }
|
|
484
|
+
]);
|
|
485
|
+
return pipeline;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Exclude specified fields from the output.
|
|
489
|
+
*
|
|
490
|
+
* Appends a `$project` stage with exclusion (`0`) for each key.
|
|
491
|
+
* All other fields pass through. The output type becomes `Omit<TOutput, K>`.
|
|
492
|
+
*
|
|
493
|
+
* @param fields - Field names to exclude from the output.
|
|
494
|
+
* @returns A new pipeline with the `$project` stage appended.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```ts
|
|
498
|
+
* const noAge = await aggregate(users)
|
|
499
|
+
* .omit('age')
|
|
500
|
+
* .toArray()
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
omit(...fields) {
|
|
504
|
+
const spec = Object.fromEntries(fields.map((f) => [f, 0]));
|
|
505
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
506
|
+
...this.stages,
|
|
507
|
+
{ $project: spec }
|
|
508
|
+
]);
|
|
509
|
+
return pipeline;
|
|
510
|
+
}
|
|
511
|
+
groupBy(field, accumulators) {
|
|
512
|
+
const resolved = typeof accumulators === "function" ? accumulators(createAccumulatorBuilder()) : accumulators;
|
|
513
|
+
const _id = Array.isArray(field) ? Object.fromEntries(field.map((f) => [f, `$${f}`])) : `$${field}`;
|
|
514
|
+
const accumExprs = Object.fromEntries(
|
|
515
|
+
Object.entries(resolved).map(([key, acc]) => [key, acc.expr])
|
|
516
|
+
);
|
|
517
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
518
|
+
...this.stages,
|
|
519
|
+
{ $group: { _id, ...accumExprs } }
|
|
520
|
+
]);
|
|
521
|
+
return pipeline;
|
|
522
|
+
}
|
|
523
|
+
// Implementation
|
|
524
|
+
addFields(fields) {
|
|
525
|
+
const resolved = typeof fields === "function" ? fields(createExpressionBuilder()) : fields;
|
|
526
|
+
const stage = Object.fromEntries(
|
|
527
|
+
Object.entries(resolved).map(([k, v]) => [
|
|
528
|
+
k,
|
|
529
|
+
v && typeof v === "object" && "__expr" in v ? v.value : v
|
|
530
|
+
])
|
|
531
|
+
);
|
|
532
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
533
|
+
...this.stages,
|
|
534
|
+
{ $addFields: stage }
|
|
535
|
+
]);
|
|
536
|
+
return pipeline;
|
|
537
|
+
}
|
|
538
|
+
// ── unwind stage ─────────────────────────────────────────────────
|
|
539
|
+
/**
|
|
540
|
+
* Deconstruct an array field, outputting one document per array element.
|
|
541
|
+
*
|
|
542
|
+
* Appends an `$unwind` stage. The unwound field's type changes from
|
|
543
|
+
* `T[]` to `T` in the output type. Documents with empty or missing
|
|
544
|
+
* arrays are dropped unless `preserveEmpty` is `true`.
|
|
545
|
+
*
|
|
546
|
+
* @param field - The name of the array field to unwind.
|
|
547
|
+
* @param options - Optional settings for the unwind stage.
|
|
548
|
+
* @param options.preserveEmpty - If `true`, documents with null, missing, or empty arrays are preserved.
|
|
549
|
+
* @returns A new pipeline with the `$unwind` stage appended.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* const flat = await aggregate(orders)
|
|
554
|
+
* .unwind('items')
|
|
555
|
+
* .toArray()
|
|
556
|
+
* // Each result has a single `items` value instead of an array
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
unwind(field, options) {
|
|
560
|
+
const stage = options?.preserveEmpty ? { $unwind: { path: `$${field}`, preserveNullAndEmptyArrays: true } } : { $unwind: `$${field}` };
|
|
561
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
562
|
+
...this.stages,
|
|
563
|
+
stage
|
|
564
|
+
]);
|
|
565
|
+
return pipeline;
|
|
566
|
+
}
|
|
567
|
+
lookup(fieldOrFrom, options) {
|
|
568
|
+
const stages = [...this.stages];
|
|
569
|
+
if (typeof fieldOrFrom === "object") {
|
|
570
|
+
const foreignName = fieldOrFrom.name;
|
|
571
|
+
const foreignField = options?.on;
|
|
572
|
+
if (!foreignField) {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`[zodmon] lookup: reverse lookup on '${foreignName}' requires an 'on' option specifying which field on the foreign collection references this collection.`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
const asField = options?.as ?? foreignName;
|
|
578
|
+
stages.push({
|
|
579
|
+
$lookup: {
|
|
580
|
+
from: foreignName,
|
|
581
|
+
localField: "_id",
|
|
582
|
+
foreignField,
|
|
583
|
+
as: asField
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
if (options?.unwind) {
|
|
587
|
+
stages.push({ $unwind: { path: `$${asField}`, preserveNullAndEmptyArrays: true } });
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
const shape = this.definition.shape;
|
|
591
|
+
const fieldSchema = shape[fieldOrFrom];
|
|
592
|
+
const ref = getRefMetadata(fieldSchema);
|
|
593
|
+
if (!ref) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
`[zodmon] lookup: field '${fieldOrFrom}' has no .ref() metadata. Use .lookup(CollectionDef, { on: foreignKey }) for reverse lookups, or add .ref(TargetCollection) to the field schema.`
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const targetName = ref.collection.name;
|
|
599
|
+
const asField = options?.as ?? targetName;
|
|
600
|
+
stages.push({
|
|
601
|
+
$lookup: {
|
|
602
|
+
from: targetName,
|
|
603
|
+
localField: fieldOrFrom,
|
|
604
|
+
foreignField: "_id",
|
|
605
|
+
as: asField
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
if (options?.unwind) {
|
|
609
|
+
stages.push({ $unwind: { path: `$${asField}`, preserveNullAndEmptyArrays: true } });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, stages);
|
|
613
|
+
return pipeline;
|
|
614
|
+
}
|
|
615
|
+
// ── Convenience shortcuts ────────────────────────────────────────
|
|
616
|
+
/**
|
|
617
|
+
* Count documents per group, sorted by count descending.
|
|
618
|
+
*
|
|
619
|
+
* Shorthand for `.groupBy(field, { count: $count() }).sort({ count: -1 })`.
|
|
620
|
+
*
|
|
621
|
+
* @param field - The field to group and count by.
|
|
622
|
+
* @returns A new pipeline producing `{ _id: TOutput[K], count: number }` results.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* const roleCounts = await aggregate(users)
|
|
627
|
+
* .countBy('role')
|
|
628
|
+
* .toArray()
|
|
629
|
+
* // [{ _id: 'user', count: 3 }, { _id: 'admin', count: 2 }]
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
countBy(field) {
|
|
633
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
634
|
+
...this.stages,
|
|
635
|
+
{ $group: { _id: `$${field}`, count: { $sum: 1 } } },
|
|
636
|
+
{ $sort: { count: -1 } }
|
|
637
|
+
]);
|
|
638
|
+
return pipeline;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Sum a numeric field per group, sorted by total descending.
|
|
642
|
+
*
|
|
643
|
+
* Shorthand for `.groupBy(field, { total: $sum('$sumField') }).sort({ total: -1 })`.
|
|
644
|
+
*
|
|
645
|
+
* @param field - The field to group by.
|
|
646
|
+
* @param sumField - The numeric field to sum.
|
|
647
|
+
* @returns A new pipeline producing `{ _id: TOutput[K], total: number }` results.
|
|
648
|
+
*
|
|
649
|
+
* @example
|
|
650
|
+
* ```ts
|
|
651
|
+
* const revenueByCategory = await aggregate(orders)
|
|
652
|
+
* .sumBy('category', 'amount')
|
|
653
|
+
* .toArray()
|
|
654
|
+
* // [{ _id: 'electronics', total: 5000 }, ...]
|
|
655
|
+
* ```
|
|
656
|
+
*/
|
|
657
|
+
sumBy(field, sumField) {
|
|
658
|
+
const pipeline = new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
659
|
+
...this.stages,
|
|
660
|
+
{ $group: { _id: `$${field}`, total: { $sum: `$${sumField}` } } },
|
|
661
|
+
{ $sort: { total: -1 } }
|
|
662
|
+
]);
|
|
663
|
+
return pipeline;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Sort by a single field with a friendly direction name.
|
|
667
|
+
*
|
|
668
|
+
* Shorthand for `.sort({ [field]: direction === 'desc' ? -1 : 1 })`.
|
|
669
|
+
*
|
|
670
|
+
* @param field - The field to sort by.
|
|
671
|
+
* @param direction - Sort direction: `'asc'` (default) or `'desc'`.
|
|
672
|
+
* @returns A new pipeline with the `$sort` stage appended.
|
|
673
|
+
*
|
|
674
|
+
* @example
|
|
675
|
+
* ```ts
|
|
676
|
+
* const youngest = await aggregate(users)
|
|
677
|
+
* .sortBy('age')
|
|
678
|
+
* .toArray()
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
681
|
+
sortBy(field, direction = "asc") {
|
|
682
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
683
|
+
...this.stages,
|
|
684
|
+
{ $sort: { [field]: direction === "desc" ? -1 : 1 } }
|
|
685
|
+
]);
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Return the top N documents sorted by a field descending.
|
|
689
|
+
*
|
|
690
|
+
* Shorthand for `.sort({ [by]: -1 }).limit(n)`.
|
|
691
|
+
*
|
|
692
|
+
* @param n - The number of documents to return.
|
|
693
|
+
* @param options - An object with a `by` field specifying the sort key.
|
|
694
|
+
* @returns A new pipeline with `$sort` and `$limit` stages appended.
|
|
695
|
+
*
|
|
696
|
+
* @example
|
|
697
|
+
* ```ts
|
|
698
|
+
* const top3 = await aggregate(users)
|
|
699
|
+
* .top(3, { by: 'score' })
|
|
700
|
+
* .toArray()
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
top(n, options) {
|
|
704
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
705
|
+
...this.stages,
|
|
706
|
+
{ $sort: { [options.by]: -1 } },
|
|
707
|
+
{ $limit: n }
|
|
708
|
+
]);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Return the bottom N documents sorted by a field ascending.
|
|
712
|
+
*
|
|
713
|
+
* Shorthand for `.sort({ [by]: 1 }).limit(n)`.
|
|
714
|
+
*
|
|
715
|
+
* @param n - The number of documents to return.
|
|
716
|
+
* @param options - An object with a `by` field specifying the sort key.
|
|
717
|
+
* @returns A new pipeline with `$sort` and `$limit` stages appended.
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```ts
|
|
721
|
+
* const bottom3 = await aggregate(users)
|
|
722
|
+
* .bottom(3, { by: 'score' })
|
|
723
|
+
* .toArray()
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
bottom(n, options) {
|
|
727
|
+
return new _AggregatePipeline(this.definition, this.nativeCollection, [
|
|
728
|
+
...this.stages,
|
|
729
|
+
{ $sort: { [options.by]: 1 } },
|
|
730
|
+
{ $limit: n }
|
|
731
|
+
]);
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
function aggregate(handle) {
|
|
735
|
+
return new AggregatePipeline(handle.definition, handle.native, []);
|
|
736
|
+
}
|
|
737
|
+
|
|
69
738
|
// src/client/client.ts
|
|
70
739
|
var import_mongodb2 = require("mongodb");
|
|
71
740
|
|
|
741
|
+
// src/indexes/spec.ts
|
|
742
|
+
function toFieldIndexSpec(def) {
|
|
743
|
+
const direction = def.text ? "text" : def.descending ? -1 : 1;
|
|
744
|
+
const key = { [def.field]: direction };
|
|
745
|
+
const options = {};
|
|
746
|
+
if (def.unique) options["unique"] = true;
|
|
747
|
+
if (def.sparse) options["sparse"] = true;
|
|
748
|
+
if (def.expireAfter !== void 0) options["expireAfterSeconds"] = def.expireAfter;
|
|
749
|
+
if (def.partial) options["partialFilterExpression"] = def.partial;
|
|
750
|
+
return { key, options };
|
|
751
|
+
}
|
|
752
|
+
function toCompoundIndexSpec(def) {
|
|
753
|
+
const key = { ...def.fields };
|
|
754
|
+
const options = {};
|
|
755
|
+
if (def.options?.unique) options["unique"] = true;
|
|
756
|
+
if (def.options?.sparse) options["sparse"] = true;
|
|
757
|
+
if (def.options?.name) options["name"] = def.options.name;
|
|
758
|
+
if (def.options?.partial) options["partialFilterExpression"] = def.options.partial;
|
|
759
|
+
return { key, options };
|
|
760
|
+
}
|
|
761
|
+
function serializeIndexKey(key) {
|
|
762
|
+
return Object.entries(key).map(([field, dir]) => `${field}:${dir}`).join(",");
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/indexes/sync.ts
|
|
766
|
+
var COMPARABLE_OPTION_KEYS = [
|
|
767
|
+
"unique",
|
|
768
|
+
"sparse",
|
|
769
|
+
"expireAfterSeconds",
|
|
770
|
+
"partialFilterExpression"
|
|
771
|
+
];
|
|
772
|
+
function extractComparableOptions(info) {
|
|
773
|
+
const result = {};
|
|
774
|
+
for (const key of COMPARABLE_OPTION_KEYS) {
|
|
775
|
+
if (info[key] !== void 0) {
|
|
776
|
+
result[key] = info[key];
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
function generateIndexName(key) {
|
|
782
|
+
return Object.entries(key).map(([field, dir]) => `${field}_${dir}`).join("_");
|
|
783
|
+
}
|
|
784
|
+
function sortKeys(obj) {
|
|
785
|
+
const sorted = {};
|
|
786
|
+
for (const key of Object.keys(obj).sort()) {
|
|
787
|
+
sorted[key] = obj[key];
|
|
788
|
+
}
|
|
789
|
+
return sorted;
|
|
790
|
+
}
|
|
791
|
+
function resolveSpecName(spec) {
|
|
792
|
+
const specName = spec.options["name"];
|
|
793
|
+
return typeof specName === "string" ? specName : generateIndexName(spec.key);
|
|
794
|
+
}
|
|
795
|
+
function resolveExistingName(info, key) {
|
|
796
|
+
const infoName = info["name"];
|
|
797
|
+
if (typeof infoName === "string") return infoName;
|
|
798
|
+
return generateIndexName(key);
|
|
799
|
+
}
|
|
800
|
+
function optionsMatch(a, b) {
|
|
801
|
+
return JSON.stringify(sortKeys(stripName(a))) === JSON.stringify(sortKeys(stripName(b)));
|
|
802
|
+
}
|
|
803
|
+
function stripName(obj) {
|
|
804
|
+
const { name: _, ...rest } = obj;
|
|
805
|
+
return rest;
|
|
806
|
+
}
|
|
807
|
+
async function processDesiredSpec(spec, existingByKey, native, dryRun, dropOrphaned, acc) {
|
|
808
|
+
const serialized = serializeIndexKey(spec.key);
|
|
809
|
+
const existing = existingByKey.get(serialized);
|
|
810
|
+
if (!existing) {
|
|
811
|
+
if (!dryRun) await native.createIndex(spec.key, spec.options);
|
|
812
|
+
acc.created.push(resolveSpecName(spec));
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
acc.matchedKeys.add(serialized);
|
|
816
|
+
const existingName = resolveExistingName(existing, spec.key);
|
|
817
|
+
const existingOpts = extractComparableOptions(existing);
|
|
818
|
+
if (optionsMatch(existingOpts, spec.options)) {
|
|
819
|
+
acc.skipped.push(existingName);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (dropOrphaned) {
|
|
823
|
+
if (!dryRun) {
|
|
824
|
+
await native.dropIndex(existingName);
|
|
825
|
+
await native.createIndex(spec.key, spec.options);
|
|
826
|
+
}
|
|
827
|
+
acc.dropped.push(existingName);
|
|
828
|
+
acc.created.push(resolveSpecName(spec));
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
acc.stale.push({
|
|
832
|
+
name: existingName,
|
|
833
|
+
key: spec.key,
|
|
834
|
+
existing: existingOpts,
|
|
835
|
+
desired: spec.options
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
async function processOrphanedIndexes(existingIndexes, desiredKeys, matchedKeys, native, dryRun, dropOrphaned, dropped) {
|
|
839
|
+
for (const idx of existingIndexes) {
|
|
840
|
+
const rawName = idx["name"];
|
|
841
|
+
const name = typeof rawName === "string" ? rawName : "";
|
|
842
|
+
if (name === "_id_") continue;
|
|
843
|
+
const serialized = serializeIndexKey(idx["key"]);
|
|
844
|
+
if (matchedKeys.has(serialized) || desiredKeys.has(serialized)) continue;
|
|
845
|
+
if (dropOrphaned) {
|
|
846
|
+
if (!dryRun) await native.dropIndex(name);
|
|
847
|
+
dropped.push(name);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
async function listIndexesSafe(native) {
|
|
852
|
+
try {
|
|
853
|
+
return await native.listIndexes().toArray();
|
|
854
|
+
} catch (err) {
|
|
855
|
+
if (err instanceof Error && err.message.includes("ns does not exist")) {
|
|
856
|
+
return [];
|
|
857
|
+
}
|
|
858
|
+
throw err;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
async function syncIndexes(handle, options) {
|
|
862
|
+
const { dryRun = false, dropOrphaned = false } = options ?? {};
|
|
863
|
+
const native = handle.native;
|
|
864
|
+
const def = handle.definition;
|
|
865
|
+
const desiredSpecs = [
|
|
866
|
+
...def.fieldIndexes.map(toFieldIndexSpec),
|
|
867
|
+
...def.compoundIndexes.map(toCompoundIndexSpec)
|
|
868
|
+
];
|
|
869
|
+
const existingIndexes = await listIndexesSafe(native);
|
|
870
|
+
const existingByKey = /* @__PURE__ */ new Map();
|
|
871
|
+
for (const idx of existingIndexes) {
|
|
872
|
+
const serialized = serializeIndexKey(idx["key"]);
|
|
873
|
+
existingByKey.set(serialized, idx);
|
|
874
|
+
}
|
|
875
|
+
const acc = {
|
|
876
|
+
created: [],
|
|
877
|
+
dropped: [],
|
|
878
|
+
skipped: [],
|
|
879
|
+
stale: [],
|
|
880
|
+
matchedKeys: /* @__PURE__ */ new Set()
|
|
881
|
+
};
|
|
882
|
+
for (const spec of desiredSpecs) {
|
|
883
|
+
await processDesiredSpec(spec, existingByKey, native, dryRun, dropOrphaned, acc);
|
|
884
|
+
}
|
|
885
|
+
const desiredKeys = new Set(desiredSpecs.map((s) => serializeIndexKey(s.key)));
|
|
886
|
+
await processOrphanedIndexes(
|
|
887
|
+
existingIndexes,
|
|
888
|
+
desiredKeys,
|
|
889
|
+
acc.matchedKeys,
|
|
890
|
+
native,
|
|
891
|
+
dryRun,
|
|
892
|
+
dropOrphaned,
|
|
893
|
+
acc.dropped
|
|
894
|
+
);
|
|
895
|
+
return {
|
|
896
|
+
created: acc.created,
|
|
897
|
+
dropped: acc.dropped,
|
|
898
|
+
skipped: acc.skipped,
|
|
899
|
+
stale: acc.stale
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
|
|
72
903
|
// src/crud/delete.ts
|
|
73
|
-
var
|
|
904
|
+
var import_zod2 = require("zod");
|
|
74
905
|
|
|
75
906
|
// src/errors/validation.ts
|
|
76
907
|
var ZodmonValidationError = class extends Error {
|
|
@@ -111,7 +942,7 @@ async function findOneAndDelete(handle, filter, options) {
|
|
|
111
942
|
try {
|
|
112
943
|
return handle.definition.schema.parse(result);
|
|
113
944
|
} catch (err) {
|
|
114
|
-
if (err instanceof
|
|
945
|
+
if (err instanceof import_zod2.z.ZodError) {
|
|
115
946
|
throw new ZodmonValidationError(handle.definition.name, err);
|
|
116
947
|
}
|
|
117
948
|
throw err;
|
|
@@ -119,7 +950,7 @@ async function findOneAndDelete(handle, filter, options) {
|
|
|
119
950
|
}
|
|
120
951
|
|
|
121
952
|
// src/crud/find.ts
|
|
122
|
-
var
|
|
953
|
+
var import_zod4 = require("zod");
|
|
123
954
|
|
|
124
955
|
// src/errors/not-found.ts
|
|
125
956
|
var ZodmonNotFoundError = class extends Error {
|
|
@@ -132,8 +963,31 @@ var ZodmonNotFoundError = class extends Error {
|
|
|
132
963
|
}
|
|
133
964
|
};
|
|
134
965
|
|
|
966
|
+
// src/indexes/warn.ts
|
|
967
|
+
var SKIP_OPERATORS = /* @__PURE__ */ new Set(["$or", "$and", "$nor", "$text", "$where", "$expr", "$comment"]);
|
|
968
|
+
function checkUnindexedFields(definition, filter) {
|
|
969
|
+
if (definition.options.warnUnindexedQueries !== true) return;
|
|
970
|
+
const covered = /* @__PURE__ */ new Set();
|
|
971
|
+
for (const fi of definition.fieldIndexes) {
|
|
972
|
+
covered.add(fi.field);
|
|
973
|
+
}
|
|
974
|
+
for (const ci of definition.compoundIndexes) {
|
|
975
|
+
const firstField = Object.keys(ci.fields)[0];
|
|
976
|
+
if (firstField !== void 0) {
|
|
977
|
+
covered.add(firstField);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
for (const key of Object.keys(filter)) {
|
|
981
|
+
if (key === "_id") continue;
|
|
982
|
+
if (SKIP_OPERATORS.has(key)) continue;
|
|
983
|
+
if (!covered.has(key)) {
|
|
984
|
+
console.warn(`[zodmon] warn: query on '${definition.name}' uses unindexed field '${key}'`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
135
989
|
// src/query/cursor.ts
|
|
136
|
-
var
|
|
990
|
+
var import_zod3 = require("zod");
|
|
137
991
|
|
|
138
992
|
// src/crud/paginate.ts
|
|
139
993
|
var import_mongodb = require("mongodb");
|
|
@@ -149,8 +1003,8 @@ function deserializeValue(value) {
|
|
|
149
1003
|
}
|
|
150
1004
|
return value;
|
|
151
1005
|
}
|
|
152
|
-
function encodeCursor(doc,
|
|
153
|
-
const values =
|
|
1006
|
+
function encodeCursor(doc, sortKeys2, direction) {
|
|
1007
|
+
const values = sortKeys2.map(([field]) => serializeValue(doc[field]));
|
|
154
1008
|
return btoa(JSON.stringify([direction, ...values]));
|
|
155
1009
|
}
|
|
156
1010
|
function decodeCursor(cursor) {
|
|
@@ -169,14 +1023,14 @@ function decodeCursor(cursor) {
|
|
|
169
1023
|
}
|
|
170
1024
|
return { direction, values: rawValues.map(deserializeValue) };
|
|
171
1025
|
}
|
|
172
|
-
function buildCursorFilter(
|
|
1026
|
+
function buildCursorFilter(sortKeys2, values, isBackward) {
|
|
173
1027
|
const clauses = [];
|
|
174
|
-
for (let i = 0; i <
|
|
1028
|
+
for (let i = 0; i < sortKeys2.length; i++) {
|
|
175
1029
|
const clause = {};
|
|
176
1030
|
for (let j = 0; j < i; j++) {
|
|
177
|
-
clause[
|
|
1031
|
+
clause[sortKeys2[j][0]] = values[j];
|
|
178
1032
|
}
|
|
179
|
-
const [field, direction] =
|
|
1033
|
+
const [field, direction] = sortKeys2[i];
|
|
180
1034
|
const isAsc = direction === 1;
|
|
181
1035
|
const op = isAsc !== isBackward ? "$gt" : "$lt";
|
|
182
1036
|
if (values[i] === null) {
|
|
@@ -272,14 +1126,37 @@ var TypedFindCursor = class {
|
|
|
272
1126
|
this.cursor.limit(n);
|
|
273
1127
|
return this;
|
|
274
1128
|
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Force the query optimizer to use the specified index.
|
|
1131
|
+
*
|
|
1132
|
+
* Only accepts index names that were declared via `.name()` in the
|
|
1133
|
+
* collection definition. If no named indexes exist, any string is accepted.
|
|
1134
|
+
*
|
|
1135
|
+
* @param indexName - The name of a declared compound index.
|
|
1136
|
+
* @returns `this` for chaining.
|
|
1137
|
+
*
|
|
1138
|
+
* @example
|
|
1139
|
+
* ```ts
|
|
1140
|
+
* const Users = collection('users', { email: z.string(), role: z.string() }, {
|
|
1141
|
+
* indexes: [index({ email: 1, role: -1 }).name('email_role_idx')],
|
|
1142
|
+
* })
|
|
1143
|
+
* const admins = await users.find({ role: 'admin' })
|
|
1144
|
+
* .hint('email_role_idx')
|
|
1145
|
+
* .toArray()
|
|
1146
|
+
* ```
|
|
1147
|
+
*/
|
|
1148
|
+
hint(indexName) {
|
|
1149
|
+
this.cursor.hint(indexName);
|
|
1150
|
+
return this;
|
|
1151
|
+
}
|
|
275
1152
|
async paginate(opts) {
|
|
276
1153
|
const sortRecord = this.sortSpec ? this.sortSpec : null;
|
|
277
|
-
const
|
|
278
|
-
const sort = Object.fromEntries(
|
|
1154
|
+
const sortKeys2 = resolveSortKeys(sortRecord);
|
|
1155
|
+
const sort = Object.fromEntries(sortKeys2);
|
|
279
1156
|
if ("page" in opts) {
|
|
280
|
-
return await this.offsetPaginate(
|
|
1157
|
+
return await this.offsetPaginate(sortKeys2, sort, opts);
|
|
281
1158
|
}
|
|
282
|
-
return await this.cursorPaginate(
|
|
1159
|
+
return await this.cursorPaginate(sortKeys2, sort, opts);
|
|
283
1160
|
}
|
|
284
1161
|
/** @internal Offset pagination implementation. */
|
|
285
1162
|
async offsetPaginate(_sortKeys, sort, opts) {
|
|
@@ -300,16 +1177,16 @@ var TypedFindCursor = class {
|
|
|
300
1177
|
};
|
|
301
1178
|
}
|
|
302
1179
|
/** @internal Cursor pagination implementation. */
|
|
303
|
-
async cursorPaginate(
|
|
1180
|
+
async cursorPaginate(sortKeys2, sort, opts) {
|
|
304
1181
|
let isBackward = false;
|
|
305
1182
|
let combinedFilter = this.filter;
|
|
306
1183
|
if (opts.cursor) {
|
|
307
1184
|
const decoded = decodeCursor(opts.cursor);
|
|
308
1185
|
isBackward = decoded.direction === "b";
|
|
309
|
-
const cursorFilter = buildCursorFilter(
|
|
1186
|
+
const cursorFilter = buildCursorFilter(sortKeys2, decoded.values, isBackward);
|
|
310
1187
|
combinedFilter = this.filter && Object.keys(this.filter).length > 0 ? { $and: [this.filter, cursorFilter] } : cursorFilter;
|
|
311
1188
|
}
|
|
312
|
-
const effectiveSort = isBackward ? Object.fromEntries(
|
|
1189
|
+
const effectiveSort = isBackward ? Object.fromEntries(sortKeys2.map(([f, d]) => [f, d === 1 ? -1 : 1])) : sort;
|
|
313
1190
|
const raw2 = await this.nativeCollection.find(combinedFilter).sort(effectiveSort).limit(opts.limit + 1).toArray();
|
|
314
1191
|
const hasMore = raw2.length > opts.limit;
|
|
315
1192
|
if (hasMore) raw2.pop();
|
|
@@ -319,8 +1196,8 @@ var TypedFindCursor = class {
|
|
|
319
1196
|
docs,
|
|
320
1197
|
hasNext: isBackward ? true : hasMore,
|
|
321
1198
|
hasPrev: isBackward ? hasMore : opts.cursor != null,
|
|
322
|
-
startCursor: docs.length > 0 ? encodeCursor(docs[0],
|
|
323
|
-
endCursor: docs.length > 0 ? encodeCursor(docs[docs.length - 1],
|
|
1199
|
+
startCursor: docs.length > 0 ? encodeCursor(docs[0], sortKeys2, "b") : null,
|
|
1200
|
+
endCursor: docs.length > 0 ? encodeCursor(docs[docs.length - 1], sortKeys2, "f") : null
|
|
324
1201
|
};
|
|
325
1202
|
}
|
|
326
1203
|
/**
|
|
@@ -370,7 +1247,7 @@ var TypedFindCursor = class {
|
|
|
370
1247
|
try {
|
|
371
1248
|
return this.schema.parse(raw2);
|
|
372
1249
|
} catch (err) {
|
|
373
|
-
if (err instanceof
|
|
1250
|
+
if (err instanceof import_zod3.z.ZodError) {
|
|
374
1251
|
throw new ZodmonValidationError(this.collectionName, err);
|
|
375
1252
|
}
|
|
376
1253
|
throw err;
|
|
@@ -380,6 +1257,7 @@ var TypedFindCursor = class {
|
|
|
380
1257
|
|
|
381
1258
|
// src/crud/find.ts
|
|
382
1259
|
async function findOne(handle, filter, options) {
|
|
1260
|
+
checkUnindexedFields(handle.definition, filter);
|
|
383
1261
|
const findOptions = options?.project ? { projection: options.project } : void 0;
|
|
384
1262
|
const raw2 = await handle.native.findOne(filter, findOptions);
|
|
385
1263
|
if (!raw2) return null;
|
|
@@ -390,7 +1268,7 @@ async function findOne(handle, filter, options) {
|
|
|
390
1268
|
try {
|
|
391
1269
|
return handle.definition.schema.parse(raw2);
|
|
392
1270
|
} catch (err) {
|
|
393
|
-
if (err instanceof
|
|
1271
|
+
if (err instanceof import_zod4.z.ZodError) {
|
|
394
1272
|
throw new ZodmonValidationError(handle.definition.name, err);
|
|
395
1273
|
}
|
|
396
1274
|
throw err;
|
|
@@ -404,6 +1282,7 @@ async function findOneOrThrow(handle, filter, options) {
|
|
|
404
1282
|
return doc;
|
|
405
1283
|
}
|
|
406
1284
|
function find(handle, filter, options) {
|
|
1285
|
+
checkUnindexedFields(handle.definition, filter);
|
|
407
1286
|
const raw2 = handle.native.find(filter);
|
|
408
1287
|
const cursor = raw2;
|
|
409
1288
|
const mode = options?.validate !== void 0 ? options.validate : handle.definition.options.validation;
|
|
@@ -411,13 +1290,13 @@ function find(handle, filter, options) {
|
|
|
411
1290
|
}
|
|
412
1291
|
|
|
413
1292
|
// src/crud/insert.ts
|
|
414
|
-
var
|
|
1293
|
+
var import_zod5 = require("zod");
|
|
415
1294
|
async function insertOne(handle, doc) {
|
|
416
1295
|
let parsed;
|
|
417
1296
|
try {
|
|
418
1297
|
parsed = handle.definition.schema.parse(doc);
|
|
419
1298
|
} catch (err) {
|
|
420
|
-
if (err instanceof
|
|
1299
|
+
if (err instanceof import_zod5.z.ZodError) {
|
|
421
1300
|
throw new ZodmonValidationError(handle.definition.name, err);
|
|
422
1301
|
}
|
|
423
1302
|
throw err;
|
|
@@ -432,7 +1311,7 @@ async function insertMany(handle, docs) {
|
|
|
432
1311
|
try {
|
|
433
1312
|
parsed.push(handle.definition.schema.parse(doc));
|
|
434
1313
|
} catch (err) {
|
|
435
|
-
if (err instanceof
|
|
1314
|
+
if (err instanceof import_zod5.z.ZodError) {
|
|
436
1315
|
throw new ZodmonValidationError(handle.definition.name, err);
|
|
437
1316
|
}
|
|
438
1317
|
throw err;
|
|
@@ -443,7 +1322,7 @@ async function insertMany(handle, docs) {
|
|
|
443
1322
|
}
|
|
444
1323
|
|
|
445
1324
|
// src/crud/update.ts
|
|
446
|
-
var
|
|
1325
|
+
var import_zod6 = require("zod");
|
|
447
1326
|
async function updateOne(handle, filter, update, options) {
|
|
448
1327
|
return await handle.native.updateOne(filter, update, options);
|
|
449
1328
|
}
|
|
@@ -474,7 +1353,7 @@ async function findOneAndUpdate(handle, filter, update, options) {
|
|
|
474
1353
|
try {
|
|
475
1354
|
return handle.definition.schema.parse(result);
|
|
476
1355
|
} catch (err) {
|
|
477
|
-
if (err instanceof
|
|
1356
|
+
if (err instanceof import_zod6.z.ZodError) {
|
|
478
1357
|
throw new ZodmonValidationError(handle.definition.name, err);
|
|
479
1358
|
}
|
|
480
1359
|
throw err;
|
|
@@ -735,6 +1614,57 @@ var CollectionHandle = class {
|
|
|
735
1614
|
async findOneAndDelete(filter, options) {
|
|
736
1615
|
return await findOneAndDelete(this, filter, options);
|
|
737
1616
|
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Synchronize the indexes declared in this collection's schema with MongoDB.
|
|
1619
|
+
*
|
|
1620
|
+
* Compares the desired indexes (from field-level `.index()` / `.unique()` /
|
|
1621
|
+
* `.text()` / `.expireAfter()` and compound `indexes` in collection options)
|
|
1622
|
+
* with the indexes that currently exist in MongoDB, then creates, drops, or
|
|
1623
|
+
* reports differences depending on the options.
|
|
1624
|
+
*
|
|
1625
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
1626
|
+
* @returns A summary of created, dropped, skipped, and stale indexes.
|
|
1627
|
+
*
|
|
1628
|
+
* @example
|
|
1629
|
+
* ```ts
|
|
1630
|
+
* const users = db.use(Users)
|
|
1631
|
+
* const result = await users.syncIndexes()
|
|
1632
|
+
* console.log('Created:', result.created)
|
|
1633
|
+
* console.log('Stale:', result.stale.map(s => s.name))
|
|
1634
|
+
* ```
|
|
1635
|
+
*
|
|
1636
|
+
* @example
|
|
1637
|
+
* ```ts
|
|
1638
|
+
* // Dry run to preview changes without modifying the database
|
|
1639
|
+
* const diff = await users.syncIndexes({ dryRun: true })
|
|
1640
|
+
* console.log('Would create:', diff.created)
|
|
1641
|
+
* console.log('Would drop:', diff.dropped)
|
|
1642
|
+
* ```
|
|
1643
|
+
*/
|
|
1644
|
+
async syncIndexes(options) {
|
|
1645
|
+
return await syncIndexes(this, options);
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Start a type-safe aggregation pipeline on this collection.
|
|
1649
|
+
*
|
|
1650
|
+
* Returns a fluent pipeline builder that tracks the output document
|
|
1651
|
+
* shape through each stage. The pipeline is lazy — no query executes
|
|
1652
|
+
* until a terminal method (`toArray`, `for await`, `explain`) is called.
|
|
1653
|
+
*
|
|
1654
|
+
* @returns A new pipeline builder starting with this collection's document type.
|
|
1655
|
+
*
|
|
1656
|
+
* @example
|
|
1657
|
+
* ```ts
|
|
1658
|
+
* const users = db.use(Users)
|
|
1659
|
+
* const result = await users.aggregate()
|
|
1660
|
+
* .match({ role: 'admin' })
|
|
1661
|
+
* .groupBy('role', { count: $count() })
|
|
1662
|
+
* .toArray()
|
|
1663
|
+
* ```
|
|
1664
|
+
*/
|
|
1665
|
+
aggregate() {
|
|
1666
|
+
return aggregate(this);
|
|
1667
|
+
}
|
|
738
1668
|
};
|
|
739
1669
|
|
|
740
1670
|
// src/client/client.ts
|
|
@@ -767,12 +1697,33 @@ var Database = class {
|
|
|
767
1697
|
);
|
|
768
1698
|
}
|
|
769
1699
|
/**
|
|
770
|
-
* Synchronize indexes
|
|
1700
|
+
* Synchronize indexes for all registered collections with MongoDB.
|
|
1701
|
+
*
|
|
1702
|
+
* Iterates every collection registered via {@link use} and calls
|
|
1703
|
+
* {@link syncIndexes} on each one. Returns a record keyed by collection
|
|
1704
|
+
* name with the sync result for each.
|
|
1705
|
+
*
|
|
1706
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
1707
|
+
* @returns A record mapping collection names to their sync results.
|
|
771
1708
|
*
|
|
772
|
-
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```ts
|
|
1711
|
+
* const db = createClient('mongodb://localhost:27017', 'myapp')
|
|
1712
|
+
* db.use(Users)
|
|
1713
|
+
* db.use(Posts)
|
|
1714
|
+
* const results = await db.syncIndexes()
|
|
1715
|
+
* console.log(results['users'].created) // ['email_1']
|
|
1716
|
+
* console.log(results['posts'].created) // ['title_1']
|
|
1717
|
+
* ```
|
|
773
1718
|
*/
|
|
774
|
-
syncIndexes() {
|
|
775
|
-
|
|
1719
|
+
async syncIndexes(options) {
|
|
1720
|
+
const results = {};
|
|
1721
|
+
for (const [name, def] of this._collections) {
|
|
1722
|
+
const native = this._db.collection(name);
|
|
1723
|
+
const handle = new CollectionHandle(def, native);
|
|
1724
|
+
results[name] = await syncIndexes(handle, options);
|
|
1725
|
+
}
|
|
1726
|
+
return results;
|
|
776
1727
|
}
|
|
777
1728
|
/**
|
|
778
1729
|
* Execute a function within a MongoDB transaction with auto-commit/rollback.
|
|
@@ -817,36 +1768,6 @@ var import_zod9 = require("zod");
|
|
|
817
1768
|
|
|
818
1769
|
// src/schema/extensions.ts
|
|
819
1770
|
var import_zod7 = require("zod");
|
|
820
|
-
|
|
821
|
-
// src/schema/ref.ts
|
|
822
|
-
var import_zod6 = require("zod");
|
|
823
|
-
var refMetadata = /* @__PURE__ */ new WeakMap();
|
|
824
|
-
function getRefMetadata(schema) {
|
|
825
|
-
if (typeof schema !== "object" || schema === null) return void 0;
|
|
826
|
-
return refMetadata.get(schema);
|
|
827
|
-
}
|
|
828
|
-
var REF_GUARD = /* @__PURE__ */ Symbol.for("zodmon_ref");
|
|
829
|
-
function installRefExtension() {
|
|
830
|
-
const proto = import_zod6.z.ZodType.prototype;
|
|
831
|
-
if (REF_GUARD in proto) return;
|
|
832
|
-
Object.defineProperty(proto, "ref", {
|
|
833
|
-
value(collection2) {
|
|
834
|
-
refMetadata.set(this, { collection: collection2 });
|
|
835
|
-
return this;
|
|
836
|
-
},
|
|
837
|
-
enumerable: true,
|
|
838
|
-
configurable: true,
|
|
839
|
-
writable: true
|
|
840
|
-
});
|
|
841
|
-
Object.defineProperty(proto, REF_GUARD, {
|
|
842
|
-
value: true,
|
|
843
|
-
enumerable: false,
|
|
844
|
-
configurable: false,
|
|
845
|
-
writable: false
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// src/schema/extensions.ts
|
|
850
1771
|
var indexMetadata = /* @__PURE__ */ new WeakMap();
|
|
851
1772
|
function getIndexMetadata(schema) {
|
|
852
1773
|
if (typeof schema !== "object" || schema === null) return void 0;
|
|
@@ -987,6 +1908,8 @@ function collection(name, shape, options) {
|
|
|
987
1908
|
schema,
|
|
988
1909
|
shape,
|
|
989
1910
|
fieldIndexes,
|
|
1911
|
+
// Safe cast: compoundIndexes is TIndexes at runtime (or an empty array when
|
|
1912
|
+
// no options provided). The spread into [...TIndexes] preserves the tuple type.
|
|
990
1913
|
compoundIndexes: compoundIndexes ?? [],
|
|
991
1914
|
options: {
|
|
992
1915
|
validation: validation ?? "strict",
|
|
@@ -1011,12 +1934,29 @@ var IndexBuilder = class _IndexBuilder {
|
|
|
1011
1934
|
options
|
|
1012
1935
|
});
|
|
1013
1936
|
}
|
|
1937
|
+
// Safe cast: _clone returns IndexBuilder<TKeys> but `this` may carry an
|
|
1938
|
+
// intersection from .name(). The cast is safe because _clone preserves all fields.
|
|
1014
1939
|
unique() {
|
|
1015
1940
|
return this._clone({ ...this.options, unique: true });
|
|
1016
1941
|
}
|
|
1942
|
+
// Safe cast: same reasoning as unique().
|
|
1017
1943
|
sparse() {
|
|
1018
1944
|
return this._clone({ ...this.options, sparse: true });
|
|
1019
1945
|
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Set a custom name for this index, preserving the literal type.
|
|
1948
|
+
*
|
|
1949
|
+
* The returned builder carries the literal name type via an intersection,
|
|
1950
|
+
* enabling type-safe `.hint()` on cursors that only accepts declared names.
|
|
1951
|
+
*
|
|
1952
|
+
* @param name - The index name.
|
|
1953
|
+
* @returns A new IndexBuilder with the name recorded at the type level.
|
|
1954
|
+
*
|
|
1955
|
+
* @example
|
|
1956
|
+
* ```ts
|
|
1957
|
+
* index({ email: 1, role: -1 }).name('email_role_idx')
|
|
1958
|
+
* ```
|
|
1959
|
+
*/
|
|
1020
1960
|
name(name) {
|
|
1021
1961
|
return this._clone({ ...this.options, name });
|
|
1022
1962
|
}
|
|
@@ -1078,30 +2018,45 @@ var $ = {
|
|
|
1078
2018
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1079
2019
|
0 && (module.exports = {
|
|
1080
2020
|
$,
|
|
2021
|
+
$addToSet,
|
|
1081
2022
|
$and,
|
|
2023
|
+
$avg,
|
|
2024
|
+
$count,
|
|
1082
2025
|
$eq,
|
|
1083
2026
|
$exists,
|
|
2027
|
+
$first,
|
|
1084
2028
|
$gt,
|
|
1085
2029
|
$gte,
|
|
1086
2030
|
$in,
|
|
2031
|
+
$last,
|
|
1087
2032
|
$lt,
|
|
1088
2033
|
$lte,
|
|
2034
|
+
$max,
|
|
2035
|
+
$min,
|
|
1089
2036
|
$ne,
|
|
1090
2037
|
$nin,
|
|
1091
2038
|
$nor,
|
|
1092
2039
|
$not,
|
|
1093
2040
|
$or,
|
|
2041
|
+
$push,
|
|
1094
2042
|
$regex,
|
|
2043
|
+
$sum,
|
|
2044
|
+
AggregatePipeline,
|
|
1095
2045
|
CollectionHandle,
|
|
1096
2046
|
Database,
|
|
1097
2047
|
IndexBuilder,
|
|
1098
2048
|
TypedFindCursor,
|
|
1099
2049
|
ZodmonNotFoundError,
|
|
1100
2050
|
ZodmonValidationError,
|
|
2051
|
+
aggregate,
|
|
2052
|
+
checkUnindexedFields,
|
|
1101
2053
|
collection,
|
|
2054
|
+
createAccumulatorBuilder,
|
|
1102
2055
|
createClient,
|
|
2056
|
+
createExpressionBuilder,
|
|
1103
2057
|
deleteMany,
|
|
1104
2058
|
deleteOne,
|
|
2059
|
+
extractComparableOptions,
|
|
1105
2060
|
extractDbName,
|
|
1106
2061
|
extractFieldIndexes,
|
|
1107
2062
|
find,
|
|
@@ -1109,6 +2064,7 @@ var $ = {
|
|
|
1109
2064
|
findOneAndDelete,
|
|
1110
2065
|
findOneAndUpdate,
|
|
1111
2066
|
findOneOrThrow,
|
|
2067
|
+
generateIndexName,
|
|
1112
2068
|
getIndexMetadata,
|
|
1113
2069
|
getRefMetadata,
|
|
1114
2070
|
index,
|
|
@@ -1118,6 +2074,10 @@ var $ = {
|
|
|
1118
2074
|
objectId,
|
|
1119
2075
|
oid,
|
|
1120
2076
|
raw,
|
|
2077
|
+
serializeIndexKey,
|
|
2078
|
+
syncIndexes,
|
|
2079
|
+
toCompoundIndexSpec,
|
|
2080
|
+
toFieldIndexSpec,
|
|
1121
2081
|
updateMany,
|
|
1122
2082
|
updateOne
|
|
1123
2083
|
});
|