@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.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ObjectId, DeleteResult, FindCursor, Collection, UpdateResult, MongoClientOptions } from 'mongodb';
|
|
1
|
+
import { ObjectId, Document, DeleteResult, FindCursor, Collection, UpdateResult, MongoClientOptions } from 'mongodb';
|
|
2
2
|
import { ZodPipe, ZodCustom, ZodTransform, z, ZodDefault } from 'zod';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -252,1097 +252,2486 @@ type InferInsert<TDef extends {
|
|
|
252
252
|
/**
|
|
253
253
|
* The immutable definition object returned by collection().
|
|
254
254
|
* Holds everything needed to later create a live collection handle.
|
|
255
|
+
*
|
|
256
|
+
* @typeParam TName - The collection name string literal.
|
|
257
|
+
* @typeParam TShape - The Zod shape defining document fields.
|
|
258
|
+
* @typeParam TIndexes - The compound indexes array type, preserving literal name types.
|
|
255
259
|
*/
|
|
256
|
-
type CollectionDefinition<TShape extends z.core.$ZodShape = z.core.$ZodShape> = {
|
|
257
|
-
readonly name:
|
|
260
|
+
type CollectionDefinition<TName extends string = string, TShape extends z.core.$ZodShape = z.core.$ZodShape, TIndexes extends readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[]> = {
|
|
261
|
+
readonly name: TName;
|
|
258
262
|
readonly schema: z.ZodObject<ResolvedShape<TShape>>;
|
|
259
263
|
readonly shape: TShape;
|
|
260
264
|
readonly fieldIndexes: FieldIndexDefinition[];
|
|
261
|
-
readonly compoundIndexes:
|
|
265
|
+
readonly compoundIndexes: TIndexes;
|
|
262
266
|
readonly options: Required<Pick<CollectionOptions, 'validation'>> & Omit<CollectionOptions, 'indexes' | 'validation'>;
|
|
263
267
|
};
|
|
264
268
|
/** Erased collection type for use in generic contexts. */
|
|
265
|
-
type AnyCollection = CollectionDefinition<z.core.$ZodShape>;
|
|
266
|
-
|
|
269
|
+
type AnyCollection = CollectionDefinition<string, z.core.$ZodShape>;
|
|
267
270
|
/**
|
|
268
|
-
*
|
|
271
|
+
* Extract declared index names from a collection definition.
|
|
269
272
|
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* Used as the operator object that can be assigned to a field in {@link TypedFilter}.
|
|
273
|
+
* Walks the `compoundIndexes` tuple and extracts the literal `name` string
|
|
274
|
+
* from each index that declared one via `.name()`. Falls back to `string`
|
|
275
|
+
* when no compound indexes have names, allowing any string as a hint.
|
|
274
276
|
*
|
|
275
277
|
* @example
|
|
276
278
|
* ```ts
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
} & (V extends string ? {
|
|
306
|
-
$regex?: RegExp | string;
|
|
307
|
-
} : unknown);
|
|
308
|
-
/** Depth counter for limiting dot-notation recursion. Index = current depth, value = next depth. */
|
|
309
|
-
type Prev = [never, 0, 1, 2];
|
|
279
|
+
* const Users = collection('users', { email: z.string() }, {
|
|
280
|
+
* indexes: [
|
|
281
|
+
* index({ email: 1 }).name('email_idx'),
|
|
282
|
+
* ],
|
|
283
|
+
* })
|
|
284
|
+
* type Names = IndexNames<typeof Users> // 'email_idx'
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
type IndexNames<TDef extends {
|
|
288
|
+
readonly compoundIndexes: readonly {
|
|
289
|
+
options?: {
|
|
290
|
+
name?: string;
|
|
291
|
+
};
|
|
292
|
+
}[];
|
|
293
|
+
}> = ExtractNames<TDef['compoundIndexes']> extends never ? string : ExtractNames<TDef['compoundIndexes']>;
|
|
294
|
+
/** @internal Helper that extracts the `name` literal from each index in a tuple. */
|
|
295
|
+
type ExtractNames<T extends readonly {
|
|
296
|
+
options?: {
|
|
297
|
+
name?: string;
|
|
298
|
+
};
|
|
299
|
+
}[]> = {
|
|
300
|
+
[K in keyof T]: T[K] extends {
|
|
301
|
+
readonly options: {
|
|
302
|
+
readonly name: infer N;
|
|
303
|
+
};
|
|
304
|
+
} ? N extends string ? N : never : never;
|
|
305
|
+
}[number];
|
|
306
|
+
|
|
310
307
|
/**
|
|
311
|
-
*
|
|
308
|
+
* Forces TypeScript to eagerly expand mapped/intersection types in tooltips.
|
|
312
309
|
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* treated as leaf nodes and do not produce sub-paths.
|
|
310
|
+
* Instead of showing `Omit<{ _id: ObjectId; name: string; salary: number }, "salary">`,
|
|
311
|
+
* the IDE will display the resolved shape `{ _id: ObjectId; name: string }`.
|
|
316
312
|
*
|
|
317
313
|
* @example
|
|
318
314
|
* ```ts
|
|
319
|
-
* type
|
|
320
|
-
*
|
|
321
|
-
* // DotPaths<User> = 'address.city' | 'address.geo' | 'address.geo.lat' | 'address.geo.lng'
|
|
315
|
+
* type Expanded = Prettify<Pick<User, 'name' | 'role'>>
|
|
316
|
+
* // ^? { name: string; role: string }
|
|
322
317
|
* ```
|
|
323
318
|
*/
|
|
324
|
-
type
|
|
325
|
-
[K in keyof T
|
|
326
|
-
}
|
|
319
|
+
type Prettify<T> = {
|
|
320
|
+
[K in keyof T]: T[K];
|
|
321
|
+
} & {};
|
|
322
|
+
|
|
327
323
|
/**
|
|
328
|
-
*
|
|
324
|
+
* Type-level marker that carries the target collection type through the
|
|
325
|
+
* type system. Intersected with the schema return type by `.ref()` so
|
|
326
|
+
* that `RefFields<T>` (future) can extract ref relationships.
|
|
329
327
|
*
|
|
330
|
-
*
|
|
331
|
-
|
|
328
|
+
* This is a phantom brand — no runtime value has this property.
|
|
329
|
+
*/
|
|
330
|
+
type RefMarker<TCollection extends AnyCollection = AnyCollection> = {
|
|
331
|
+
readonly _ref: TCollection;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Metadata stored in the WeakMap sidecar for schemas marked with `.ref()`.
|
|
335
|
+
* Holds a reference to the target collection definition object.
|
|
336
|
+
*/
|
|
337
|
+
type RefMetadata = {
|
|
338
|
+
readonly collection: AnyCollection;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Module augmentation: adds `.ref()` to all `ZodType` schemas.
|
|
332
342
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
343
|
+
* The intersection constraint `this['_zod']['output'] extends InferDocument<TCollection>['_id']`
|
|
344
|
+
* ensures compile-time type safety: the field's output type must match the
|
|
345
|
+
* target collection's `_id` type. Mismatches produce a type error.
|
|
336
346
|
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
347
|
+
* Supports both default ObjectId `_id` and custom `_id` types (string, nanoid, etc.):
|
|
348
|
+
* - `objectId().ref(Users)` compiles when Users has ObjectId `_id`
|
|
349
|
+
* - `z.string().ref(Orgs)` compiles when Orgs has string `_id`
|
|
350
|
+
* - `z.string().ref(Users)` is a type error (string ≠ ObjectId)
|
|
351
|
+
* - `objectId().ref(Orgs)` is a type error (ObjectId ≠ string)
|
|
340
352
|
*/
|
|
341
|
-
|
|
353
|
+
declare module 'zod' {
|
|
354
|
+
interface ZodType {
|
|
355
|
+
/**
|
|
356
|
+
* Declare a typed foreign key reference to another collection.
|
|
357
|
+
*
|
|
358
|
+
* Stores the target collection definition in metadata for runtime
|
|
359
|
+
* populate resolution, and brands the return type with
|
|
360
|
+
* `RefMarker<TCollection>` so `RefFields<T>` can extract refs
|
|
361
|
+
* at the type level.
|
|
362
|
+
*
|
|
363
|
+
* The field's output type must match the target collection's `_id` type.
|
|
364
|
+
* Mismatched types produce a compile error.
|
|
365
|
+
*
|
|
366
|
+
* Apply `.ref()` before wrapper methods like `.optional()` or `.nullable()`:
|
|
367
|
+
* `objectId().ref(Users).optional()` — not `objectId().optional().ref(Users)`.
|
|
368
|
+
*
|
|
369
|
+
* @param collection - The target collection definition object.
|
|
370
|
+
* @returns The same schema instance, branded with the ref marker.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```ts
|
|
374
|
+
* const Posts = collection('posts', {
|
|
375
|
+
* authorId: objectId().ref(Users),
|
|
376
|
+
* title: z.string(),
|
|
377
|
+
* })
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
ref<TCollection extends AnyCollection>(collection: TCollection & (this['_zod']['output'] extends InferDocument<TCollection>['_id'] ? unknown : never)): this & RefMarker<TCollection>;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
342
383
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
* Validates filter objects at compile time — rejects nonexistent fields, type mismatches,
|
|
346
|
-
* and invalid operator usage. Unlike the MongoDB driver's `Filter<T>`, does NOT allow
|
|
347
|
-
* arbitrary keys via `& Document`.
|
|
384
|
+
* Retrieve the ref metadata attached to a Zod schema, if any.
|
|
348
385
|
*
|
|
349
|
-
*
|
|
350
|
-
* - **Direct field values** (implicit `$eq`): `{ name: 'Alice' }`
|
|
351
|
-
* - **Comparison operators**: `{ age: { $gt: 25 } }` or `{ age: $gt(25) }`
|
|
352
|
-
* - **Dot notation** for nested fields up to 3 levels: `{ 'address.city': 'NYC' }`
|
|
386
|
+
* Returns `undefined` when the schema was never marked with `.ref()`.
|
|
353
387
|
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
388
|
+
* @param schema - The Zod schema to inspect. Accepts `unknown` for
|
|
389
|
+
* convenience; non-object values safely return `undefined`.
|
|
390
|
+
* @returns The {@link RefMetadata} for the schema, or `undefined`.
|
|
356
391
|
*
|
|
357
392
|
* @example
|
|
358
393
|
* ```ts
|
|
359
|
-
*
|
|
360
|
-
* const
|
|
361
|
-
*
|
|
362
|
-
* // Builder functions mixed with object literals
|
|
363
|
-
* const filter: TypedFilter<User> = { age: $gte(18), role: $in(['admin', 'mod']) }
|
|
364
|
-
*
|
|
365
|
-
* // Logical composition — T inferred from find() context
|
|
366
|
-
* posts.find($and(
|
|
367
|
-
* $or({ published: true }, { views: $gte(100) }),
|
|
368
|
-
* { title: $regex(/guide/i) },
|
|
369
|
-
* ))
|
|
370
|
-
*
|
|
371
|
-
* // Dynamic conditional building
|
|
372
|
-
* const conditions: TypedFilter<User>[] = []
|
|
373
|
-
* if (name) conditions.push({ name })
|
|
374
|
-
* if (minAge) conditions.push({ age: $gte(minAge) })
|
|
375
|
-
* const filter = conditions.length ? $and<User>(...conditions) : {}
|
|
394
|
+
* const authorId = objectId().ref(Users)
|
|
395
|
+
* const meta = getRefMetadata(authorId)
|
|
396
|
+
* // => { collection: Users }
|
|
376
397
|
* ```
|
|
377
398
|
*/
|
|
378
|
-
|
|
379
|
-
[K in keyof T]?: T[K] | ComparisonOperators<T[K]>;
|
|
380
|
-
} & {
|
|
381
|
-
[P in DotPaths<T>]?: DotPathType<T, P> | ComparisonOperators<DotPathType<T, P>>;
|
|
382
|
-
} & {
|
|
383
|
-
/** Joins clauses with a logical AND. Matches documents that satisfy all filters. */
|
|
384
|
-
$and?: TypedFilter<T>[];
|
|
385
|
-
/** Joins clauses with a logical OR. Matches documents that satisfy at least one filter. */
|
|
386
|
-
$or?: TypedFilter<T>[];
|
|
387
|
-
/** Joins clauses with a logical NOR. Matches documents that fail all filters. */
|
|
388
|
-
$nor?: TypedFilter<T>[];
|
|
389
|
-
};
|
|
399
|
+
declare function getRefMetadata(schema: unknown): RefMetadata | undefined;
|
|
390
400
|
|
|
391
401
|
/**
|
|
392
|
-
*
|
|
402
|
+
* Branded wrapper for accumulator expressions used inside `groupBy`.
|
|
403
|
+
*
|
|
404
|
+
* Carries the MongoDB accumulator expression at runtime and the inferred
|
|
405
|
+
* result type `T` at the type level.
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```ts
|
|
409
|
+
* const acc: Accumulator<number> = {
|
|
410
|
+
* __accum: true,
|
|
411
|
+
* expr: { $sum: '$price' },
|
|
412
|
+
* }
|
|
413
|
+
* ```
|
|
393
414
|
*/
|
|
394
|
-
type
|
|
395
|
-
|
|
396
|
-
|
|
415
|
+
type Accumulator<T = unknown> = {
|
|
416
|
+
readonly __accum: true;
|
|
417
|
+
readonly expr: Document;
|
|
418
|
+
/** @internal Phantom field — carries the result type at the type level. */
|
|
419
|
+
readonly _type?: T;
|
|
397
420
|
};
|
|
398
421
|
/**
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* Removes the first document that matches the filter from the collection.
|
|
402
|
-
* No validation is performed — the document is deleted directly through
|
|
403
|
-
* the MongoDB driver.
|
|
404
|
-
*
|
|
405
|
-
* @param handle - The collection handle to delete from.
|
|
406
|
-
* @param filter - Type-safe filter to match documents.
|
|
407
|
-
* @returns The MongoDB `DeleteResult` with the deleted count.
|
|
422
|
+
* Extracts the result type from an `Accumulator<T>`.
|
|
408
423
|
*
|
|
409
424
|
* @example
|
|
410
425
|
* ```ts
|
|
411
|
-
*
|
|
412
|
-
*
|
|
426
|
+
* type Count = InferAccumulator<Accumulator<number>>
|
|
427
|
+
* // ^? number
|
|
413
428
|
* ```
|
|
414
429
|
*/
|
|
415
|
-
|
|
430
|
+
type InferAccumulator<T> = T extends Accumulator<infer R> ? R : never;
|
|
416
431
|
/**
|
|
417
|
-
*
|
|
432
|
+
* Maps an accumulator spec object to its inferred output shape.
|
|
418
433
|
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
* the MongoDB driver.
|
|
434
|
+
* Given `{ count: Accumulator<number>, names: Accumulator<string[]> }`,
|
|
435
|
+
* produces `{ count: number, names: string[] }`.
|
|
422
436
|
*
|
|
423
|
-
* @
|
|
424
|
-
*
|
|
425
|
-
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```ts
|
|
439
|
+
* type Spec = {
|
|
440
|
+
* count: Accumulator<number>
|
|
441
|
+
* total: Accumulator<number>
|
|
442
|
+
* }
|
|
443
|
+
* type Result = InferAccumulators<Spec>
|
|
444
|
+
* // ^? { count: number; total: number }
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
type InferAccumulators<T extends Record<string, Accumulator>> = {
|
|
448
|
+
[K in keyof T]: InferAccumulator<T[K]>;
|
|
449
|
+
};
|
|
450
|
+
/**
|
|
451
|
+
* Field reference string prefixed with `$`, constrained to keys of `T`.
|
|
426
452
|
*
|
|
427
453
|
* @example
|
|
428
454
|
* ```ts
|
|
429
|
-
*
|
|
430
|
-
*
|
|
455
|
+
* type OrderRef = FieldRef<{ price: number; qty: number }>
|
|
456
|
+
* // ^? '$price' | '$qty'
|
|
431
457
|
* ```
|
|
432
458
|
*/
|
|
433
|
-
|
|
459
|
+
type FieldRef<T> = `$${keyof T & string}`;
|
|
434
460
|
/**
|
|
435
|
-
*
|
|
461
|
+
* Typed accumulator factory passed to the `groupBy` callback.
|
|
436
462
|
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
463
|
+
* Each method constrains field names to `keyof T & string`, providing
|
|
464
|
+
* IDE autocomplete and compile-time validation. Return types resolve
|
|
465
|
+
* to the actual field type (`T[K]`), not `unknown`.
|
|
440
466
|
*
|
|
441
|
-
* @
|
|
442
|
-
* @param filter - Type-safe filter to match documents.
|
|
443
|
-
* @param options - Optional settings: `validate`.
|
|
444
|
-
* @returns The deleted document, or `null` if no document matches.
|
|
445
|
-
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
467
|
+
* @typeParam T - The current pipeline output document type.
|
|
446
468
|
*
|
|
447
469
|
* @example
|
|
448
470
|
* ```ts
|
|
449
|
-
*
|
|
450
|
-
*
|
|
471
|
+
* users.aggregate()
|
|
472
|
+
* .groupBy('role', acc => ({
|
|
473
|
+
* minSalary: acc.min('salary'), // Accumulator<number>
|
|
474
|
+
* firstName: acc.first('name'), // Accumulator<string>
|
|
475
|
+
* allNames: acc.push('name'), // Accumulator<string[]>
|
|
476
|
+
* count: acc.count(), // Accumulator<number>
|
|
477
|
+
* }))
|
|
451
478
|
* ```
|
|
479
|
+
*/
|
|
480
|
+
type AccumulatorBuilder<T> = {
|
|
481
|
+
/** Count documents in each group. Always returns `Accumulator<number>`. */
|
|
482
|
+
count(): Accumulator<number>;
|
|
483
|
+
/** Sum a numeric field. Always returns `Accumulator<number>`. */
|
|
484
|
+
sum<K extends keyof T & string>(field: K): Accumulator<number>;
|
|
485
|
+
/** Sum a literal number per document. Always returns `Accumulator<number>`. */
|
|
486
|
+
sum(value: number): Accumulator<number>;
|
|
487
|
+
/** Average a numeric field. Always returns `Accumulator<number>`. */
|
|
488
|
+
avg<K extends keyof T & string>(field: K): Accumulator<number>;
|
|
489
|
+
/** Minimum value of a field. Returns `Accumulator<T[K]>`. */
|
|
490
|
+
min<K extends keyof T & string>(field: K): Accumulator<T[K]>;
|
|
491
|
+
/** Maximum value of a field. Returns `Accumulator<T[K]>`. */
|
|
492
|
+
max<K extends keyof T & string>(field: K): Accumulator<T[K]>;
|
|
493
|
+
/** First value in each group (by document order). Returns `Accumulator<T[K]>`. */
|
|
494
|
+
first<K extends keyof T & string>(field: K): Accumulator<T[K]>;
|
|
495
|
+
/** Last value in each group (by document order). Returns `Accumulator<T[K]>`. */
|
|
496
|
+
last<K extends keyof T & string>(field: K): Accumulator<T[K]>;
|
|
497
|
+
/** Collect values into an array (with duplicates). Returns `Accumulator<T[K][]>`. */
|
|
498
|
+
push<K extends keyof T & string>(field: K): Accumulator<T[K][]>;
|
|
499
|
+
/** Collect unique values into an array. Returns `Accumulator<T[K][]>`. */
|
|
500
|
+
addToSet<K extends keyof T & string>(field: K): Accumulator<T[K][]>;
|
|
501
|
+
};
|
|
502
|
+
/**
|
|
503
|
+
* Resolves the document field type from a `$`-prefixed field reference.
|
|
452
504
|
*
|
|
453
505
|
* @example
|
|
454
506
|
* ```ts
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
* { validate: false },
|
|
459
|
-
* )
|
|
507
|
+
* type Order = { price: number; name: string }
|
|
508
|
+
* type PriceType = FieldRefType<Order, '$price'>
|
|
509
|
+
* // ^? number
|
|
460
510
|
* ```
|
|
461
511
|
*/
|
|
462
|
-
|
|
463
|
-
|
|
512
|
+
type FieldRefType<T, F extends string> = F extends `$${infer K}` ? K extends keyof T ? T[K] : never : never;
|
|
464
513
|
/**
|
|
465
|
-
*
|
|
514
|
+
* Output shape of a single-field `groupBy` stage.
|
|
515
|
+
*
|
|
516
|
+
* The `_id` field holds the grouped-by field's value, and the rest of the
|
|
517
|
+
* shape comes from the inferred accumulator types.
|
|
466
518
|
*
|
|
467
519
|
* @example
|
|
468
520
|
* ```ts
|
|
469
|
-
*
|
|
521
|
+
* type Result = GroupByResult<Order, 'status', { count: Accumulator<number> }>
|
|
522
|
+
* // ^? { _id: string; count: number }
|
|
470
523
|
* ```
|
|
471
524
|
*/
|
|
472
|
-
type
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
/** The number of documents per page. */
|
|
476
|
-
perPage: number;
|
|
477
|
-
};
|
|
525
|
+
type GroupByResult<T, K extends keyof T, TAccum extends Record<string, Accumulator>> = Prettify<{
|
|
526
|
+
_id: T[K];
|
|
527
|
+
} & InferAccumulators<TAccum>>;
|
|
478
528
|
/**
|
|
479
|
-
*
|
|
529
|
+
* Output shape of a compound (multi-field) `groupBy` stage.
|
|
530
|
+
*
|
|
531
|
+
* The `_id` field is an object with one property per grouped field,
|
|
532
|
+
* and the rest of the shape comes from the inferred accumulator types.
|
|
480
533
|
*
|
|
481
534
|
* @example
|
|
482
535
|
* ```ts
|
|
483
|
-
*
|
|
484
|
-
*
|
|
536
|
+
* type Result = GroupByCompoundResult<Order, 'status' | 'region', { count: Accumulator<number> }>
|
|
537
|
+
* // ^? { _id: Pick<Order, 'status' | 'region'>; count: number }
|
|
485
538
|
* ```
|
|
486
539
|
*/
|
|
487
|
-
type
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
/** Opaque cursor string from a previous `startCursor` or `endCursor`. */
|
|
491
|
-
cursor?: string | null;
|
|
492
|
-
};
|
|
540
|
+
type GroupByCompoundResult<T, K extends keyof T, TAccum extends Record<string, Accumulator>> = Prettify<{
|
|
541
|
+
_id: Prettify<Pick<T, K>>;
|
|
542
|
+
} & InferAccumulators<TAccum>>;
|
|
493
543
|
/**
|
|
494
|
-
*
|
|
544
|
+
* Output shape after unwinding an array field.
|
|
545
|
+
*
|
|
546
|
+
* The unwound field's type changes from `Array<E>` to `E`; all other
|
|
547
|
+
* fields are preserved as-is.
|
|
495
548
|
*
|
|
496
549
|
* @example
|
|
497
550
|
* ```ts
|
|
498
|
-
*
|
|
499
|
-
*
|
|
551
|
+
* type Order = { items: string[]; total: number }
|
|
552
|
+
* type Unwound = UnwindResult<Order, 'items'>
|
|
553
|
+
* // ^? { items: string; total: number }
|
|
500
554
|
* ```
|
|
501
555
|
*/
|
|
502
|
-
type
|
|
503
|
-
|
|
504
|
-
docs: TDoc[];
|
|
505
|
-
/** Total number of matching documents. */
|
|
506
|
-
total: number;
|
|
507
|
-
/** Current page number (1-indexed). */
|
|
508
|
-
page: number;
|
|
509
|
-
/** Number of documents per page. */
|
|
510
|
-
perPage: number;
|
|
511
|
-
/** Total number of pages (`Math.ceil(total / perPage)`). */
|
|
512
|
-
totalPages: number;
|
|
513
|
-
/** Whether there is a next page. */
|
|
514
|
-
hasNext: boolean;
|
|
515
|
-
/** Whether there is a previous page. */
|
|
516
|
-
hasPrev: boolean;
|
|
556
|
+
type UnwindResult<T, K extends keyof T> = {
|
|
557
|
+
[P in keyof T]: P extends K ? (T[P] extends ReadonlyArray<infer E> ? E : T[P]) : T[P];
|
|
517
558
|
};
|
|
518
559
|
/**
|
|
519
|
-
*
|
|
560
|
+
* Narrows document field types based on a filter expression.
|
|
561
|
+
*
|
|
562
|
+
* Inspects each field in the filter for value-constraining operators
|
|
563
|
+
* (`$eq`, `$in`, `$ne`, `$nin`, or direct equality) and narrows the
|
|
564
|
+
* corresponding field type in the output. Fields not mentioned in the
|
|
565
|
+
* filter, or filtered with unsupported operators, keep their original type.
|
|
566
|
+
*
|
|
567
|
+
* Used internally by {@link AggregatePipeline.match} for automatic
|
|
568
|
+
* type narrowing (Tier 2). Requires `as const` on array literals
|
|
569
|
+
* for `$in` / `$nin` inference.
|
|
520
570
|
*
|
|
521
571
|
* @example
|
|
522
572
|
* ```ts
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
* }
|
|
573
|
+
* type Narrowed = NarrowFromFilter<Employee, { role: 'engineer' }>
|
|
574
|
+
* // ^? { ..., role: 'engineer', ... }
|
|
575
|
+
*
|
|
576
|
+
* type Subset = NarrowFromFilter<Employee, { role: { $in: readonly ['engineer', 'designer'] } }>
|
|
577
|
+
* // ^? { ..., role: 'engineer' | 'designer', ... }
|
|
578
|
+
*
|
|
579
|
+
* type Excluded = NarrowFromFilter<Employee, { role: { $ne: 'intern' } }>
|
|
580
|
+
* // ^? { ..., role: 'engineer' | 'manager' | 'designer', ... }
|
|
527
581
|
* ```
|
|
528
582
|
*/
|
|
529
|
-
type
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
endCursor: string | null;
|
|
583
|
+
type NarrowFromFilter<T, F> = {
|
|
584
|
+
[K in keyof T]: K extends keyof F ? F[K] extends T[K] ? F[K] : F[K] extends {
|
|
585
|
+
$eq: infer V extends T[K];
|
|
586
|
+
} ? V : F[K] extends {
|
|
587
|
+
$in: ReadonlyArray<infer V extends T[K]>;
|
|
588
|
+
} ? V : F[K] extends {
|
|
589
|
+
$ne: infer V extends T[K];
|
|
590
|
+
} ? Exclude<T[K], V> : F[K] extends {
|
|
591
|
+
$nin: ReadonlyArray<infer V extends T[K]>;
|
|
592
|
+
} ? Exclude<T[K], V> : T[K] : T[K];
|
|
540
593
|
};
|
|
541
|
-
|
|
542
594
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
* Constrains sort keys to top-level fields of `T` with direction `1` (ascending)
|
|
546
|
-
* or `-1` (descending). Dot-path sorts deferred to v1.0.
|
|
595
|
+
* Extract field keys from a collection definition whose schema fields
|
|
596
|
+
* carry `RefMarker` (i.e. fields that have `.ref()` metadata).
|
|
547
597
|
*
|
|
548
598
|
* @example
|
|
549
599
|
* ```ts
|
|
550
|
-
*
|
|
600
|
+
* type EmpRefs = RefFields<typeof Employees> // → 'departmentId'
|
|
551
601
|
* ```
|
|
552
602
|
*/
|
|
553
|
-
type
|
|
603
|
+
type RefFields<TDef extends AnyCollection> = {
|
|
604
|
+
[K in keyof TDef['shape'] & string]: TDef['shape'][K] extends RefMarker ? K : never;
|
|
605
|
+
}[keyof TDef['shape'] & string];
|
|
554
606
|
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
* Provides chainable query modifiers (`sort`, `skip`, `limit`) that return
|
|
558
|
-
* `this` for fluent chaining, and terminal methods (`toArray`,
|
|
559
|
-
* `[Symbol.asyncIterator]`) that validate each document against the
|
|
560
|
-
* collection's Zod schema before returning.
|
|
561
|
-
*
|
|
562
|
-
* Created by {@link find} — do not construct directly.
|
|
563
|
-
*
|
|
564
|
-
* @typeParam TDef - The collection definition type, used to infer the document type.
|
|
607
|
+
* Given a collection definition and a ref-bearing field key, extract
|
|
608
|
+
* the target collection type from the `RefMarker<TCollection>` phantom.
|
|
565
609
|
*
|
|
566
610
|
* @example
|
|
567
611
|
* ```ts
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
* .limit(10)
|
|
571
|
-
* .toArray()
|
|
612
|
+
* type Target = ExtractRefCollection<typeof Employees, 'departmentId'>
|
|
613
|
+
* // → typeof Departments
|
|
572
614
|
* ```
|
|
573
615
|
*/
|
|
574
|
-
|
|
575
|
-
/** @internal */
|
|
576
|
-
private cursor;
|
|
577
|
-
/** @internal */
|
|
578
|
-
private schema;
|
|
579
|
-
/** @internal */
|
|
580
|
-
private collectionName;
|
|
581
|
-
/** @internal */
|
|
582
|
-
private mode;
|
|
583
|
-
/** @internal */
|
|
584
|
-
private readonly nativeCollection;
|
|
585
|
-
/** @internal */
|
|
586
|
-
private readonly filter;
|
|
587
|
-
/** @internal */
|
|
588
|
-
private sortSpec;
|
|
589
|
-
/** @internal */
|
|
590
|
-
constructor(cursor: FindCursor<InferDocument<TDef>>, definition: TDef, mode: ValidationMode | false, nativeCollection: Collection<InferDocument<TDef>>, filter: any);
|
|
591
|
-
/**
|
|
592
|
-
* Set the sort order for the query.
|
|
593
|
-
*
|
|
594
|
-
* Only top-level document fields are accepted as sort keys.
|
|
595
|
-
* Values must be `1` (ascending) or `-1` (descending).
|
|
596
|
-
*
|
|
597
|
-
* @param spec - Sort specification mapping field names to sort direction.
|
|
598
|
-
* @returns `this` for chaining.
|
|
599
|
-
*
|
|
600
|
-
* @example
|
|
601
|
-
* ```ts
|
|
602
|
-
* find(users, {}).sort({ name: 1, age: -1 }).toArray()
|
|
603
|
-
* ```
|
|
604
|
-
*/
|
|
605
|
-
sort(spec: TypedSort<InferDocument<TDef>>): this;
|
|
606
|
-
/**
|
|
607
|
-
* Skip the first `n` documents in the result set.
|
|
608
|
-
*
|
|
609
|
-
* @param n - Number of documents to skip.
|
|
610
|
-
* @returns `this` for chaining.
|
|
611
|
-
*
|
|
612
|
-
* @example
|
|
613
|
-
* ```ts
|
|
614
|
-
* find(users, {}).skip(10).limit(10).toArray() // page 2
|
|
615
|
-
* ```
|
|
616
|
-
*/
|
|
617
|
-
skip(n: number): this;
|
|
618
|
-
/**
|
|
619
|
-
* Limit the number of documents returned.
|
|
620
|
-
*
|
|
621
|
-
* @param n - Maximum number of documents to return.
|
|
622
|
-
* @returns `this` for chaining.
|
|
623
|
-
*
|
|
624
|
-
* @example
|
|
625
|
-
* ```ts
|
|
626
|
-
* find(users, {}).limit(10).toArray() // at most 10 docs
|
|
627
|
-
* ```
|
|
628
|
-
*/
|
|
629
|
-
limit(n: number): this;
|
|
630
|
-
/**
|
|
631
|
-
* Execute the query with offset-based pagination, returning a page of documents
|
|
632
|
-
* with total count and navigation metadata.
|
|
633
|
-
*
|
|
634
|
-
* Runs `countDocuments` and `find` in parallel for performance. Ignores any
|
|
635
|
-
* `.skip()` or `.limit()` already set on the cursor — issues a fresh query.
|
|
636
|
-
*
|
|
637
|
-
* @param opts - Offset pagination options: `page` (1-indexed) and `perPage`.
|
|
638
|
-
* @returns A page with `docs`, `total`, `totalPages`, `hasNext`, `hasPrev`.
|
|
639
|
-
* @throws {ZodmonValidationError} When a document fails schema validation.
|
|
640
|
-
*
|
|
641
|
-
* @example
|
|
642
|
-
* ```ts
|
|
643
|
-
* const page = await users.find({ role: 'admin' })
|
|
644
|
-
* .sort({ createdAt: -1 })
|
|
645
|
-
* .paginate({ page: 2, perPage: 10 })
|
|
646
|
-
* console.log(page.total, page.totalPages, page.hasNext)
|
|
647
|
-
* ```
|
|
648
|
-
*/
|
|
649
|
-
paginate(opts: OffsetPaginateOptions): Promise<OffsetPage<InferDocument<TDef>>>;
|
|
650
|
-
/**
|
|
651
|
-
* Execute the query with cursor-based pagination, returning a page of documents
|
|
652
|
-
* with opaque cursors for forward/backward navigation.
|
|
653
|
-
*
|
|
654
|
-
* Uses the `limit + 1` trick to determine `hasNext`/`hasPrev` without extra queries.
|
|
655
|
-
* Direction is encoded in the cursor — pass `endCursor` to go forward, `startCursor`
|
|
656
|
-
* to go backward.
|
|
657
|
-
*
|
|
658
|
-
* @param opts - Cursor pagination options: `limit` and optional `cursor`.
|
|
659
|
-
* @returns A page with `docs`, `hasNext`, `hasPrev`, `startCursor`, `endCursor`.
|
|
660
|
-
* @throws {ZodmonValidationError} When a document fails schema validation.
|
|
661
|
-
* @throws {Error} When the cursor string is malformed.
|
|
662
|
-
*
|
|
663
|
-
* @example
|
|
664
|
-
* ```ts
|
|
665
|
-
* const first = await users.find({}).sort({ name: 1 }).paginate({ limit: 10 })
|
|
666
|
-
* const next = await users.find({}).sort({ name: 1 })
|
|
667
|
-
* .paginate({ cursor: first.endCursor, limit: 10 })
|
|
668
|
-
* ```
|
|
669
|
-
*/
|
|
670
|
-
paginate(opts: CursorPaginateOptions): Promise<CursorPage<InferDocument<TDef>>>;
|
|
671
|
-
/** @internal Offset pagination implementation. */
|
|
672
|
-
private offsetPaginate;
|
|
673
|
-
/** @internal Cursor pagination implementation. */
|
|
674
|
-
private cursorPaginate;
|
|
675
|
-
/**
|
|
676
|
-
* Execute the query and return all matching documents as an array.
|
|
677
|
-
*
|
|
678
|
-
* Each document is validated against the collection's Zod schema
|
|
679
|
-
* according to the resolved validation mode.
|
|
680
|
-
*
|
|
681
|
-
* @returns Array of validated documents.
|
|
682
|
-
* @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.
|
|
683
|
-
*
|
|
684
|
-
* @example
|
|
685
|
-
* ```ts
|
|
686
|
-
* const admins = await find(users, { role: 'admin' }).toArray()
|
|
687
|
-
* ```
|
|
688
|
-
*/
|
|
689
|
-
toArray(): Promise<InferDocument<TDef>[]>;
|
|
690
|
-
/**
|
|
691
|
-
* Async iterator for streaming documents one at a time.
|
|
692
|
-
*
|
|
693
|
-
* Each yielded document is validated against the collection's Zod schema.
|
|
694
|
-
* Memory-efficient for large result sets.
|
|
695
|
-
*
|
|
696
|
-
* @yields Validated documents one at a time.
|
|
697
|
-
* @throws {ZodmonValidationError} When a document fails schema validation.
|
|
698
|
-
*
|
|
699
|
-
* @example
|
|
700
|
-
* ```ts
|
|
701
|
-
* for await (const user of find(users, {})) {
|
|
702
|
-
* console.log(user.name)
|
|
703
|
-
* }
|
|
704
|
-
* ```
|
|
705
|
-
*/
|
|
706
|
-
[Symbol.asyncIterator](): AsyncGenerator<InferDocument<TDef>>;
|
|
707
|
-
/** @internal Validate a single raw document against the schema. */
|
|
708
|
-
private validateDoc;
|
|
709
|
-
}
|
|
710
|
-
|
|
616
|
+
type ExtractRefCollection<TDef extends AnyCollection, K extends RefFields<TDef>> = TDef['shape'][K] extends RefMarker<infer TCol> ? TCol : never;
|
|
711
617
|
/**
|
|
712
|
-
*
|
|
618
|
+
* Extract the collection name string literal from a collection definition.
|
|
619
|
+
*
|
|
620
|
+
* @example
|
|
621
|
+
* ```ts
|
|
622
|
+
* CollectionName<typeof Departments> // → 'departments'
|
|
623
|
+
* ```
|
|
713
624
|
*/
|
|
714
|
-
type
|
|
715
|
-
/** MongoDB projection — include (`1`) or exclude (`0`) fields. Typed projections deferred to v1.0. */
|
|
716
|
-
project?: Record<string, 0 | 1>;
|
|
717
|
-
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
718
|
-
validate?: ValidationMode | false;
|
|
719
|
-
};
|
|
625
|
+
type CollectionName<TDef extends AnyCollection> = TDef['name'];
|
|
720
626
|
/**
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
* Queries MongoDB, then validates the fetched document against the collection's
|
|
724
|
-
* Zod schema. Validation mode is resolved from the per-query option, falling
|
|
725
|
-
* back to the collection-level default (which defaults to `'strict'`).
|
|
627
|
+
* Branded wrapper for computed expressions used inside `addFields`.
|
|
726
628
|
*
|
|
727
|
-
*
|
|
728
|
-
*
|
|
729
|
-
* @param options - Optional projection and validation overrides.
|
|
730
|
-
* @returns The matched document, or `null` if no document matches.
|
|
731
|
-
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
629
|
+
* Carries the MongoDB expression at runtime and the inferred
|
|
630
|
+
* result type `T` at the type level.
|
|
732
631
|
*
|
|
733
632
|
* @example
|
|
734
633
|
* ```ts
|
|
735
|
-
* const
|
|
736
|
-
*
|
|
634
|
+
* const yearExpr: Expression<number> = {
|
|
635
|
+
* __expr: true,
|
|
636
|
+
* value: { $year: '$hiredAt' },
|
|
637
|
+
* }
|
|
737
638
|
* ```
|
|
738
639
|
*/
|
|
739
|
-
|
|
640
|
+
type Expression<T = unknown> = {
|
|
641
|
+
readonly __expr: true;
|
|
642
|
+
readonly value: Document;
|
|
643
|
+
/** @internal Phantom field — carries the result type at the type level. */
|
|
644
|
+
readonly _type?: T;
|
|
645
|
+
};
|
|
740
646
|
/**
|
|
741
|
-
*
|
|
647
|
+
* Unwrap an `Expression<T>` to its result type `T`.
|
|
648
|
+
* Non-Expression values resolve to `unknown`.
|
|
742
649
|
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
650
|
+
* @example
|
|
651
|
+
* ```ts
|
|
652
|
+
* type N = InferExpression<Expression<number>> // → number
|
|
653
|
+
* type U = InferExpression<{ $year: string }> // → unknown
|
|
654
|
+
* ```
|
|
655
|
+
*/
|
|
656
|
+
type InferExpression<T> = T extends Expression<infer R> ? R : unknown;
|
|
657
|
+
/**
|
|
658
|
+
* Map an added-fields spec to its resolved output shape.
|
|
745
659
|
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
* @param options - Optional projection and validation overrides.
|
|
749
|
-
* @returns The matched document (never null).
|
|
750
|
-
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
751
|
-
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
660
|
+
* `Expression<V>` values unwrap to `V`. Raw expression objects resolve
|
|
661
|
+
* to `unknown`, signaling that type information is lost.
|
|
752
662
|
*
|
|
753
663
|
* @example
|
|
754
664
|
* ```ts
|
|
755
|
-
*
|
|
756
|
-
*
|
|
665
|
+
* type Spec = { year: Expression<number>; raw: { $year: string } }
|
|
666
|
+
* type Result = InferAddedFields<Spec>
|
|
667
|
+
* // ^? { year: number; raw: unknown }
|
|
757
668
|
* ```
|
|
758
669
|
*/
|
|
759
|
-
|
|
670
|
+
type InferAddedFields<T extends Record<string, unknown>> = {
|
|
671
|
+
[K in keyof T]: InferExpression<T[K]>;
|
|
672
|
+
};
|
|
760
673
|
/**
|
|
761
|
-
*
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
674
|
+
* Typed expression factory passed to the `addFields` callback.
|
|
675
|
+
*
|
|
676
|
+
* Each method constrains field names to `keyof T & string`, providing
|
|
677
|
+
* IDE autocomplete and compile-time validation. Return types resolve
|
|
678
|
+
* to `Expression<R>` where `R` is the MongoDB operator's output type.
|
|
679
|
+
*
|
|
680
|
+
* @typeParam T - The current pipeline output document type.
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```ts
|
|
684
|
+
* employees.aggregate()
|
|
685
|
+
* .addFields(expr => ({
|
|
686
|
+
* hireYear: expr.year('hiredAt'), // Expression<number>
|
|
687
|
+
* fullName: expr.concat('name', ' '), // Expression<string>
|
|
688
|
+
* isHighPay: expr.gte('salary', 100_000), // Expression<boolean>
|
|
689
|
+
* }))
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
type ExpressionBuilder<T> = {
|
|
693
|
+
/** Add a numeric field and a value. Returns `Expression<number>`. */
|
|
694
|
+
add<K extends keyof T & string>(field: K, value: number | (keyof T & string)): Expression<number>;
|
|
695
|
+
/** Subtract a value from a numeric field. Returns `Expression<number>`. */
|
|
696
|
+
subtract<K extends keyof T & string>(field: K, value: number | (keyof T & string)): Expression<number>;
|
|
697
|
+
/** Multiply a numeric field by a value. Returns `Expression<number>`. */
|
|
698
|
+
multiply<K extends keyof T & string>(field: K, value: number | (keyof T & string)): Expression<number>;
|
|
699
|
+
/** Divide a numeric field by a value. Returns `Expression<number>`. */
|
|
700
|
+
divide<K extends keyof T & string>(field: K, value: number | (keyof T & string)): Expression<number>;
|
|
701
|
+
/** Modulo of a numeric field. Returns `Expression<number>`. */
|
|
702
|
+
mod<K extends keyof T & string>(field: K, value: number | (keyof T & string)): Expression<number>;
|
|
703
|
+
/** Absolute value of a numeric field. Returns `Expression<number>`. */
|
|
704
|
+
abs<K extends keyof T & string>(field: K): Expression<number>;
|
|
705
|
+
/** Ceiling of a numeric field. Returns `Expression<number>`. */
|
|
706
|
+
ceil<K extends keyof T & string>(field: K): Expression<number>;
|
|
707
|
+
/** Floor of a numeric field. Returns `Expression<number>`. */
|
|
708
|
+
floor<K extends keyof T & string>(field: K): Expression<number>;
|
|
709
|
+
/** Round a numeric field to N decimal places. Returns `Expression<number>`. */
|
|
710
|
+
round<K extends keyof T & string>(field: K, place?: number): Expression<number>;
|
|
711
|
+
/** Concatenate field values and literal strings. Returns `Expression<string>`. */
|
|
712
|
+
concat(...parts: Array<(keyof T & string) | string>): Expression<string>;
|
|
713
|
+
/** Convert a string field to lowercase. Returns `Expression<string>`. */
|
|
714
|
+
toLower<K extends keyof T & string>(field: K): Expression<string>;
|
|
715
|
+
/** Convert a string field to uppercase. Returns `Expression<string>`. */
|
|
716
|
+
toUpper<K extends keyof T & string>(field: K): Expression<string>;
|
|
717
|
+
/** Trim whitespace from a string field. Returns `Expression<string>`. */
|
|
718
|
+
trim<K extends keyof T & string>(field: K): Expression<string>;
|
|
719
|
+
/** Extract a substring from a string field. Returns `Expression<string>`. */
|
|
720
|
+
substr<K extends keyof T & string>(field: K, start: number, length: number): Expression<string>;
|
|
721
|
+
/** Test equality of a field against a value. Returns `Expression<boolean>`. */
|
|
722
|
+
eq<K extends keyof T & string>(field: K, value: T[K]): Expression<boolean>;
|
|
723
|
+
/** Test if a field is greater than a value. Returns `Expression<boolean>`. */
|
|
724
|
+
gt<K extends keyof T & string>(field: K, value: T[K]): Expression<boolean>;
|
|
725
|
+
/** Test if a field is greater than or equal to a value. Returns `Expression<boolean>`. */
|
|
726
|
+
gte<K extends keyof T & string>(field: K, value: T[K]): Expression<boolean>;
|
|
727
|
+
/** Test if a field is less than a value. Returns `Expression<boolean>`. */
|
|
728
|
+
lt<K extends keyof T & string>(field: K, value: T[K]): Expression<boolean>;
|
|
729
|
+
/** Test if a field is less than or equal to a value. Returns `Expression<boolean>`. */
|
|
730
|
+
lte<K extends keyof T & string>(field: K, value: T[K]): Expression<boolean>;
|
|
731
|
+
/** Test if a field is not equal to a value. Returns `Expression<boolean>`. */
|
|
732
|
+
ne<K extends keyof T & string>(field: K, value: T[K]): Expression<boolean>;
|
|
733
|
+
/** Extract year from a date field. Returns `Expression<number>`. */
|
|
734
|
+
year<K extends keyof T & string>(field: K): Expression<number>;
|
|
735
|
+
/** Extract month (1-12) from a date field. Returns `Expression<number>`. */
|
|
736
|
+
month<K extends keyof T & string>(field: K): Expression<number>;
|
|
737
|
+
/** Extract day of month (1-31) from a date field. Returns `Expression<number>`. */
|
|
738
|
+
dayOfMonth<K extends keyof T & string>(field: K): Expression<number>;
|
|
739
|
+
/** Count elements in an array field. Returns `Expression<number>`. */
|
|
740
|
+
size<K extends keyof T & string>(field: K): Expression<number>;
|
|
741
|
+
/** Conditional expression. Returns `Expression<TThen | TElse>`. */
|
|
742
|
+
cond<TThen, TElse>(condition: Expression<boolean>, thenValue: TThen, elseValue: TElse): Expression<TThen | TElse>;
|
|
743
|
+
/** Replace null/missing field with a default. Returns `Expression<NonNullable<T[K]> | TDefault>`. */
|
|
744
|
+
ifNull<K extends keyof T & string, TDefault>(field: K, fallback: TDefault): Expression<NonNullable<T[K]> | TDefault>;
|
|
766
745
|
};
|
|
746
|
+
|
|
767
747
|
/**
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
* The cursor is lazy — no query is executed until a terminal method
|
|
771
|
-
* (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
|
|
772
|
-
* to shape the query before executing.
|
|
748
|
+
* Counts the number of documents in each group.
|
|
773
749
|
*
|
|
774
|
-
*
|
|
775
|
-
* a terminal method consumes it.
|
|
750
|
+
* Equivalent to `{ $sum: 1 }` in a MongoDB `$group` stage.
|
|
776
751
|
*
|
|
777
|
-
* @
|
|
778
|
-
* @param filter - Type-safe filter to match documents.
|
|
779
|
-
* @param options - Optional validation overrides.
|
|
780
|
-
* @returns A typed cursor for chaining query modifiers.
|
|
752
|
+
* @returns An `Accumulator<number>` that counts documents.
|
|
781
753
|
*
|
|
782
754
|
* @example
|
|
783
755
|
* ```ts
|
|
784
|
-
* const
|
|
785
|
-
*
|
|
786
|
-
* .limit(10)
|
|
787
|
-
* .toArray()
|
|
756
|
+
* const pipeline = orders.aggregate()
|
|
757
|
+
* .groupBy('status', { count: $count() })
|
|
788
758
|
* ```
|
|
759
|
+
*/
|
|
760
|
+
declare const $count: () => Accumulator<number>;
|
|
761
|
+
/**
|
|
762
|
+
* Sums numeric values across documents in each group.
|
|
763
|
+
*
|
|
764
|
+
* Accepts either a `$`-prefixed field reference or a literal number.
|
|
765
|
+
*
|
|
766
|
+
* @param field - A `$field` reference to a numeric field, or a literal number.
|
|
767
|
+
* @returns An `Accumulator<number>`.
|
|
789
768
|
*
|
|
790
769
|
* @example
|
|
791
770
|
* ```ts
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
*
|
|
771
|
+
* const pipeline = orders.aggregate()
|
|
772
|
+
* .groupBy('status', {
|
|
773
|
+
* total: $sum('$amount'),
|
|
774
|
+
* fixed: $sum(1),
|
|
775
|
+
* })
|
|
795
776
|
* ```
|
|
796
777
|
*/
|
|
797
|
-
declare
|
|
798
|
-
|
|
778
|
+
declare const $sum: (field: `$${string}` | number) => Accumulator<number>;
|
|
799
779
|
/**
|
|
800
|
-
*
|
|
780
|
+
* Computes the average of numeric values across documents in each group.
|
|
781
|
+
*
|
|
782
|
+
* @param field - A `$field` reference to a numeric field.
|
|
783
|
+
* @returns An `Accumulator<number>`.
|
|
801
784
|
*
|
|
802
785
|
* @example
|
|
803
786
|
* ```ts
|
|
804
|
-
*
|
|
805
|
-
*
|
|
787
|
+
* const pipeline = orders.aggregate()
|
|
788
|
+
* .groupBy('category', { avgPrice: $avg('$price') })
|
|
806
789
|
* ```
|
|
807
790
|
*/
|
|
808
|
-
|
|
791
|
+
declare const $avg: (field: `$${string}`) => Accumulator<number>;
|
|
809
792
|
/**
|
|
810
|
-
*
|
|
793
|
+
* Returns the minimum value across documents in each group.
|
|
811
794
|
*
|
|
812
|
-
*
|
|
813
|
-
* for
|
|
795
|
+
* For full type safety, prefer the callback builder (`acc.min('price')`).
|
|
796
|
+
* The standalone form accepts an explicit result type `R` for manual typing.
|
|
797
|
+
*
|
|
798
|
+
* @typeParam R - The expected result type (defaults to `unknown`).
|
|
799
|
+
* @param field - A `$field` reference.
|
|
800
|
+
* @returns An `Accumulator<R>`.
|
|
814
801
|
*
|
|
815
802
|
* @example
|
|
816
803
|
* ```ts
|
|
817
|
-
*
|
|
804
|
+
* // Tier 2 — explicit result type
|
|
805
|
+
* const pipeline = orders.aggregate()
|
|
806
|
+
* .groupBy('category', { cheapest: $min<number>('$price') })
|
|
807
|
+
*
|
|
808
|
+
* // Tier 1 — callback builder (recommended)
|
|
809
|
+
* orders.aggregate()
|
|
810
|
+
* .groupBy('category', acc => ({ cheapest: acc.min('price') }))
|
|
818
811
|
* ```
|
|
819
812
|
*/
|
|
820
|
-
|
|
821
|
-
[K in keyof T]?: T[K];
|
|
822
|
-
} & {
|
|
823
|
-
[P in DotPaths<T>]?: DotPathType<T, P>;
|
|
824
|
-
};
|
|
813
|
+
declare const $min: <R = unknown>(field: `$${string}`) => Accumulator<R>;
|
|
825
814
|
/**
|
|
826
|
-
*
|
|
815
|
+
* Returns the maximum value across documents in each group.
|
|
827
816
|
*
|
|
828
|
-
*
|
|
817
|
+
* For full type safety, prefer the callback builder (`acc.max('price')`).
|
|
818
|
+
* The standalone form accepts an explicit result type `R` for manual typing.
|
|
819
|
+
*
|
|
820
|
+
* @typeParam R - The expected result type (defaults to `unknown`).
|
|
821
|
+
* @param field - A `$field` reference.
|
|
822
|
+
* @returns An `Accumulator<R>`.
|
|
829
823
|
*
|
|
830
824
|
* @example
|
|
831
825
|
* ```ts
|
|
832
|
-
*
|
|
826
|
+
* // Tier 2 — explicit result type
|
|
827
|
+
* const pipeline = orders.aggregate()
|
|
828
|
+
* .groupBy('category', { priciest: $max<number>('$price') })
|
|
829
|
+
*
|
|
830
|
+
* // Tier 1 — callback builder (recommended)
|
|
831
|
+
* orders.aggregate()
|
|
832
|
+
* .groupBy('category', acc => ({ priciest: acc.max('price') }))
|
|
833
833
|
* ```
|
|
834
834
|
*/
|
|
835
|
-
|
|
836
|
-
[K in keyof T as NonNullable<T[K]> extends number ? K : never]?: number;
|
|
837
|
-
} & {
|
|
838
|
-
[P in DotPaths<T> as DotPathType<T, P> extends number ? P : never]?: number;
|
|
839
|
-
};
|
|
835
|
+
declare const $max: <R = unknown>(field: `$${string}`) => Accumulator<R>;
|
|
840
836
|
/**
|
|
841
|
-
*
|
|
837
|
+
* Returns the first value in each group according to the document order.
|
|
842
838
|
*
|
|
843
|
-
*
|
|
839
|
+
* For full type safety, prefer the callback builder (`acc.first('name')`).
|
|
840
|
+
* The standalone form accepts an explicit result type `R` for manual typing.
|
|
841
|
+
*
|
|
842
|
+
* @typeParam R - The expected result type (defaults to `unknown`).
|
|
843
|
+
* @param field - A `$field` reference.
|
|
844
|
+
* @returns An `Accumulator<R>`.
|
|
844
845
|
*
|
|
845
846
|
* @example
|
|
846
847
|
* ```ts
|
|
847
|
-
*
|
|
848
|
+
* // Tier 2 — explicit result type
|
|
849
|
+
* const pipeline = orders.aggregate()
|
|
850
|
+
* .sort({ createdAt: 1 })
|
|
851
|
+
* .groupBy('status', { earliest: $first<Date>('$createdAt') })
|
|
852
|
+
*
|
|
853
|
+
* // Tier 1 — callback builder (recommended)
|
|
854
|
+
* orders.aggregate()
|
|
855
|
+
* .sort({ createdAt: 1 })
|
|
856
|
+
* .groupBy('status', acc => ({ earliest: acc.first('createdAt') }))
|
|
848
857
|
* ```
|
|
849
858
|
*/
|
|
850
|
-
|
|
851
|
-
/** Array of elements to push. */
|
|
852
|
-
$each: E[];
|
|
853
|
-
/** Position at which to insert elements. */
|
|
854
|
-
$position?: number;
|
|
855
|
-
/** Maximum array length after push. */
|
|
856
|
-
$slice?: number;
|
|
857
|
-
/** Sort order applied after push. */
|
|
858
|
-
$sort?: 1 | -1 | Record<string, 1 | -1>;
|
|
859
|
-
};
|
|
859
|
+
declare const $first: <R = unknown>(field: `$${string}`) => Accumulator<R>;
|
|
860
860
|
/**
|
|
861
|
-
*
|
|
861
|
+
* Returns the last value in each group according to the document order.
|
|
862
862
|
*
|
|
863
|
-
*
|
|
863
|
+
* For full type safety, prefer the callback builder (`acc.last('name')`).
|
|
864
|
+
* The standalone form accepts an explicit result type `R` for manual typing.
|
|
865
|
+
*
|
|
866
|
+
* @typeParam R - The expected result type (defaults to `unknown`).
|
|
867
|
+
* @param field - A `$field` reference.
|
|
868
|
+
* @returns An `Accumulator<R>`.
|
|
864
869
|
*
|
|
865
870
|
* @example
|
|
866
871
|
* ```ts
|
|
867
|
-
*
|
|
868
|
-
* const
|
|
872
|
+
* // Tier 2 — explicit result type
|
|
873
|
+
* const pipeline = orders.aggregate()
|
|
874
|
+
* .sort({ createdAt: -1 })
|
|
875
|
+
* .groupBy('status', { latest: $last<Date>('$createdAt') })
|
|
876
|
+
*
|
|
877
|
+
* // Tier 1 — callback builder (recommended)
|
|
878
|
+
* orders.aggregate()
|
|
879
|
+
* .sort({ createdAt: -1 })
|
|
880
|
+
* .groupBy('status', acc => ({ latest: acc.last('createdAt') }))
|
|
869
881
|
* ```
|
|
870
882
|
*/
|
|
871
|
-
|
|
872
|
-
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: ArrayElement<NonNullable<T[K]>> | PushModifiers<ArrayElement<NonNullable<T[K]>>>;
|
|
873
|
-
};
|
|
883
|
+
declare const $last: <R = unknown>(field: `$${string}`) => Accumulator<R>;
|
|
874
884
|
/**
|
|
875
|
-
*
|
|
885
|
+
* Pushes values into an array for each group.
|
|
876
886
|
*
|
|
877
|
-
*
|
|
878
|
-
* For
|
|
887
|
+
* May contain duplicates. Use `$addToSet` for unique values.
|
|
888
|
+
* For full type safety, prefer the callback builder (`acc.push('name')`).
|
|
889
|
+
*
|
|
890
|
+
* @typeParam R - The element type (defaults to `unknown`).
|
|
891
|
+
* @param field - A `$field` reference.
|
|
892
|
+
* @returns An `Accumulator<R[]>`.
|
|
879
893
|
*
|
|
880
894
|
* @example
|
|
881
895
|
* ```ts
|
|
882
|
-
*
|
|
883
|
-
* const
|
|
884
|
-
*
|
|
896
|
+
* // Tier 2 — explicit element type
|
|
897
|
+
* const pipeline = orders.aggregate()
|
|
898
|
+
* .groupBy('status', { items: $push<string>('$name') })
|
|
899
|
+
*
|
|
900
|
+
* // Tier 1 — callback builder (recommended)
|
|
901
|
+
* orders.aggregate()
|
|
902
|
+
* .groupBy('status', acc => ({ items: acc.push('name') }))
|
|
885
903
|
* ```
|
|
886
904
|
*/
|
|
887
|
-
|
|
888
|
-
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: ArrayElement<NonNullable<T[K]>> | ComparisonOperators<ArrayElement<NonNullable<T[K]>>> | (ArrayElement<NonNullable<T[K]>> extends Record<string, unknown> ? TypedFilter<ArrayElement<NonNullable<T[K]>>> : unknown);
|
|
889
|
-
};
|
|
905
|
+
declare const $push: <R = unknown>(field: `$${string}`) => Accumulator<R[]>;
|
|
890
906
|
/**
|
|
891
|
-
*
|
|
907
|
+
* Collects unique values into an array for each group (set semantics).
|
|
908
|
+
*
|
|
909
|
+
* Like `$push` but deduplicates.
|
|
910
|
+
* For full type safety, prefer the callback builder (`acc.addToSet('tag')`).
|
|
911
|
+
*
|
|
912
|
+
* @typeParam R - The element type (defaults to `unknown`).
|
|
913
|
+
* @param field - A `$field` reference.
|
|
914
|
+
* @returns An `Accumulator<R[]>`.
|
|
892
915
|
*
|
|
893
916
|
* @example
|
|
894
917
|
* ```ts
|
|
895
|
-
*
|
|
918
|
+
* // Tier 2 — explicit element type
|
|
919
|
+
* const pipeline = orders.aggregate()
|
|
920
|
+
* .groupBy('category', { uniqueTags: $addToSet<string>('$tag') })
|
|
921
|
+
*
|
|
922
|
+
* // Tier 1 — callback builder (recommended)
|
|
923
|
+
* orders.aggregate()
|
|
924
|
+
* .groupBy('category', acc => ({ uniqueTags: acc.addToSet('tag') }))
|
|
896
925
|
* ```
|
|
897
926
|
*/
|
|
898
|
-
|
|
899
|
-
$each: E[];
|
|
900
|
-
};
|
|
927
|
+
declare const $addToSet: <R = unknown>(field: `$${string}`) => Accumulator<R[]>;
|
|
901
928
|
/**
|
|
902
|
-
*
|
|
929
|
+
* Create a typed accumulator builder for use inside `groupBy` callbacks.
|
|
903
930
|
*
|
|
904
|
-
*
|
|
931
|
+
* The builder adds the `$` prefix to field names automatically and returns
|
|
932
|
+
* properly typed `Accumulator<T[K]>` values. Primarily used internally by
|
|
933
|
+
* `AggregatePipeline.groupBy` — most users interact with the builder via
|
|
934
|
+
* the callback parameter rather than calling this directly.
|
|
935
|
+
*
|
|
936
|
+
* @typeParam T - The current pipeline output document type.
|
|
937
|
+
* @returns An `AccumulatorBuilder<T>` with methods for each MongoDB accumulator.
|
|
905
938
|
*
|
|
906
939
|
* @example
|
|
907
940
|
* ```ts
|
|
908
|
-
* const
|
|
909
|
-
*
|
|
941
|
+
* const acc = createAccumulatorBuilder<{ salary: number; name: string }>()
|
|
942
|
+
* acc.min('salary') // { __accum: true, expr: { $min: '$salary' } }
|
|
943
|
+
* acc.push('name') // { __accum: true, expr: { $push: '$name' } }
|
|
910
944
|
* ```
|
|
911
945
|
*/
|
|
912
|
-
|
|
913
|
-
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: ArrayElement<NonNullable<T[K]>> | AddToSetEach<ArrayElement<NonNullable<T[K]>>>;
|
|
914
|
-
};
|
|
946
|
+
declare function createAccumulatorBuilder<T>(): AccumulatorBuilder<T>;
|
|
915
947
|
/**
|
|
916
|
-
*
|
|
948
|
+
* Create a typed expression builder for use inside `addFields` callbacks.
|
|
917
949
|
*
|
|
918
|
-
*
|
|
950
|
+
* The builder adds the `$` prefix to field names automatically and returns
|
|
951
|
+
* properly typed `Expression<T>` values. Primarily used internally by
|
|
952
|
+
* `AggregatePipeline.addFields` — most users interact with the builder via
|
|
953
|
+
* the callback parameter rather than calling this directly.
|
|
954
|
+
*
|
|
955
|
+
* @typeParam T - The current pipeline output document type.
|
|
956
|
+
* @returns An `ExpressionBuilder<T>` with methods for each MongoDB expression operator.
|
|
919
957
|
*
|
|
920
958
|
* @example
|
|
921
959
|
* ```ts
|
|
922
|
-
* const
|
|
960
|
+
* const expr = createExpressionBuilder<{ salary: number; name: string; hiredAt: Date }>()
|
|
961
|
+
* expr.year('hiredAt') // { __expr: true, value: { $year: '$hiredAt' } }
|
|
962
|
+
* expr.multiply('salary', 0.9) // { __expr: true, value: { $multiply: ['$salary', 0.9] } }
|
|
923
963
|
* ```
|
|
924
964
|
*/
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
};
|
|
965
|
+
declare function createExpressionBuilder<T>(): ExpressionBuilder<T>;
|
|
966
|
+
|
|
928
967
|
/**
|
|
929
|
-
*
|
|
968
|
+
* Comparison operators for a field value of type `V`.
|
|
930
969
|
*
|
|
931
|
-
*
|
|
970
|
+
* Maps each MongoDB comparison operator to its expected value type.
|
|
971
|
+
* `$regex` is only available when `V` extends `string`.
|
|
972
|
+
*
|
|
973
|
+
* Used as the operator object that can be assigned to a field in {@link TypedFilter}.
|
|
932
974
|
*
|
|
933
975
|
* @example
|
|
934
976
|
* ```ts
|
|
935
|
-
*
|
|
977
|
+
* // As a raw object (without builder functions)
|
|
978
|
+
* const filter: TypedFilter<User> = { age: { $gt: 25, $lte: 65 } }
|
|
979
|
+
*
|
|
980
|
+
* // $regex only available on string fields
|
|
981
|
+
* const filter: TypedFilter<User> = { name: { $regex: /^A/i } }
|
|
936
982
|
* ```
|
|
937
983
|
*/
|
|
938
|
-
type
|
|
939
|
-
|
|
940
|
-
|
|
984
|
+
type ComparisonOperators<V> = {
|
|
985
|
+
/** Matches values equal to the specified value. */
|
|
986
|
+
$eq?: V;
|
|
987
|
+
/** Matches values not equal to the specified value. */
|
|
988
|
+
$ne?: V;
|
|
989
|
+
/** Matches values greater than the specified value. */
|
|
990
|
+
$gt?: V;
|
|
991
|
+
/** Matches values greater than or equal to the specified value. */
|
|
992
|
+
$gte?: V;
|
|
993
|
+
/** Matches values less than the specified value. */
|
|
994
|
+
$lt?: V;
|
|
995
|
+
/** Matches values less than or equal to the specified value. */
|
|
996
|
+
$lte?: V;
|
|
997
|
+
/** Matches any value in the specified array. Accepts `as const` arrays for type narrowing. */
|
|
998
|
+
$in?: readonly V[];
|
|
999
|
+
/** Matches none of the values in the specified array. Accepts `as const` arrays for type narrowing. */
|
|
1000
|
+
$nin?: readonly V[];
|
|
1001
|
+
/** Matches documents where the field exists (`true`) or does not exist (`false`). */
|
|
1002
|
+
$exists?: boolean;
|
|
1003
|
+
/** Negates a comparison operator. */
|
|
1004
|
+
$not?: ComparisonOperators<V>;
|
|
1005
|
+
} & (V extends string ? {
|
|
1006
|
+
$regex?: RegExp | string;
|
|
1007
|
+
} : unknown);
|
|
1008
|
+
/** Depth counter for limiting dot-notation recursion. Index = current depth, value = next depth. */
|
|
1009
|
+
type Prev = [never, 0, 1, 2];
|
|
941
1010
|
/**
|
|
942
|
-
*
|
|
1011
|
+
* Generates a union of all valid dot-separated paths for nested object fields in `T`.
|
|
943
1012
|
*
|
|
944
|
-
*
|
|
1013
|
+
* Recursion is limited to 3 levels deep to prevent TypeScript compilation performance issues.
|
|
1014
|
+
* Only plain object fields are traversed — arrays, `Date`, `RegExp`, and `ObjectId` are
|
|
1015
|
+
* treated as leaf nodes and do not produce sub-paths.
|
|
945
1016
|
*
|
|
946
1017
|
* @example
|
|
947
1018
|
* ```ts
|
|
948
|
-
*
|
|
1019
|
+
* type User = { address: { city: string; geo: { lat: number; lng: number } } }
|
|
1020
|
+
*
|
|
1021
|
+
* // DotPaths<User> = 'address.city' | 'address.geo' | 'address.geo.lat' | 'address.geo.lng'
|
|
949
1022
|
* ```
|
|
950
1023
|
*/
|
|
951
|
-
type
|
|
952
|
-
[K in keyof T
|
|
953
|
-
|
|
954
|
-
};
|
|
955
|
-
};
|
|
1024
|
+
type DotPaths<T, Depth extends number = 3> = Depth extends 0 ? never : {
|
|
1025
|
+
[K in keyof T & string]: NonNullable<T[K]> extends ReadonlyArray<unknown> | Date | RegExp | ObjectId ? never : NonNullable<T[K]> extends Record<string, unknown> ? `${K}.${keyof NonNullable<T[K]> & string}` | `${K}.${DotPaths<NonNullable<T[K]>, Prev[Depth]>}` : never;
|
|
1026
|
+
}[keyof T & string];
|
|
956
1027
|
/**
|
|
957
|
-
*
|
|
1028
|
+
* Resolves the value type at a dot-separated path `P` within type `T`.
|
|
958
1029
|
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
1030
|
+
* Splits `P` on the first `.` and recursively descends into `T`'s nested types.
|
|
1031
|
+
* Returns `never` if the path is invalid.
|
|
961
1032
|
*
|
|
962
1033
|
* @example
|
|
963
1034
|
* ```ts
|
|
964
|
-
*
|
|
1035
|
+
* type User = { address: { city: string; geo: { lat: number } } }
|
|
1036
|
+
*
|
|
1037
|
+
* // DotPathType<User, 'address.city'> = string
|
|
1038
|
+
* // DotPathType<User, 'address.geo.lat'> = number
|
|
965
1039
|
* ```
|
|
966
1040
|
*/
|
|
967
|
-
type
|
|
968
|
-
[K in keyof T & string]?: string;
|
|
969
|
-
};
|
|
1041
|
+
type DotPathType<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? Rest extends keyof NonNullable<T[K]> ? NonNullable<T[K]>[Rest] : DotPathType<NonNullable<T[K]>, Rest> : never : P extends keyof T ? T[P] : never;
|
|
970
1042
|
/**
|
|
971
|
-
* Strict type-safe MongoDB
|
|
1043
|
+
* Strict type-safe MongoDB filter query type.
|
|
972
1044
|
*
|
|
973
|
-
* Validates
|
|
974
|
-
*
|
|
975
|
-
*
|
|
1045
|
+
* Validates filter objects at compile time — rejects nonexistent fields, type mismatches,
|
|
1046
|
+
* and invalid operator usage. Unlike the MongoDB driver's `Filter<T>`, does NOT allow
|
|
1047
|
+
* arbitrary keys via `& Document`.
|
|
1048
|
+
*
|
|
1049
|
+
* Supports three forms of filter expressions:
|
|
1050
|
+
* - **Direct field values** (implicit `$eq`): `{ name: 'Alice' }`
|
|
1051
|
+
* - **Comparison operators**: `{ age: { $gt: 25 } }` or `{ age: $gt(25) }`
|
|
1052
|
+
* - **Dot notation** for nested fields up to 3 levels: `{ 'address.city': 'NYC' }`
|
|
1053
|
+
*
|
|
1054
|
+
* Logical operators `$and`, `$or`, and `$nor` accept arrays of `TypedFilter<T>`
|
|
1055
|
+
* for composing complex queries.
|
|
976
1056
|
*
|
|
977
1057
|
* @example
|
|
978
1058
|
* ```ts
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
1059
|
+
* // Simple equality
|
|
1060
|
+
* const filter: TypedFilter<User> = { name: 'Alice' }
|
|
1061
|
+
*
|
|
1062
|
+
* // Builder functions mixed with object literals
|
|
1063
|
+
* const filter: TypedFilter<User> = { age: $gte(18), role: $in(['admin', 'mod']) }
|
|
1064
|
+
*
|
|
1065
|
+
* // Logical composition — T inferred from find() context
|
|
1066
|
+
* posts.find($and(
|
|
1067
|
+
* $or({ published: true }, { views: $gte(100) }),
|
|
1068
|
+
* { title: $regex(/guide/i) },
|
|
1069
|
+
* ))
|
|
1070
|
+
*
|
|
1071
|
+
* // Dynamic conditional building
|
|
1072
|
+
* const conditions: TypedFilter<User>[] = []
|
|
1073
|
+
* if (name) conditions.push({ name })
|
|
1074
|
+
* if (minAge) conditions.push({ age: $gte(minAge) })
|
|
1075
|
+
* const filter = conditions.length ? $and<User>(...conditions) : {}
|
|
983
1076
|
* ```
|
|
984
1077
|
*/
|
|
985
|
-
type
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
/**
|
|
991
|
-
$
|
|
992
|
-
/**
|
|
993
|
-
$
|
|
994
|
-
/**
|
|
995
|
-
$
|
|
996
|
-
/** Appends a value to an array field. Supports `$each`, `$position`, `$slice`, `$sort`. */
|
|
997
|
-
$push?: PushFields<T>;
|
|
998
|
-
/** Removes matching values from an array field. */
|
|
999
|
-
$pull?: PullFields<T>;
|
|
1000
|
-
/** Adds a value to an array only if it doesn't already exist. */
|
|
1001
|
-
$addToSet?: AddToSetFields<T>;
|
|
1002
|
-
/** Removes the first (`-1`) or last (`1`) element of an array. */
|
|
1003
|
-
$pop?: PopFields<T>;
|
|
1004
|
-
/** Removes the specified fields from the document. */
|
|
1005
|
-
$unset?: UnsetFields<T>;
|
|
1006
|
-
/** Sets the field to the current date. Only accepts Date-typed fields. */
|
|
1007
|
-
$currentDate?: CurrentDateFields<T>;
|
|
1008
|
-
/** Renames a field. */
|
|
1009
|
-
$rename?: RenameFields<T>;
|
|
1078
|
+
type TypedFilter<T> = {
|
|
1079
|
+
[K in keyof T]?: T[K] | ComparisonOperators<T[K]>;
|
|
1080
|
+
} & {
|
|
1081
|
+
[P in DotPaths<T>]?: DotPathType<T, P> | ComparisonOperators<DotPathType<T, P>>;
|
|
1082
|
+
} & {
|
|
1083
|
+
/** Joins clauses with a logical AND. Matches documents that satisfy all filters. */
|
|
1084
|
+
$and?: TypedFilter<T>[];
|
|
1085
|
+
/** Joins clauses with a logical OR. Matches documents that satisfy at least one filter. */
|
|
1086
|
+
$or?: TypedFilter<T>[];
|
|
1087
|
+
/** Joins clauses with a logical NOR. Matches documents that fail all filters. */
|
|
1088
|
+
$nor?: TypedFilter<T>[];
|
|
1010
1089
|
};
|
|
1011
1090
|
|
|
1012
1091
|
/**
|
|
1013
|
-
* Options for {@link
|
|
1014
|
-
*/
|
|
1015
|
-
type UpdateOptions = {
|
|
1016
|
-
/** When `true`, inserts a new document if no document matches the filter. */
|
|
1017
|
-
upsert?: boolean;
|
|
1018
|
-
};
|
|
1019
|
-
/**
|
|
1020
|
-
* Options for {@link findOneAndUpdate}.
|
|
1092
|
+
* Options for {@link findOneAndDelete}.
|
|
1021
1093
|
*/
|
|
1022
|
-
type
|
|
1023
|
-
/** Whether to return the document before or after the update. Defaults to `'after'`. */
|
|
1024
|
-
returnDocument?: 'before' | 'after';
|
|
1025
|
-
/** When `true`, inserts a new document if no document matches the filter. */
|
|
1026
|
-
upsert?: boolean;
|
|
1094
|
+
type FindOneAndDeleteOptions = {
|
|
1027
1095
|
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
1028
1096
|
validate?: ValidationMode | false;
|
|
1029
1097
|
};
|
|
1030
1098
|
/**
|
|
1031
|
-
*
|
|
1099
|
+
* Delete a single document matching the filter.
|
|
1032
1100
|
*
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1101
|
+
* Removes the first document that matches the filter from the collection.
|
|
1102
|
+
* No validation is performed — the document is deleted directly through
|
|
1103
|
+
* the MongoDB driver.
|
|
1036
1104
|
*
|
|
1037
|
-
* @param handle - The collection handle to
|
|
1105
|
+
* @param handle - The collection handle to delete from.
|
|
1038
1106
|
* @param filter - Type-safe filter to match documents.
|
|
1039
|
-
* @
|
|
1040
|
-
* @param options - Optional settings such as `upsert`.
|
|
1041
|
-
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
1107
|
+
* @returns The MongoDB `DeleteResult` with the deleted count.
|
|
1042
1108
|
*
|
|
1043
1109
|
* @example
|
|
1044
1110
|
* ```ts
|
|
1045
|
-
* const result = await
|
|
1046
|
-
* console.log(result.
|
|
1111
|
+
* const result = await deleteOne(users, { name: 'Ada' })
|
|
1112
|
+
* console.log(result.deletedCount) // 1
|
|
1047
1113
|
* ```
|
|
1048
1114
|
*/
|
|
1049
|
-
declare function
|
|
1115
|
+
declare function deleteOne<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>): Promise<DeleteResult>;
|
|
1050
1116
|
/**
|
|
1051
|
-
*
|
|
1117
|
+
* Delete all documents matching the filter.
|
|
1052
1118
|
*
|
|
1053
|
-
*
|
|
1054
|
-
*
|
|
1055
|
-
*
|
|
1119
|
+
* Removes every document that matches the filter from the collection.
|
|
1120
|
+
* No validation is performed — documents are deleted directly through
|
|
1121
|
+
* the MongoDB driver.
|
|
1056
1122
|
*
|
|
1057
|
-
* @param handle - The collection handle to
|
|
1123
|
+
* @param handle - The collection handle to delete from.
|
|
1058
1124
|
* @param filter - Type-safe filter to match documents.
|
|
1059
|
-
* @
|
|
1060
|
-
* @param options - Optional settings such as `upsert`.
|
|
1061
|
-
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
1125
|
+
* @returns The MongoDB `DeleteResult` with the deleted count.
|
|
1062
1126
|
*
|
|
1063
1127
|
* @example
|
|
1064
1128
|
* ```ts
|
|
1065
|
-
* const result = await
|
|
1066
|
-
* console.log(result.
|
|
1129
|
+
* const result = await deleteMany(users, { role: 'guest' })
|
|
1130
|
+
* console.log(result.deletedCount) // number of guests removed
|
|
1067
1131
|
* ```
|
|
1068
1132
|
*/
|
|
1069
|
-
declare function
|
|
1133
|
+
declare function deleteMany<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>): Promise<DeleteResult>;
|
|
1070
1134
|
/**
|
|
1071
|
-
* Find a single document matching the filter,
|
|
1135
|
+
* Find a single document matching the filter, delete it, and return the document.
|
|
1136
|
+
*
|
|
1137
|
+
* Returns the deleted document, or `null` if no document matches the filter.
|
|
1138
|
+
* The returned document is validated against the collection's Zod schema
|
|
1139
|
+
* using the same resolution logic as {@link findOne}.
|
|
1140
|
+
*
|
|
1141
|
+
* @param handle - The collection handle to delete from.
|
|
1142
|
+
* @param filter - Type-safe filter to match documents.
|
|
1143
|
+
* @param options - Optional settings: `validate`.
|
|
1144
|
+
* @returns The deleted document, or `null` if no document matches.
|
|
1145
|
+
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
1146
|
+
*
|
|
1147
|
+
* @example
|
|
1148
|
+
* ```ts
|
|
1149
|
+
* const user = await findOneAndDelete(users, { name: 'Ada' })
|
|
1150
|
+
* if (user) console.log(user.name) // 'Ada' (the deleted document)
|
|
1151
|
+
* ```
|
|
1152
|
+
*
|
|
1153
|
+
* @example
|
|
1154
|
+
* ```ts
|
|
1155
|
+
* const user = await findOneAndDelete(
|
|
1156
|
+
* users,
|
|
1157
|
+
* { role: 'guest' },
|
|
1158
|
+
* { validate: false },
|
|
1159
|
+
* )
|
|
1160
|
+
* ```
|
|
1161
|
+
*/
|
|
1162
|
+
declare function findOneAndDelete<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneAndDeleteOptions): Promise<InferDocument<TDef> | null>;
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Options for offset-based pagination.
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* ```ts
|
|
1169
|
+
* await users.find({}).sort({ name: 1 }).paginate({ page: 2, perPage: 10 })
|
|
1170
|
+
* ```
|
|
1171
|
+
*/
|
|
1172
|
+
type OffsetPaginateOptions = {
|
|
1173
|
+
/** The page number to retrieve (1-indexed). */
|
|
1174
|
+
page: number;
|
|
1175
|
+
/** The number of documents per page. */
|
|
1176
|
+
perPage: number;
|
|
1177
|
+
};
|
|
1178
|
+
/**
|
|
1179
|
+
* Options for cursor-based pagination.
|
|
1180
|
+
*
|
|
1181
|
+
* @example
|
|
1182
|
+
* ```ts
|
|
1183
|
+
* const first = await users.find({}).sort({ name: 1 }).paginate({ limit: 10 })
|
|
1184
|
+
* const next = await users.find({}).sort({ name: 1 }).paginate({ cursor: first.endCursor, limit: 10 })
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
type CursorPaginateOptions = {
|
|
1188
|
+
/** Maximum number of documents to return. */
|
|
1189
|
+
limit: number;
|
|
1190
|
+
/** Opaque cursor string from a previous `startCursor` or `endCursor`. */
|
|
1191
|
+
cursor?: string | null;
|
|
1192
|
+
};
|
|
1193
|
+
/**
|
|
1194
|
+
* Result of offset-based pagination.
|
|
1195
|
+
*
|
|
1196
|
+
* @example
|
|
1197
|
+
* ```ts
|
|
1198
|
+
* const page = await users.find({}).paginate({ page: 1, perPage: 10 })
|
|
1199
|
+
* console.log(page.total, page.totalPages, page.hasNext)
|
|
1200
|
+
* ```
|
|
1201
|
+
*/
|
|
1202
|
+
type OffsetPage<TDoc> = {
|
|
1203
|
+
/** The documents for this page. */
|
|
1204
|
+
docs: TDoc[];
|
|
1205
|
+
/** Total number of matching documents. */
|
|
1206
|
+
total: number;
|
|
1207
|
+
/** Current page number (1-indexed). */
|
|
1208
|
+
page: number;
|
|
1209
|
+
/** Number of documents per page. */
|
|
1210
|
+
perPage: number;
|
|
1211
|
+
/** Total number of pages (`Math.ceil(total / perPage)`). */
|
|
1212
|
+
totalPages: number;
|
|
1213
|
+
/** Whether there is a next page. */
|
|
1214
|
+
hasNext: boolean;
|
|
1215
|
+
/** Whether there is a previous page. */
|
|
1216
|
+
hasPrev: boolean;
|
|
1217
|
+
};
|
|
1218
|
+
/**
|
|
1219
|
+
* Result of cursor-based pagination.
|
|
1220
|
+
*
|
|
1221
|
+
* @example
|
|
1222
|
+
* ```ts
|
|
1223
|
+
* const page = await users.find({}).paginate({ limit: 10 })
|
|
1224
|
+
* if (page.hasNext) {
|
|
1225
|
+
* const next = await users.find({}).paginate({ cursor: page.endCursor, limit: 10 })
|
|
1226
|
+
* }
|
|
1227
|
+
* ```
|
|
1228
|
+
*/
|
|
1229
|
+
type CursorPage<TDoc> = {
|
|
1230
|
+
/** The documents for this page. */
|
|
1231
|
+
docs: TDoc[];
|
|
1232
|
+
/** Whether there are more documents after this page. */
|
|
1233
|
+
hasNext: boolean;
|
|
1234
|
+
/** Whether there are documents before this page. */
|
|
1235
|
+
hasPrev: boolean;
|
|
1236
|
+
/** Cursor for the first document (pass to `cursor` to go backward). `null` when empty. */
|
|
1237
|
+
startCursor: string | null;
|
|
1238
|
+
/** Cursor for the last document (pass to `cursor` to go forward). `null` when empty. */
|
|
1239
|
+
endCursor: string | null;
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Type-safe sort specification for a document type.
|
|
1244
|
+
*
|
|
1245
|
+
* Constrains sort keys to top-level fields of `T` with direction `1` (ascending)
|
|
1246
|
+
* or `-1` (descending). Dot-path sorts deferred to v1.0.
|
|
1247
|
+
*
|
|
1248
|
+
* @example
|
|
1249
|
+
* ```ts
|
|
1250
|
+
* const sort: TypedSort<User> = { name: 1, createdAt: -1 }
|
|
1251
|
+
* ```
|
|
1252
|
+
*/
|
|
1253
|
+
type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
|
|
1254
|
+
/**
|
|
1255
|
+
* Type-safe cursor wrapping MongoDB's `FindCursor`.
|
|
1256
|
+
*
|
|
1257
|
+
* Provides chainable query modifiers (`sort`, `skip`, `limit`, `hint`) that return
|
|
1258
|
+
* `this` for fluent chaining, and terminal methods (`toArray`,
|
|
1259
|
+
* `[Symbol.asyncIterator]`) that validate each document against the
|
|
1260
|
+
* collection's Zod schema before returning.
|
|
1261
|
+
*
|
|
1262
|
+
* Created by {@link find} — do not construct directly.
|
|
1263
|
+
*
|
|
1264
|
+
* @typeParam TDef - The collection definition type, used to infer the document type.
|
|
1265
|
+
* @typeParam TIndexNames - Union of declared index names accepted by `.hint()`.
|
|
1266
|
+
*
|
|
1267
|
+
* @example
|
|
1268
|
+
* ```ts
|
|
1269
|
+
* const docs = await find(users, { role: 'admin' })
|
|
1270
|
+
* .sort({ name: 1 })
|
|
1271
|
+
* .limit(10)
|
|
1272
|
+
* .toArray()
|
|
1273
|
+
* ```
|
|
1274
|
+
*/
|
|
1275
|
+
declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends string = string> {
|
|
1276
|
+
/** @internal */
|
|
1277
|
+
private cursor;
|
|
1278
|
+
/** @internal */
|
|
1279
|
+
private schema;
|
|
1280
|
+
/** @internal */
|
|
1281
|
+
private collectionName;
|
|
1282
|
+
/** @internal */
|
|
1283
|
+
private mode;
|
|
1284
|
+
/** @internal */
|
|
1285
|
+
private readonly nativeCollection;
|
|
1286
|
+
/** @internal */
|
|
1287
|
+
private readonly filter;
|
|
1288
|
+
/** @internal */
|
|
1289
|
+
private sortSpec;
|
|
1290
|
+
/** @internal */
|
|
1291
|
+
constructor(cursor: FindCursor<InferDocument<TDef>>, definition: TDef, mode: ValidationMode | false, nativeCollection: Collection<InferDocument<TDef>>, filter: any);
|
|
1292
|
+
/**
|
|
1293
|
+
* Set the sort order for the query.
|
|
1294
|
+
*
|
|
1295
|
+
* Only top-level document fields are accepted as sort keys.
|
|
1296
|
+
* Values must be `1` (ascending) or `-1` (descending).
|
|
1297
|
+
*
|
|
1298
|
+
* @param spec - Sort specification mapping field names to sort direction.
|
|
1299
|
+
* @returns `this` for chaining.
|
|
1300
|
+
*
|
|
1301
|
+
* @example
|
|
1302
|
+
* ```ts
|
|
1303
|
+
* find(users, {}).sort({ name: 1, age: -1 }).toArray()
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
sort(spec: TypedSort<InferDocument<TDef>>): this;
|
|
1307
|
+
/**
|
|
1308
|
+
* Skip the first `n` documents in the result set.
|
|
1309
|
+
*
|
|
1310
|
+
* @param n - Number of documents to skip.
|
|
1311
|
+
* @returns `this` for chaining.
|
|
1312
|
+
*
|
|
1313
|
+
* @example
|
|
1314
|
+
* ```ts
|
|
1315
|
+
* find(users, {}).skip(10).limit(10).toArray() // page 2
|
|
1316
|
+
* ```
|
|
1317
|
+
*/
|
|
1318
|
+
skip(n: number): this;
|
|
1319
|
+
/**
|
|
1320
|
+
* Limit the number of documents returned.
|
|
1321
|
+
*
|
|
1322
|
+
* @param n - Maximum number of documents to return.
|
|
1323
|
+
* @returns `this` for chaining.
|
|
1324
|
+
*
|
|
1325
|
+
* @example
|
|
1326
|
+
* ```ts
|
|
1327
|
+
* find(users, {}).limit(10).toArray() // at most 10 docs
|
|
1328
|
+
* ```
|
|
1329
|
+
*/
|
|
1330
|
+
limit(n: number): this;
|
|
1331
|
+
/**
|
|
1332
|
+
* Force the query optimizer to use the specified index.
|
|
1333
|
+
*
|
|
1334
|
+
* Only accepts index names that were declared via `.name()` in the
|
|
1335
|
+
* collection definition. If no named indexes exist, any string is accepted.
|
|
1336
|
+
*
|
|
1337
|
+
* @param indexName - The name of a declared compound index.
|
|
1338
|
+
* @returns `this` for chaining.
|
|
1339
|
+
*
|
|
1340
|
+
* @example
|
|
1341
|
+
* ```ts
|
|
1342
|
+
* const Users = collection('users', { email: z.string(), role: z.string() }, {
|
|
1343
|
+
* indexes: [index({ email: 1, role: -1 }).name('email_role_idx')],
|
|
1344
|
+
* })
|
|
1345
|
+
* const admins = await users.find({ role: 'admin' })
|
|
1346
|
+
* .hint('email_role_idx')
|
|
1347
|
+
* .toArray()
|
|
1348
|
+
* ```
|
|
1349
|
+
*/
|
|
1350
|
+
hint(indexName: TIndexNames): this;
|
|
1351
|
+
/**
|
|
1352
|
+
* Execute the query with offset-based pagination, returning a page of documents
|
|
1353
|
+
* with total count and navigation metadata.
|
|
1354
|
+
*
|
|
1355
|
+
* Runs `countDocuments` and `find` in parallel for performance. Ignores any
|
|
1356
|
+
* `.skip()` or `.limit()` already set on the cursor — issues a fresh query.
|
|
1357
|
+
*
|
|
1358
|
+
* @param opts - Offset pagination options: `page` (1-indexed) and `perPage`.
|
|
1359
|
+
* @returns A page with `docs`, `total`, `totalPages`, `hasNext`, `hasPrev`.
|
|
1360
|
+
* @throws {ZodmonValidationError} When a document fails schema validation.
|
|
1361
|
+
*
|
|
1362
|
+
* @example
|
|
1363
|
+
* ```ts
|
|
1364
|
+
* const page = await users.find({ role: 'admin' })
|
|
1365
|
+
* .sort({ createdAt: -1 })
|
|
1366
|
+
* .paginate({ page: 2, perPage: 10 })
|
|
1367
|
+
* console.log(page.total, page.totalPages, page.hasNext)
|
|
1368
|
+
* ```
|
|
1369
|
+
*/
|
|
1370
|
+
paginate(opts: OffsetPaginateOptions): Promise<OffsetPage<InferDocument<TDef>>>;
|
|
1371
|
+
/**
|
|
1372
|
+
* Execute the query with cursor-based pagination, returning a page of documents
|
|
1373
|
+
* with opaque cursors for forward/backward navigation.
|
|
1374
|
+
*
|
|
1375
|
+
* Uses the `limit + 1` trick to determine `hasNext`/`hasPrev` without extra queries.
|
|
1376
|
+
* Direction is encoded in the cursor — pass `endCursor` to go forward, `startCursor`
|
|
1377
|
+
* to go backward.
|
|
1378
|
+
*
|
|
1379
|
+
* @param opts - Cursor pagination options: `limit` and optional `cursor`.
|
|
1380
|
+
* @returns A page with `docs`, `hasNext`, `hasPrev`, `startCursor`, `endCursor`.
|
|
1381
|
+
* @throws {ZodmonValidationError} When a document fails schema validation.
|
|
1382
|
+
* @throws {Error} When the cursor string is malformed.
|
|
1383
|
+
*
|
|
1384
|
+
* @example
|
|
1385
|
+
* ```ts
|
|
1386
|
+
* const first = await users.find({}).sort({ name: 1 }).paginate({ limit: 10 })
|
|
1387
|
+
* const next = await users.find({}).sort({ name: 1 })
|
|
1388
|
+
* .paginate({ cursor: first.endCursor, limit: 10 })
|
|
1389
|
+
* ```
|
|
1390
|
+
*/
|
|
1391
|
+
paginate(opts: CursorPaginateOptions): Promise<CursorPage<InferDocument<TDef>>>;
|
|
1392
|
+
/** @internal Offset pagination implementation. */
|
|
1393
|
+
private offsetPaginate;
|
|
1394
|
+
/** @internal Cursor pagination implementation. */
|
|
1395
|
+
private cursorPaginate;
|
|
1396
|
+
/**
|
|
1397
|
+
* Execute the query and return all matching documents as an array.
|
|
1398
|
+
*
|
|
1399
|
+
* Each document is validated against the collection's Zod schema
|
|
1400
|
+
* according to the resolved validation mode.
|
|
1401
|
+
*
|
|
1402
|
+
* @returns Array of validated documents.
|
|
1403
|
+
* @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.
|
|
1404
|
+
*
|
|
1405
|
+
* @example
|
|
1406
|
+
* ```ts
|
|
1407
|
+
* const admins = await find(users, { role: 'admin' }).toArray()
|
|
1408
|
+
* ```
|
|
1409
|
+
*/
|
|
1410
|
+
toArray(): Promise<InferDocument<TDef>[]>;
|
|
1411
|
+
/**
|
|
1412
|
+
* Async iterator for streaming documents one at a time.
|
|
1413
|
+
*
|
|
1414
|
+
* Each yielded document is validated against the collection's Zod schema.
|
|
1415
|
+
* Memory-efficient for large result sets.
|
|
1416
|
+
*
|
|
1417
|
+
* @yields Validated documents one at a time.
|
|
1418
|
+
* @throws {ZodmonValidationError} When a document fails schema validation.
|
|
1419
|
+
*
|
|
1420
|
+
* @example
|
|
1421
|
+
* ```ts
|
|
1422
|
+
* for await (const user of find(users, {})) {
|
|
1423
|
+
* console.log(user.name)
|
|
1424
|
+
* }
|
|
1425
|
+
* ```
|
|
1426
|
+
*/
|
|
1427
|
+
[Symbol.asyncIterator](): AsyncGenerator<InferDocument<TDef>>;
|
|
1428
|
+
/** @internal Validate a single raw document against the schema. */
|
|
1429
|
+
private validateDoc;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
/**
|
|
1433
|
+
* Options for {@link findOne} and {@link findOneOrThrow}.
|
|
1434
|
+
*/
|
|
1435
|
+
type FindOneOptions = {
|
|
1436
|
+
/** MongoDB projection — include (`1`) or exclude (`0`) fields. Typed projections deferred to v1.0. */
|
|
1437
|
+
project?: Record<string, 0 | 1>;
|
|
1438
|
+
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
1439
|
+
validate?: ValidationMode | false;
|
|
1440
|
+
};
|
|
1441
|
+
/**
|
|
1442
|
+
* Find a single document matching the filter.
|
|
1443
|
+
*
|
|
1444
|
+
* Queries MongoDB, then validates the fetched document against the collection's
|
|
1445
|
+
* Zod schema. Validation mode is resolved from the per-query option, falling
|
|
1446
|
+
* back to the collection-level default (which defaults to `'strict'`).
|
|
1447
|
+
*
|
|
1448
|
+
* @param handle - The collection handle to query.
|
|
1449
|
+
* @param filter - Type-safe filter to match documents.
|
|
1450
|
+
* @param options - Optional projection and validation overrides.
|
|
1451
|
+
* @returns The matched document, or `null` if no document matches.
|
|
1452
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1453
|
+
*
|
|
1454
|
+
* @example
|
|
1455
|
+
* ```ts
|
|
1456
|
+
* const user = await findOne(users, { name: 'Ada' })
|
|
1457
|
+
* if (user) console.log(user.role) // typed as 'admin' | 'user'
|
|
1458
|
+
* ```
|
|
1459
|
+
*/
|
|
1460
|
+
declare function findOne<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef> | null>;
|
|
1461
|
+
/**
|
|
1462
|
+
* Find a single document matching the filter, or throw if none exists.
|
|
1463
|
+
*
|
|
1464
|
+
* Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}
|
|
1465
|
+
* instead of returning `null` when no document matches the filter.
|
|
1466
|
+
*
|
|
1467
|
+
* @param handle - The collection handle to query.
|
|
1468
|
+
* @param filter - Type-safe filter to match documents.
|
|
1469
|
+
* @param options - Optional projection and validation overrides.
|
|
1470
|
+
* @returns The matched document (never null).
|
|
1471
|
+
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
1472
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1473
|
+
*
|
|
1474
|
+
* @example
|
|
1475
|
+
* ```ts
|
|
1476
|
+
* const user = await findOneOrThrow(users, { name: 'Ada' })
|
|
1477
|
+
* console.log(user.role) // typed as 'admin' | 'user', guaranteed non-null
|
|
1478
|
+
* ```
|
|
1479
|
+
*/
|
|
1480
|
+
declare function findOneOrThrow<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
|
|
1481
|
+
/**
|
|
1482
|
+
* Options for {@link find}.
|
|
1483
|
+
*/
|
|
1484
|
+
type FindOptions = {
|
|
1485
|
+
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
1486
|
+
validate?: ValidationMode | false;
|
|
1487
|
+
};
|
|
1488
|
+
/**
|
|
1489
|
+
* Find all documents matching the filter, returning a chainable typed cursor.
|
|
1490
|
+
*
|
|
1491
|
+
* The cursor is lazy — no query is executed until a terminal method
|
|
1492
|
+
* (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
|
|
1493
|
+
* to shape the query before executing.
|
|
1494
|
+
*
|
|
1495
|
+
* Each document is validated against the collection's Zod schema when
|
|
1496
|
+
* a terminal method consumes it.
|
|
1497
|
+
*
|
|
1498
|
+
* @param handle - The collection handle to query.
|
|
1499
|
+
* @param filter - Type-safe filter to match documents.
|
|
1500
|
+
* @param options - Optional validation overrides.
|
|
1501
|
+
* @returns A typed cursor for chaining query modifiers.
|
|
1502
|
+
*
|
|
1503
|
+
* @example
|
|
1504
|
+
* ```ts
|
|
1505
|
+
* const admins = await find(users, { role: 'admin' })
|
|
1506
|
+
* .sort({ name: 1 })
|
|
1507
|
+
* .limit(10)
|
|
1508
|
+
* .toArray()
|
|
1509
|
+
* ```
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* ```ts
|
|
1513
|
+
* for await (const user of find(users, {})) {
|
|
1514
|
+
* console.log(user.name)
|
|
1515
|
+
* }
|
|
1516
|
+
* ```
|
|
1517
|
+
*/
|
|
1518
|
+
declare function find<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef, IndexNames<TDef>>;
|
|
1519
|
+
|
|
1520
|
+
/**
|
|
1521
|
+
* Extracts the element type from an array type.
|
|
1522
|
+
*
|
|
1523
|
+
* @example
|
|
1524
|
+
* ```ts
|
|
1525
|
+
* type E = ArrayElement<string[]> // string
|
|
1526
|
+
* type N = ArrayElement<number[]> // number
|
|
1527
|
+
* ```
|
|
1528
|
+
*/
|
|
1529
|
+
type ArrayElement<T> = T extends ReadonlyArray<infer E> ? E : never;
|
|
1530
|
+
/**
|
|
1531
|
+
* Fields valid for `$set`, `$setOnInsert`, `$min`, and `$max` operators.
|
|
1532
|
+
*
|
|
1533
|
+
* Allows top-level fields with matching value types plus dot-notation paths
|
|
1534
|
+
* for nested field updates.
|
|
1535
|
+
*
|
|
1536
|
+
* @example
|
|
1537
|
+
* ```ts
|
|
1538
|
+
* const set: SetFields<User> = { name: 'Alice', 'address.city': 'NYC' }
|
|
1539
|
+
* ```
|
|
1540
|
+
*/
|
|
1541
|
+
type SetFields<T> = {
|
|
1542
|
+
[K in keyof T]?: T[K];
|
|
1543
|
+
} & {
|
|
1544
|
+
[P in DotPaths<T>]?: DotPathType<T, P>;
|
|
1545
|
+
};
|
|
1546
|
+
/**
|
|
1547
|
+
* Fields valid for the `$inc` operator — only number-typed fields.
|
|
1548
|
+
*
|
|
1549
|
+
* Includes dot-notation paths that resolve to number types.
|
|
1550
|
+
*
|
|
1551
|
+
* @example
|
|
1552
|
+
* ```ts
|
|
1553
|
+
* const inc: IncFields<User> = { age: 1, 'stats.views': 5 }
|
|
1554
|
+
* ```
|
|
1555
|
+
*/
|
|
1556
|
+
type IncFields<T> = {
|
|
1557
|
+
[K in keyof T as NonNullable<T[K]> extends number ? K : never]?: number;
|
|
1558
|
+
} & {
|
|
1559
|
+
[P in DotPaths<T> as DotPathType<T, P> extends number ? P : never]?: number;
|
|
1560
|
+
};
|
|
1561
|
+
/**
|
|
1562
|
+
* Modifiers for the `$push` operator.
|
|
1563
|
+
*
|
|
1564
|
+
* Use with `$push` to insert multiple elements and control array position/size.
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```ts
|
|
1568
|
+
* const push = { tags: { $each: ['a', 'b'], $position: 0, $slice: 10 } }
|
|
1569
|
+
* ```
|
|
1570
|
+
*/
|
|
1571
|
+
type PushModifiers<E> = {
|
|
1572
|
+
/** Array of elements to push. */
|
|
1573
|
+
$each: E[];
|
|
1574
|
+
/** Position at which to insert elements. */
|
|
1575
|
+
$position?: number;
|
|
1576
|
+
/** Maximum array length after push. */
|
|
1577
|
+
$slice?: number;
|
|
1578
|
+
/** Sort order applied after push. */
|
|
1579
|
+
$sort?: 1 | -1 | Record<string, 1 | -1>;
|
|
1580
|
+
};
|
|
1581
|
+
/**
|
|
1582
|
+
* Fields valid for the `$push` operator — only array-typed fields.
|
|
1583
|
+
*
|
|
1584
|
+
* Accepts a single element value or a modifier object with `$each`.
|
|
1585
|
+
*
|
|
1586
|
+
* @example
|
|
1587
|
+
* ```ts
|
|
1588
|
+
* const push: PushFields<User> = { tags: 'dev' }
|
|
1589
|
+
* const pushMany: PushFields<User> = { tags: { $each: ['a', 'b'] } }
|
|
1590
|
+
* ```
|
|
1591
|
+
*/
|
|
1592
|
+
type PushFields<T> = {
|
|
1593
|
+
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: ArrayElement<NonNullable<T[K]>> | PushModifiers<ArrayElement<NonNullable<T[K]>>>;
|
|
1594
|
+
};
|
|
1595
|
+
/**
|
|
1596
|
+
* Fields valid for the `$pull` operator — only array-typed fields.
|
|
1597
|
+
*
|
|
1598
|
+
* For primitive arrays: accepts a value or comparison operators.
|
|
1599
|
+
* For object arrays: also accepts a `TypedFilter` on the element type.
|
|
1600
|
+
*
|
|
1601
|
+
* @example
|
|
1602
|
+
* ```ts
|
|
1603
|
+
* const pull: PullFields<User> = { tags: 'old' }
|
|
1604
|
+
* const pullQuery: PullFields<User> = { scores: { $lt: 50 } }
|
|
1605
|
+
* const pullObj: PullFields<User> = { comments: { author: 'spam' } }
|
|
1606
|
+
* ```
|
|
1607
|
+
*/
|
|
1608
|
+
type PullFields<T> = {
|
|
1609
|
+
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: ArrayElement<NonNullable<T[K]>> | ComparisonOperators<ArrayElement<NonNullable<T[K]>>> | (ArrayElement<NonNullable<T[K]>> extends Record<string, unknown> ? TypedFilter<ArrayElement<NonNullable<T[K]>>> : unknown);
|
|
1610
|
+
};
|
|
1611
|
+
/**
|
|
1612
|
+
* Modifier for the `$addToSet` operator's `$each` syntax.
|
|
1613
|
+
*
|
|
1614
|
+
* @example
|
|
1615
|
+
* ```ts
|
|
1616
|
+
* const addToSet = { tags: { $each: ['a', 'b'] } }
|
|
1617
|
+
* ```
|
|
1618
|
+
*/
|
|
1619
|
+
type AddToSetEach<E> = {
|
|
1620
|
+
$each: E[];
|
|
1621
|
+
};
|
|
1622
|
+
/**
|
|
1623
|
+
* Fields valid for the `$addToSet` operator — only array-typed fields.
|
|
1624
|
+
*
|
|
1625
|
+
* Accepts a single element value or `{ $each: [...] }` for multiple elements.
|
|
1626
|
+
*
|
|
1627
|
+
* @example
|
|
1628
|
+
* ```ts
|
|
1629
|
+
* const add: AddToSetFields<User> = { tags: 'dev' }
|
|
1630
|
+
* const addMany: AddToSetFields<User> = { tags: { $each: ['a', 'b'] } }
|
|
1631
|
+
* ```
|
|
1632
|
+
*/
|
|
1633
|
+
type AddToSetFields<T> = {
|
|
1634
|
+
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: ArrayElement<NonNullable<T[K]>> | AddToSetEach<ArrayElement<NonNullable<T[K]>>>;
|
|
1635
|
+
};
|
|
1636
|
+
/**
|
|
1637
|
+
* Fields valid for the `$pop` operator — only array-typed fields.
|
|
1638
|
+
*
|
|
1639
|
+
* Value `1` removes the last element, `-1` removes the first.
|
|
1640
|
+
*
|
|
1641
|
+
* @example
|
|
1642
|
+
* ```ts
|
|
1643
|
+
* const pop: PopFields<User> = { tags: -1 } // remove first
|
|
1644
|
+
* ```
|
|
1645
|
+
*/
|
|
1646
|
+
type PopFields<T> = {
|
|
1647
|
+
[K in keyof T as NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never]?: 1 | -1;
|
|
1648
|
+
};
|
|
1649
|
+
/**
|
|
1650
|
+
* Fields valid for the `$unset` operator — any existing field.
|
|
1651
|
+
*
|
|
1652
|
+
* Value is `''`, `true`, or `1` (all mean "remove this field").
|
|
1653
|
+
*
|
|
1654
|
+
* @example
|
|
1655
|
+
* ```ts
|
|
1656
|
+
* const unset: UnsetFields<User> = { middleName: '' }
|
|
1657
|
+
* ```
|
|
1658
|
+
*/
|
|
1659
|
+
type UnsetFields<T> = {
|
|
1660
|
+
[K in keyof T]?: '' | true | 1;
|
|
1661
|
+
};
|
|
1662
|
+
/**
|
|
1663
|
+
* Fields valid for the `$currentDate` operator — only Date-typed fields.
|
|
1664
|
+
*
|
|
1665
|
+
* Sets the field to the current date. Value is `true` or `{ $type: 'date' }`.
|
|
1666
|
+
*
|
|
1667
|
+
* @example
|
|
1668
|
+
* ```ts
|
|
1669
|
+
* const cd: CurrentDateFields<User> = { createdAt: true }
|
|
1670
|
+
* ```
|
|
1671
|
+
*/
|
|
1672
|
+
type CurrentDateFields<T> = {
|
|
1673
|
+
[K in keyof T as NonNullable<T[K]> extends Date ? K : never]?: true | {
|
|
1674
|
+
$type: 'date';
|
|
1675
|
+
};
|
|
1676
|
+
};
|
|
1677
|
+
/**
|
|
1678
|
+
* Fields valid for the `$rename` operator.
|
|
1679
|
+
*
|
|
1680
|
+
* Renames an existing field to a new name. Key is the current field name,
|
|
1681
|
+
* value is the new name as a string.
|
|
1682
|
+
*
|
|
1683
|
+
* @example
|
|
1684
|
+
* ```ts
|
|
1685
|
+
* const rename: RenameFields<User> = { name: 'fullName' }
|
|
1686
|
+
* ```
|
|
1687
|
+
*/
|
|
1688
|
+
type RenameFields<T> = {
|
|
1689
|
+
[K in keyof T & string]?: string;
|
|
1690
|
+
};
|
|
1691
|
+
/**
|
|
1692
|
+
* Strict type-safe MongoDB update filter.
|
|
1693
|
+
*
|
|
1694
|
+
* Validates update operators against field types at compile time. Each operator
|
|
1695
|
+
* constrains which fields it accepts (e.g. `$inc` only on numbers, `$push` only
|
|
1696
|
+
* on arrays). Supports dot-notation paths for nested field access.
|
|
1697
|
+
*
|
|
1698
|
+
* @example
|
|
1699
|
+
* ```ts
|
|
1700
|
+
* const update: TypedUpdateFilter<User> = {
|
|
1701
|
+
* $set: { name: 'Alice' },
|
|
1702
|
+
* $inc: { age: 1 },
|
|
1703
|
+
* }
|
|
1704
|
+
* ```
|
|
1705
|
+
*/
|
|
1706
|
+
type TypedUpdateFilter<T> = {
|
|
1707
|
+
/** Sets the value of one or more fields. */
|
|
1708
|
+
$set?: SetFields<T>;
|
|
1709
|
+
/** Sets fields only when inserting (upsert). Same typing as `$set`. */
|
|
1710
|
+
$setOnInsert?: SetFields<T>;
|
|
1711
|
+
/** Increments numeric fields by the given amount. Only accepts number-typed fields. */
|
|
1712
|
+
$inc?: IncFields<T>;
|
|
1713
|
+
/** Updates the field if the given value is less than the current value. */
|
|
1714
|
+
$min?: SetFields<T>;
|
|
1715
|
+
/** Updates the field if the given value is greater than the current value. */
|
|
1716
|
+
$max?: SetFields<T>;
|
|
1717
|
+
/** Appends a value to an array field. Supports `$each`, `$position`, `$slice`, `$sort`. */
|
|
1718
|
+
$push?: PushFields<T>;
|
|
1719
|
+
/** Removes matching values from an array field. */
|
|
1720
|
+
$pull?: PullFields<T>;
|
|
1721
|
+
/** Adds a value to an array only if it doesn't already exist. */
|
|
1722
|
+
$addToSet?: AddToSetFields<T>;
|
|
1723
|
+
/** Removes the first (`-1`) or last (`1`) element of an array. */
|
|
1724
|
+
$pop?: PopFields<T>;
|
|
1725
|
+
/** Removes the specified fields from the document. */
|
|
1726
|
+
$unset?: UnsetFields<T>;
|
|
1727
|
+
/** Sets the field to the current date. Only accepts Date-typed fields. */
|
|
1728
|
+
$currentDate?: CurrentDateFields<T>;
|
|
1729
|
+
/** Renames a field. */
|
|
1730
|
+
$rename?: RenameFields<T>;
|
|
1731
|
+
};
|
|
1732
|
+
|
|
1733
|
+
/**
|
|
1734
|
+
* Options for {@link updateOne} and {@link updateMany}.
|
|
1735
|
+
*/
|
|
1736
|
+
type UpdateOptions = {
|
|
1737
|
+
/** When `true`, inserts a new document if no document matches the filter. */
|
|
1738
|
+
upsert?: boolean;
|
|
1739
|
+
};
|
|
1740
|
+
/**
|
|
1741
|
+
* Options for {@link findOneAndUpdate}.
|
|
1742
|
+
*/
|
|
1743
|
+
type FindOneAndUpdateOptions = {
|
|
1744
|
+
/** Whether to return the document before or after the update. Defaults to `'after'`. */
|
|
1745
|
+
returnDocument?: 'before' | 'after';
|
|
1746
|
+
/** When `true`, inserts a new document if no document matches the filter. */
|
|
1747
|
+
upsert?: boolean;
|
|
1748
|
+
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
1749
|
+
validate?: ValidationMode | false;
|
|
1750
|
+
};
|
|
1751
|
+
/**
|
|
1752
|
+
* Update a single document matching the filter.
|
|
1753
|
+
*
|
|
1754
|
+
* Applies the update operators to the first document that matches the filter.
|
|
1755
|
+
* Does not validate the update against the Zod schema — validation happens
|
|
1756
|
+
* at the field-operator level through {@link TypedUpdateFilter}.
|
|
1757
|
+
*
|
|
1758
|
+
* @param handle - The collection handle to update in.
|
|
1759
|
+
* @param filter - Type-safe filter to match documents.
|
|
1760
|
+
* @param update - Type-safe update operators to apply.
|
|
1761
|
+
* @param options - Optional settings such as `upsert`.
|
|
1762
|
+
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
1763
|
+
*
|
|
1764
|
+
* @example
|
|
1765
|
+
* ```ts
|
|
1766
|
+
* const result = await updateOne(users, { name: 'Ada' }, { $set: { role: 'admin' } })
|
|
1767
|
+
* console.log(result.modifiedCount) // 1
|
|
1768
|
+
* ```
|
|
1769
|
+
*/
|
|
1770
|
+
declare function updateOne<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, update: TypedUpdateFilter<InferDocument<TDef>>, options?: UpdateOptions): Promise<UpdateResult>;
|
|
1771
|
+
/**
|
|
1772
|
+
* Update all documents matching the filter.
|
|
1773
|
+
*
|
|
1774
|
+
* Applies the update operators to every document that matches the filter.
|
|
1775
|
+
* Does not validate the update against the Zod schema — validation happens
|
|
1776
|
+
* at the field-operator level through {@link TypedUpdateFilter}.
|
|
1777
|
+
*
|
|
1778
|
+
* @param handle - The collection handle to update in.
|
|
1779
|
+
* @param filter - Type-safe filter to match documents.
|
|
1780
|
+
* @param update - Type-safe update operators to apply.
|
|
1781
|
+
* @param options - Optional settings such as `upsert`.
|
|
1782
|
+
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
1783
|
+
*
|
|
1784
|
+
* @example
|
|
1785
|
+
* ```ts
|
|
1786
|
+
* const result = await updateMany(users, { role: 'guest' }, { $set: { role: 'user' } })
|
|
1787
|
+
* console.log(result.modifiedCount) // number of guests promoted
|
|
1788
|
+
* ```
|
|
1789
|
+
*/
|
|
1790
|
+
declare function updateMany<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, update: TypedUpdateFilter<InferDocument<TDef>>, options?: UpdateOptions): Promise<UpdateResult>;
|
|
1791
|
+
/**
|
|
1792
|
+
* Find a single document matching the filter, apply an update, and return the document.
|
|
1793
|
+
*
|
|
1794
|
+
* By default, returns the document **after** the update is applied. Set
|
|
1795
|
+
* `returnDocument: 'before'` to get the pre-update snapshot. The returned
|
|
1796
|
+
* document is validated against the collection's Zod schema using the same
|
|
1797
|
+
* resolution logic as {@link findOne}.
|
|
1798
|
+
*
|
|
1799
|
+
* @param handle - The collection handle to update in.
|
|
1800
|
+
* @param filter - Type-safe filter to match documents.
|
|
1801
|
+
* @param update - Type-safe update operators to apply.
|
|
1802
|
+
* @param options - Optional settings: `returnDocument`, `upsert`, `validate`.
|
|
1803
|
+
* @returns The matched document (before or after update), or `null` if no document matches.
|
|
1804
|
+
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
1805
|
+
*
|
|
1806
|
+
* @example
|
|
1807
|
+
* ```ts
|
|
1808
|
+
* const user = await findOneAndUpdate(
|
|
1809
|
+
* users,
|
|
1810
|
+
* { name: 'Ada' },
|
|
1811
|
+
* { $set: { role: 'admin' } },
|
|
1812
|
+
* )
|
|
1813
|
+
* if (user) console.log(user.role) // 'admin' (returned after update)
|
|
1814
|
+
* ```
|
|
1815
|
+
*
|
|
1816
|
+
* @example
|
|
1817
|
+
* ```ts
|
|
1818
|
+
* const before = await findOneAndUpdate(
|
|
1819
|
+
* users,
|
|
1820
|
+
* { name: 'Ada' },
|
|
1821
|
+
* { $inc: { loginCount: 1 } },
|
|
1822
|
+
* { returnDocument: 'before' },
|
|
1823
|
+
* )
|
|
1824
|
+
* ```
|
|
1825
|
+
*/
|
|
1826
|
+
declare function findOneAndUpdate<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, update: TypedUpdateFilter<InferDocument<TDef>>, options?: FindOneAndUpdateOptions): Promise<InferDocument<TDef> | null>;
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* Options controlling how {@link syncIndexes} behaves.
|
|
1830
|
+
*
|
|
1831
|
+
* @example
|
|
1832
|
+
* ```ts
|
|
1833
|
+
* await users.syncIndexes({ dryRun: true })
|
|
1834
|
+
* await users.syncIndexes({ dropOrphaned: true })
|
|
1835
|
+
* ```
|
|
1836
|
+
*/
|
|
1837
|
+
type SyncIndexesOptions = {
|
|
1838
|
+
/**
|
|
1839
|
+
* When `true`, compute the diff without actually creating, dropping, or
|
|
1840
|
+
* modifying any indexes. The returned {@link SyncIndexesResult} shows what
|
|
1841
|
+
* *would* happen.
|
|
1842
|
+
*/
|
|
1843
|
+
dryRun?: boolean;
|
|
1844
|
+
/**
|
|
1845
|
+
* When `true`, drop indexes that exist in MongoDB but are not declared in
|
|
1846
|
+
* the schema. Also drops and recreates stale indexes (same key, different
|
|
1847
|
+
* options). When `false` (the default), orphaned and stale indexes are
|
|
1848
|
+
* reported but left untouched.
|
|
1849
|
+
*/
|
|
1850
|
+
dropOrphaned?: boolean;
|
|
1851
|
+
};
|
|
1852
|
+
/**
|
|
1853
|
+
* Describes an index whose key matches a desired index but whose options differ.
|
|
1854
|
+
*
|
|
1855
|
+
* Returned in {@link SyncIndexesResult.stale} so the caller can decide whether
|
|
1856
|
+
* to manually reconcile or re-run with `dropOrphaned: true`.
|
|
1857
|
+
*
|
|
1858
|
+
* @example
|
|
1859
|
+
* ```ts
|
|
1860
|
+
* const result = await users.syncIndexes()
|
|
1861
|
+
* for (const s of result.stale) {
|
|
1862
|
+
* console.log(`${s.name}: key=${JSON.stringify(s.key)}`)
|
|
1863
|
+
* console.log(` existing=${JSON.stringify(s.existing)}`)
|
|
1864
|
+
* console.log(` desired=${JSON.stringify(s.desired)}`)
|
|
1865
|
+
* }
|
|
1866
|
+
* ```
|
|
1867
|
+
*/
|
|
1868
|
+
type StaleIndex = {
|
|
1869
|
+
/** The MongoDB index name (e.g. `'email_1'`). */
|
|
1870
|
+
name: string;
|
|
1871
|
+
/** The index key spec (e.g. `{ email: 1 }`). */
|
|
1872
|
+
key: Record<string, 1 | -1 | 'text'>;
|
|
1873
|
+
/** The relevant options currently set on the existing index. */
|
|
1874
|
+
existing: Record<string, unknown>;
|
|
1875
|
+
/** The options the schema declares for this index. */
|
|
1876
|
+
desired: Record<string, unknown>;
|
|
1877
|
+
};
|
|
1878
|
+
/**
|
|
1879
|
+
* The result of a {@link syncIndexes} call.
|
|
1880
|
+
*
|
|
1881
|
+
* Every array contains index names. `stale` contains full details so the
|
|
1882
|
+
* caller can inspect the mismatch.
|
|
1883
|
+
*
|
|
1884
|
+
* @example
|
|
1885
|
+
* ```ts
|
|
1886
|
+
* const result = await users.syncIndexes()
|
|
1887
|
+
* console.log('created:', result.created)
|
|
1888
|
+
* console.log('dropped:', result.dropped)
|
|
1889
|
+
* console.log('skipped:', result.skipped)
|
|
1890
|
+
* console.log('stale:', result.stale.map(s => s.name))
|
|
1891
|
+
* ```
|
|
1892
|
+
*/
|
|
1893
|
+
type SyncIndexesResult = {
|
|
1894
|
+
/** Names of indexes that were created (or would be created in dryRun mode). */
|
|
1895
|
+
created: string[];
|
|
1896
|
+
/** Names of indexes that were dropped (or would be dropped in dryRun mode). */
|
|
1897
|
+
dropped: string[];
|
|
1898
|
+
/** Names of indexes that already existed with matching options — no action taken. */
|
|
1899
|
+
skipped: string[];
|
|
1900
|
+
/** Indexes whose key matches a desired spec but whose options differ. */
|
|
1901
|
+
stale: StaleIndex[];
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
/**
|
|
1905
|
+
* Typed wrapper around a MongoDB driver `Collection`.
|
|
1906
|
+
*
|
|
1907
|
+
* Created by {@link Database.use}. Holds the original `CollectionDefinition`
|
|
1908
|
+
* (for runtime schema validation and index metadata) alongside the native
|
|
1909
|
+
* driver collection parameterized with the inferred document type.
|
|
1910
|
+
*
|
|
1911
|
+
* @typeParam TDef - The collection definition type. Used to derive both
|
|
1912
|
+
* the document type (`InferDocument`) and the insert type (`InferInsert`).
|
|
1913
|
+
*/
|
|
1914
|
+
declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
1915
|
+
/** The collection definition containing schema, name, and index metadata. */
|
|
1916
|
+
readonly definition: TDef;
|
|
1917
|
+
/** The underlying MongoDB driver collection, typed to the inferred document type. */
|
|
1918
|
+
readonly native: Collection<InferDocument<TDef>>;
|
|
1919
|
+
constructor(definition: TDef, native: Collection<InferDocument<TDef>>);
|
|
1920
|
+
/**
|
|
1921
|
+
* Insert a single document into the collection.
|
|
1922
|
+
*
|
|
1923
|
+
* Validates the input against the collection's Zod schema before writing.
|
|
1924
|
+
* Schema defaults (including auto-generated `_id`) are applied during
|
|
1925
|
+
* validation. Returns the full document with all defaults filled in.
|
|
1926
|
+
*
|
|
1927
|
+
* @param doc - The document to insert. Fields with `.default()` are optional.
|
|
1928
|
+
* @returns The inserted document with `_id` and all defaults applied.
|
|
1929
|
+
* @throws {ZodmonValidationError} When the document fails schema validation.
|
|
1930
|
+
*
|
|
1931
|
+
* @example
|
|
1932
|
+
* ```ts
|
|
1933
|
+
* const users = db.use(Users)
|
|
1934
|
+
* const user = await users.insertOne({ name: 'Ada' })
|
|
1935
|
+
* console.log(user._id) // ObjectId (auto-generated)
|
|
1936
|
+
* console.log(user.role) // 'user' (schema default)
|
|
1937
|
+
* ```
|
|
1938
|
+
*/
|
|
1939
|
+
insertOne(doc: InferInsert<TDef>): Promise<InferDocument<TDef>>;
|
|
1940
|
+
/**
|
|
1941
|
+
* Insert multiple documents into the collection.
|
|
1942
|
+
*
|
|
1943
|
+
* Validates every document against the collection's Zod schema before
|
|
1944
|
+
* writing any to MongoDB. If any document fails validation, none are
|
|
1945
|
+
* inserted (fail-fast before the driver call).
|
|
1946
|
+
*
|
|
1947
|
+
* @param docs - The documents to insert.
|
|
1948
|
+
* @returns The inserted documents with `_id` and all defaults applied.
|
|
1949
|
+
* @throws {ZodmonValidationError} When any document fails schema validation.
|
|
1950
|
+
*
|
|
1951
|
+
* @example
|
|
1952
|
+
* ```ts
|
|
1953
|
+
* const created = await users.insertMany([
|
|
1954
|
+
* { name: 'Ada' },
|
|
1955
|
+
* { name: 'Bob', role: 'admin' },
|
|
1956
|
+
* ])
|
|
1957
|
+
* ```
|
|
1958
|
+
*/
|
|
1959
|
+
insertMany(docs: InferInsert<TDef>[]): Promise<InferDocument<TDef>[]>;
|
|
1960
|
+
/**
|
|
1961
|
+
* Find a single document matching the filter.
|
|
1962
|
+
*
|
|
1963
|
+
* Queries MongoDB, then validates the fetched document against the collection's
|
|
1964
|
+
* Zod schema. Validation mode is resolved from the per-query option, falling
|
|
1965
|
+
* back to the collection-level default (which defaults to `'strict'`).
|
|
1966
|
+
*
|
|
1967
|
+
* @param filter - Type-safe filter to match documents.
|
|
1968
|
+
* @param options - Optional projection and validation overrides.
|
|
1969
|
+
* @returns The matched document, or `null` if no document matches.
|
|
1970
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1971
|
+
*
|
|
1972
|
+
* @example
|
|
1973
|
+
* ```ts
|
|
1974
|
+
* const users = db.use(Users)
|
|
1975
|
+
* const user = await users.findOne({ name: 'Ada' })
|
|
1976
|
+
* if (user) console.log(user.role)
|
|
1977
|
+
* ```
|
|
1978
|
+
*/
|
|
1979
|
+
findOne(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef> | null>;
|
|
1980
|
+
/**
|
|
1981
|
+
* Find a single document matching the filter, or throw if none exists.
|
|
1982
|
+
*
|
|
1983
|
+
* Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}
|
|
1984
|
+
* instead of returning `null` when no document matches the filter.
|
|
1985
|
+
*
|
|
1986
|
+
* @param filter - Type-safe filter to match documents.
|
|
1987
|
+
* @param options - Optional projection and validation overrides.
|
|
1988
|
+
* @returns The matched document (never null).
|
|
1989
|
+
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
1990
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1991
|
+
*
|
|
1992
|
+
* @example
|
|
1993
|
+
* ```ts
|
|
1994
|
+
* const users = db.use(Users)
|
|
1995
|
+
* const user = await users.findOneOrThrow({ name: 'Ada' })
|
|
1996
|
+
* console.log(user.role) // guaranteed non-null
|
|
1997
|
+
* ```
|
|
1998
|
+
*/
|
|
1999
|
+
findOneOrThrow(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
|
|
2000
|
+
/**
|
|
2001
|
+
* Find all documents matching the filter, returning a chainable typed cursor.
|
|
2002
|
+
*
|
|
2003
|
+
* The cursor is lazy — no query is executed until a terminal method
|
|
2004
|
+
* (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
|
|
2005
|
+
* to shape the query before executing.
|
|
2006
|
+
*
|
|
2007
|
+
* @param filter - Type-safe filter to match documents.
|
|
2008
|
+
* @param options - Optional validation overrides.
|
|
2009
|
+
* @returns A typed cursor for chaining query modifiers.
|
|
2010
|
+
*
|
|
2011
|
+
* @example
|
|
2012
|
+
* ```ts
|
|
2013
|
+
* const users = db.use(Users)
|
|
2014
|
+
* const admins = await users.find({ role: 'admin' })
|
|
2015
|
+
* .sort({ name: 1 })
|
|
2016
|
+
* .limit(10)
|
|
2017
|
+
* .toArray()
|
|
2018
|
+
* ```
|
|
2019
|
+
*/
|
|
2020
|
+
find(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef, IndexNames<TDef>>;
|
|
2021
|
+
/**
|
|
2022
|
+
* Update a single document matching the filter.
|
|
2023
|
+
*
|
|
2024
|
+
* Applies the update operators to the first document that matches the filter.
|
|
2025
|
+
* Does not validate the update against the Zod schema — validation happens
|
|
2026
|
+
* at the field-operator level through {@link TypedUpdateFilter}.
|
|
2027
|
+
*
|
|
2028
|
+
* @param filter - Type-safe filter to match documents.
|
|
2029
|
+
* @param update - Type-safe update operators to apply.
|
|
2030
|
+
* @param options - Optional settings such as `upsert`.
|
|
2031
|
+
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
2032
|
+
*
|
|
2033
|
+
* @example
|
|
2034
|
+
* ```ts
|
|
2035
|
+
* const users = db.use(Users)
|
|
2036
|
+
* const result = await users.updateOne({ name: 'Ada' }, { $set: { role: 'admin' } })
|
|
2037
|
+
* console.log(result.modifiedCount) // 1
|
|
2038
|
+
* ```
|
|
2039
|
+
*/
|
|
2040
|
+
updateOne(filter: TypedFilter<InferDocument<TDef>>, update: TypedUpdateFilter<InferDocument<TDef>>, options?: UpdateOptions): Promise<UpdateResult>;
|
|
2041
|
+
/**
|
|
2042
|
+
* Update all documents matching the filter.
|
|
2043
|
+
*
|
|
2044
|
+
* Applies the update operators to every document that matches the filter.
|
|
2045
|
+
* Does not validate the update against the Zod schema — validation happens
|
|
2046
|
+
* at the field-operator level through {@link TypedUpdateFilter}.
|
|
2047
|
+
*
|
|
2048
|
+
* @param filter - Type-safe filter to match documents.
|
|
2049
|
+
* @param update - Type-safe update operators to apply.
|
|
2050
|
+
* @param options - Optional settings such as `upsert`.
|
|
2051
|
+
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
2052
|
+
*
|
|
2053
|
+
* @example
|
|
2054
|
+
* ```ts
|
|
2055
|
+
* const users = db.use(Users)
|
|
2056
|
+
* const result = await users.updateMany({ role: 'guest' }, { $set: { role: 'user' } })
|
|
2057
|
+
* console.log(result.modifiedCount) // number of guests promoted
|
|
2058
|
+
* ```
|
|
2059
|
+
*/
|
|
2060
|
+
updateMany(filter: TypedFilter<InferDocument<TDef>>, update: TypedUpdateFilter<InferDocument<TDef>>, options?: UpdateOptions): Promise<UpdateResult>;
|
|
2061
|
+
/**
|
|
2062
|
+
* Find a single document matching the filter, apply an update, and return the document.
|
|
2063
|
+
*
|
|
2064
|
+
* By default, returns the document **after** the update is applied. Set
|
|
2065
|
+
* `returnDocument: 'before'` to get the pre-update snapshot. The returned
|
|
2066
|
+
* document is validated against the collection's Zod schema using the same
|
|
2067
|
+
* resolution logic as {@link findOne}.
|
|
2068
|
+
*
|
|
2069
|
+
* @param filter - Type-safe filter to match documents.
|
|
2070
|
+
* @param update - Type-safe update operators to apply.
|
|
2071
|
+
* @param options - Optional settings: `returnDocument`, `upsert`, `validate`.
|
|
2072
|
+
* @returns The matched document (before or after update), or `null` if no document matches.
|
|
2073
|
+
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
2074
|
+
*
|
|
2075
|
+
* @example
|
|
2076
|
+
* ```ts
|
|
2077
|
+
* const users = db.use(Users)
|
|
2078
|
+
* const user = await users.findOneAndUpdate(
|
|
2079
|
+
* { name: 'Ada' },
|
|
2080
|
+
* { $set: { role: 'admin' } },
|
|
2081
|
+
* )
|
|
2082
|
+
* if (user) console.log(user.role) // 'admin' (returned after update)
|
|
2083
|
+
* ```
|
|
2084
|
+
*/
|
|
2085
|
+
findOneAndUpdate(filter: TypedFilter<InferDocument<TDef>>, update: TypedUpdateFilter<InferDocument<TDef>>, options?: FindOneAndUpdateOptions): Promise<InferDocument<TDef> | null>;
|
|
2086
|
+
/**
|
|
2087
|
+
* Delete a single document matching the filter.
|
|
2088
|
+
*
|
|
2089
|
+
* Removes the first document that matches the filter from the collection.
|
|
2090
|
+
* No validation is performed — the document is deleted directly through
|
|
2091
|
+
* the MongoDB driver.
|
|
2092
|
+
*
|
|
2093
|
+
* @param filter - Type-safe filter to match documents.
|
|
2094
|
+
* @returns The MongoDB `DeleteResult` with the deleted count.
|
|
2095
|
+
*
|
|
2096
|
+
* @example
|
|
2097
|
+
* ```ts
|
|
2098
|
+
* const users = db.use(Users)
|
|
2099
|
+
* const result = await users.deleteOne({ name: 'Ada' })
|
|
2100
|
+
* console.log(result.deletedCount) // 1
|
|
2101
|
+
* ```
|
|
2102
|
+
*/
|
|
2103
|
+
deleteOne(filter: TypedFilter<InferDocument<TDef>>): Promise<DeleteResult>;
|
|
2104
|
+
/**
|
|
2105
|
+
* Delete all documents matching the filter.
|
|
2106
|
+
*
|
|
2107
|
+
* Removes every document that matches the filter from the collection.
|
|
2108
|
+
* No validation is performed — documents are deleted directly through
|
|
2109
|
+
* the MongoDB driver.
|
|
2110
|
+
*
|
|
2111
|
+
* @param filter - Type-safe filter to match documents.
|
|
2112
|
+
* @returns The MongoDB `DeleteResult` with the deleted count.
|
|
2113
|
+
*
|
|
2114
|
+
* @example
|
|
2115
|
+
* ```ts
|
|
2116
|
+
* const users = db.use(Users)
|
|
2117
|
+
* const result = await users.deleteMany({ role: 'guest' })
|
|
2118
|
+
* console.log(result.deletedCount) // number of guests removed
|
|
2119
|
+
* ```
|
|
2120
|
+
*/
|
|
2121
|
+
deleteMany(filter: TypedFilter<InferDocument<TDef>>): Promise<DeleteResult>;
|
|
2122
|
+
/**
|
|
2123
|
+
* Find a single document matching the filter, delete it, and return the document.
|
|
2124
|
+
*
|
|
2125
|
+
* Returns the deleted document, or `null` if no document matches the filter.
|
|
2126
|
+
* The returned document is validated against the collection's Zod schema
|
|
2127
|
+
* using the same resolution logic as {@link findOne}.
|
|
2128
|
+
*
|
|
2129
|
+
* @param filter - Type-safe filter to match documents.
|
|
2130
|
+
* @param options - Optional settings: `validate`.
|
|
2131
|
+
* @returns The deleted document, or `null` if no document matches.
|
|
2132
|
+
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
2133
|
+
*
|
|
2134
|
+
* @example
|
|
2135
|
+
* ```ts
|
|
2136
|
+
* const users = db.use(Users)
|
|
2137
|
+
* const user = await users.findOneAndDelete({ name: 'Ada' })
|
|
2138
|
+
* if (user) console.log(user.name) // 'Ada' (the deleted document)
|
|
2139
|
+
* ```
|
|
2140
|
+
*/
|
|
2141
|
+
findOneAndDelete(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneAndDeleteOptions): Promise<InferDocument<TDef> | null>;
|
|
2142
|
+
/**
|
|
2143
|
+
* Synchronize the indexes declared in this collection's schema with MongoDB.
|
|
2144
|
+
*
|
|
2145
|
+
* Compares the desired indexes (from field-level `.index()` / `.unique()` /
|
|
2146
|
+
* `.text()` / `.expireAfter()` and compound `indexes` in collection options)
|
|
2147
|
+
* with the indexes that currently exist in MongoDB, then creates, drops, or
|
|
2148
|
+
* reports differences depending on the options.
|
|
2149
|
+
*
|
|
2150
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
2151
|
+
* @returns A summary of created, dropped, skipped, and stale indexes.
|
|
2152
|
+
*
|
|
2153
|
+
* @example
|
|
2154
|
+
* ```ts
|
|
2155
|
+
* const users = db.use(Users)
|
|
2156
|
+
* const result = await users.syncIndexes()
|
|
2157
|
+
* console.log('Created:', result.created)
|
|
2158
|
+
* console.log('Stale:', result.stale.map(s => s.name))
|
|
2159
|
+
* ```
|
|
2160
|
+
*
|
|
2161
|
+
* @example
|
|
2162
|
+
* ```ts
|
|
2163
|
+
* // Dry run to preview changes without modifying the database
|
|
2164
|
+
* const diff = await users.syncIndexes({ dryRun: true })
|
|
2165
|
+
* console.log('Would create:', diff.created)
|
|
2166
|
+
* console.log('Would drop:', diff.dropped)
|
|
2167
|
+
* ```
|
|
2168
|
+
*/
|
|
2169
|
+
syncIndexes(options?: SyncIndexesOptions): Promise<SyncIndexesResult>;
|
|
2170
|
+
/**
|
|
2171
|
+
* Start a type-safe aggregation pipeline on this collection.
|
|
2172
|
+
*
|
|
2173
|
+
* Returns a fluent pipeline builder that tracks the output document
|
|
2174
|
+
* shape through each stage. The pipeline is lazy — no query executes
|
|
2175
|
+
* until a terminal method (`toArray`, `for await`, `explain`) is called.
|
|
2176
|
+
*
|
|
2177
|
+
* @returns A new pipeline builder starting with this collection's document type.
|
|
2178
|
+
*
|
|
2179
|
+
* @example
|
|
2180
|
+
* ```ts
|
|
2181
|
+
* const users = db.use(Users)
|
|
2182
|
+
* const result = await users.aggregate()
|
|
2183
|
+
* .match({ role: 'admin' })
|
|
2184
|
+
* .groupBy('role', { count: $count() })
|
|
2185
|
+
* .toArray()
|
|
2186
|
+
* ```
|
|
2187
|
+
*/
|
|
2188
|
+
aggregate(): AggregatePipeline<TDef, InferDocument<TDef>>;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
/**
|
|
2192
|
+
* Immutable aggregation pipeline builder for type-safe MongoDB aggregations.
|
|
1072
2193
|
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
* resolution logic as {@link findOne}.
|
|
2194
|
+
* Each stage method returns a **new** `AggregatePipeline` instance — the
|
|
2195
|
+
* original is never mutated. This makes it safe to branch from a shared base
|
|
2196
|
+
* pipeline without cross-contamination.
|
|
1077
2197
|
*
|
|
1078
|
-
* @
|
|
1079
|
-
*
|
|
1080
|
-
* @param update - Type-safe update operators to apply.
|
|
1081
|
-
* @param options - Optional settings: `returnDocument`, `upsert`, `validate`.
|
|
1082
|
-
* @returns The matched document (before or after update), or `null` if no document matches.
|
|
1083
|
-
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
2198
|
+
* Use {@link aggregate} to create a pipeline from a {@link CollectionHandle},
|
|
2199
|
+
* or call `raw()` to append arbitrary stages.
|
|
1084
2200
|
*
|
|
1085
|
-
* @
|
|
1086
|
-
*
|
|
1087
|
-
* const user = await findOneAndUpdate(
|
|
1088
|
-
* users,
|
|
1089
|
-
* { name: 'Ada' },
|
|
1090
|
-
* { $set: { role: 'admin' } },
|
|
1091
|
-
* )
|
|
1092
|
-
* if (user) console.log(user.role) // 'admin' (returned after update)
|
|
1093
|
-
* ```
|
|
2201
|
+
* @typeParam TDef - The collection definition type, used to derive document types.
|
|
2202
|
+
* @typeParam TOutput - The current output document type (changes as stages transform the shape).
|
|
1094
2203
|
*
|
|
1095
2204
|
* @example
|
|
1096
2205
|
* ```ts
|
|
1097
|
-
* const
|
|
1098
|
-
*
|
|
1099
|
-
* { name:
|
|
1100
|
-
*
|
|
1101
|
-
* { returnDocument: 'before' },
|
|
1102
|
-
* )
|
|
2206
|
+
* const results = await aggregate(users)
|
|
2207
|
+
* .raw({ $match: { role: 'admin' } })
|
|
2208
|
+
* .raw({ $sort: { name: 1 } })
|
|
2209
|
+
* .toArray()
|
|
1103
2210
|
* ```
|
|
1104
2211
|
*/
|
|
1105
|
-
declare
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
* Created by {@link Database.use}. Holds the original `CollectionDefinition`
|
|
1111
|
-
* (for runtime schema validation and index metadata) alongside the native
|
|
1112
|
-
* driver collection parameterized with the inferred document type.
|
|
1113
|
-
*
|
|
1114
|
-
* @typeParam TDef - The collection definition type. Used to derive both
|
|
1115
|
-
* the document type (`InferDocument`) and the insert type (`InferInsert`).
|
|
1116
|
-
*/
|
|
1117
|
-
declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
1118
|
-
/** The collection definition containing schema, name, and index metadata. */
|
|
1119
|
-
readonly definition: TDef;
|
|
1120
|
-
/** The underlying MongoDB driver collection, typed to the inferred document type. */
|
|
1121
|
-
readonly native: Collection<InferDocument<TDef>>;
|
|
1122
|
-
constructor(definition: TDef, native: Collection<InferDocument<TDef>>);
|
|
2212
|
+
declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
2213
|
+
protected readonly definition: TDef;
|
|
2214
|
+
private readonly nativeCollection;
|
|
2215
|
+
private readonly stages;
|
|
2216
|
+
constructor(definition: TDef, nativeCollection: Collection<InferDocument<TDef>>, stages: Document[]);
|
|
1123
2217
|
/**
|
|
1124
|
-
*
|
|
2218
|
+
* Append an arbitrary aggregation stage to the pipeline (escape hatch).
|
|
1125
2219
|
*
|
|
1126
|
-
*
|
|
1127
|
-
*
|
|
1128
|
-
* validation. Returns the full document with all defaults filled in.
|
|
2220
|
+
* Returns a new pipeline instance with the stage appended — the
|
|
2221
|
+
* original pipeline is not modified.
|
|
1129
2222
|
*
|
|
1130
|
-
*
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
2223
|
+
* Optionally accepts a type parameter `TNew` to change the output
|
|
2224
|
+
* type when the stage transforms the document shape.
|
|
2225
|
+
*
|
|
2226
|
+
* @typeParam TNew - The output type after this stage. Defaults to the current output type.
|
|
2227
|
+
* @param stage - A raw MongoDB aggregation stage document (e.g. `{ $match: { ... } }`).
|
|
2228
|
+
* @returns A new pipeline with the stage appended.
|
|
1133
2229
|
*
|
|
1134
2230
|
* @example
|
|
1135
2231
|
* ```ts
|
|
1136
|
-
* const
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
*
|
|
2232
|
+
* const admins = aggregate(users)
|
|
2233
|
+
* .raw({ $match: { role: 'admin' } })
|
|
2234
|
+
* .toArray()
|
|
2235
|
+
* ```
|
|
2236
|
+
*
|
|
2237
|
+
* @example
|
|
2238
|
+
* ```ts
|
|
2239
|
+
* // Change output type with a $project stage
|
|
2240
|
+
* const names = aggregate(users)
|
|
2241
|
+
* .raw<{ name: string }>({ $project: { name: 1, _id: 0 } })
|
|
2242
|
+
* .toArray()
|
|
1140
2243
|
* ```
|
|
1141
2244
|
*/
|
|
1142
|
-
|
|
2245
|
+
raw<TNew = TOutput>(stage: Document): AggregatePipeline<TDef, TNew>;
|
|
1143
2246
|
/**
|
|
1144
|
-
*
|
|
2247
|
+
* Execute the pipeline and return all results as an array.
|
|
1145
2248
|
*
|
|
1146
|
-
*
|
|
1147
|
-
* writing any to MongoDB. If any document fails validation, none are
|
|
1148
|
-
* inserted (fail-fast before the driver call).
|
|
2249
|
+
* @returns A promise resolving to the array of output documents.
|
|
1149
2250
|
*
|
|
1150
|
-
* @
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
2251
|
+
* @example
|
|
2252
|
+
* ```ts
|
|
2253
|
+
* const results = await aggregate(users)
|
|
2254
|
+
* .raw({ $match: { age: { $gte: 18 } } })
|
|
2255
|
+
* .toArray()
|
|
2256
|
+
* ```
|
|
2257
|
+
*/
|
|
2258
|
+
toArray(): Promise<TOutput[]>;
|
|
2259
|
+
/**
|
|
2260
|
+
* Stream pipeline results one document at a time via `for await...of`.
|
|
2261
|
+
*
|
|
2262
|
+
* @returns An async generator yielding output documents.
|
|
1153
2263
|
*
|
|
1154
2264
|
* @example
|
|
1155
2265
|
* ```ts
|
|
1156
|
-
* const
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
* ])
|
|
2266
|
+
* for await (const user of aggregate(users).raw({ $match: { role: 'admin' } })) {
|
|
2267
|
+
* console.log(user.name)
|
|
2268
|
+
* }
|
|
1160
2269
|
* ```
|
|
1161
2270
|
*/
|
|
1162
|
-
|
|
2271
|
+
[Symbol.asyncIterator](): AsyncGenerator<TOutput>;
|
|
1163
2272
|
/**
|
|
1164
|
-
*
|
|
2273
|
+
* Return the query execution plan without running the pipeline.
|
|
1165
2274
|
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
* back to the collection-level default (which defaults to `'strict'`).
|
|
2275
|
+
* Useful for debugging and understanding how MongoDB will process
|
|
2276
|
+
* the pipeline stages.
|
|
1169
2277
|
*
|
|
1170
|
-
* @
|
|
1171
|
-
* @param options - Optional projection and validation overrides.
|
|
1172
|
-
* @returns The matched document, or `null` if no document matches.
|
|
1173
|
-
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
2278
|
+
* @returns A promise resolving to the explain output document.
|
|
1174
2279
|
*
|
|
1175
2280
|
* @example
|
|
1176
2281
|
* ```ts
|
|
1177
|
-
* const
|
|
1178
|
-
*
|
|
1179
|
-
*
|
|
2282
|
+
* const plan = await aggregate(users)
|
|
2283
|
+
* .raw({ $match: { role: 'admin' } })
|
|
2284
|
+
* .explain()
|
|
2285
|
+
* console.log(plan)
|
|
1180
2286
|
* ```
|
|
1181
2287
|
*/
|
|
1182
|
-
|
|
2288
|
+
explain(): Promise<Document>;
|
|
1183
2289
|
/**
|
|
1184
|
-
*
|
|
2290
|
+
* Filter documents using a type-safe match expression.
|
|
1185
2291
|
*
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
2292
|
+
* Appends a `$match` stage to the pipeline. The filter is constrained
|
|
2293
|
+
* to the current output type, so only valid fields and operators are accepted.
|
|
1188
2294
|
*
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1191
|
-
*
|
|
1192
|
-
*
|
|
1193
|
-
*
|
|
2295
|
+
* Supports two forms of type narrowing:
|
|
2296
|
+
*
|
|
2297
|
+
* **Tier 1 — Explicit type parameter:**
|
|
2298
|
+
* ```ts
|
|
2299
|
+
* .match<{ role: 'engineer' | 'designer' }>({ role: { $in: ['engineer', 'designer'] } })
|
|
2300
|
+
* // role narrows to 'engineer' | 'designer'
|
|
2301
|
+
* ```
|
|
2302
|
+
*
|
|
2303
|
+
* **Tier 2 — Automatic inference from filter literals:**
|
|
2304
|
+
* ```ts
|
|
2305
|
+
* .match({ role: 'engineer' }) // role narrows to 'engineer'
|
|
2306
|
+
* .match({ role: { $ne: 'intern' } }) // role narrows to Exclude<Role, 'intern'>
|
|
2307
|
+
* .match({ role: { $in: ['engineer', 'designer'] as const } }) // needs as const
|
|
2308
|
+
* ```
|
|
2309
|
+
*
|
|
2310
|
+
* When no type parameter is provided and the filter doesn't contain
|
|
2311
|
+
* inferrable literals, the output type is unchanged (backward compatible).
|
|
2312
|
+
*
|
|
2313
|
+
* @typeParam TNarrow - Optional object mapping field names to narrowed types. Must be a subtype of the corresponding fields in TOutput.
|
|
2314
|
+
* @typeParam F - Inferred from the filter argument. Do not provide explicitly.
|
|
2315
|
+
* @param filter - A type-safe filter for the current output type.
|
|
2316
|
+
* @returns A new pipeline with the `$match` stage appended and output type narrowed.
|
|
1194
2317
|
*
|
|
1195
2318
|
* @example
|
|
1196
2319
|
* ```ts
|
|
1197
|
-
*
|
|
1198
|
-
* const
|
|
1199
|
-
*
|
|
2320
|
+
* // Explicit narrowing
|
|
2321
|
+
* const filtered = await users.aggregate()
|
|
2322
|
+
* .match<{ role: 'engineer' }>({ role: 'engineer' })
|
|
2323
|
+
* .toArray()
|
|
2324
|
+
* // filtered[0].role → 'engineer'
|
|
2325
|
+
*
|
|
2326
|
+
* // Automatic narrowing with $in (requires as const)
|
|
2327
|
+
* const subset = await users.aggregate()
|
|
2328
|
+
* .match({ role: { $in: ['engineer', 'designer'] as const } })
|
|
2329
|
+
* .toArray()
|
|
2330
|
+
* // subset[0].role → 'engineer' | 'designer'
|
|
1200
2331
|
* ```
|
|
1201
2332
|
*/
|
|
1202
|
-
|
|
2333
|
+
match<TNarrow extends {
|
|
2334
|
+
[K in keyof TNarrow]: K extends keyof TOutput ? TOutput[K] : never;
|
|
2335
|
+
} = {}, F extends TypedFilter<TOutput> = TypedFilter<TOutput>>(filter: F): AggregatePipeline<TDef, Prettify<Omit<NarrowFromFilter<TOutput, F>, keyof TNarrow> & TNarrow>>;
|
|
1203
2336
|
/**
|
|
1204
|
-
*
|
|
2337
|
+
* Sort documents by one or more fields.
|
|
1205
2338
|
*
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
1208
|
-
* to shape the query before executing.
|
|
2339
|
+
* Appends a `$sort` stage. Keys are constrained to `keyof TOutput & string`
|
|
2340
|
+
* and values must be `1` (ascending) or `-1` (descending).
|
|
1209
2341
|
*
|
|
1210
|
-
* @param
|
|
1211
|
-
* @
|
|
1212
|
-
* @returns A typed cursor for chaining query modifiers.
|
|
2342
|
+
* @param spec - A sort specification mapping field names to sort direction.
|
|
2343
|
+
* @returns A new pipeline with the `$sort` stage appended.
|
|
1213
2344
|
*
|
|
1214
2345
|
* @example
|
|
1215
2346
|
* ```ts
|
|
1216
|
-
* const
|
|
1217
|
-
*
|
|
2347
|
+
* const sorted = await aggregate(users)
|
|
2348
|
+
* .sort({ age: -1, name: 1 })
|
|
2349
|
+
* .toArray()
|
|
2350
|
+
* ```
|
|
2351
|
+
*/
|
|
2352
|
+
sort(spec: Partial<Record<keyof TOutput & string, 1 | -1>>): AggregatePipeline<TDef, TOutput>;
|
|
2353
|
+
/**
|
|
2354
|
+
* Skip a number of documents in the pipeline.
|
|
2355
|
+
*
|
|
2356
|
+
* Appends a `$skip` stage. Commonly used with {@link limit} for pagination.
|
|
2357
|
+
*
|
|
2358
|
+
* @param n - The number of documents to skip.
|
|
2359
|
+
* @returns A new pipeline with the `$skip` stage appended.
|
|
2360
|
+
*
|
|
2361
|
+
* @example
|
|
2362
|
+
* ```ts
|
|
2363
|
+
* // Page 2 (10 items per page)
|
|
2364
|
+
* const page2 = await aggregate(users)
|
|
1218
2365
|
* .sort({ name: 1 })
|
|
2366
|
+
* .skip(10)
|
|
1219
2367
|
* .limit(10)
|
|
1220
2368
|
* .toArray()
|
|
1221
2369
|
* ```
|
|
1222
2370
|
*/
|
|
1223
|
-
|
|
2371
|
+
skip(n: number): AggregatePipeline<TDef, TOutput>;
|
|
1224
2372
|
/**
|
|
1225
|
-
*
|
|
2373
|
+
* Limit the number of documents passing through the pipeline.
|
|
1226
2374
|
*
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1229
|
-
* at the field-operator level through {@link TypedUpdateFilter}.
|
|
2375
|
+
* Appends a `$limit` stage. Commonly used with {@link skip} for pagination,
|
|
2376
|
+
* or after {@link sort} to get top/bottom N results.
|
|
1230
2377
|
*
|
|
1231
|
-
* @param
|
|
1232
|
-
* @
|
|
1233
|
-
* @param options - Optional settings such as `upsert`.
|
|
1234
|
-
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
2378
|
+
* @param n - The maximum number of documents to pass through.
|
|
2379
|
+
* @returns A new pipeline with the `$limit` stage appended.
|
|
1235
2380
|
*
|
|
1236
2381
|
* @example
|
|
1237
2382
|
* ```ts
|
|
1238
|
-
* const
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
2383
|
+
* const top5 = await aggregate(users)
|
|
2384
|
+
* .sort({ score: -1 })
|
|
2385
|
+
* .limit(5)
|
|
2386
|
+
* .toArray()
|
|
1241
2387
|
* ```
|
|
1242
2388
|
*/
|
|
1243
|
-
|
|
2389
|
+
limit(n: number): AggregatePipeline<TDef, TOutput>;
|
|
1244
2390
|
/**
|
|
1245
|
-
*
|
|
2391
|
+
* Include only specified fields in the output.
|
|
1246
2392
|
*
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
2393
|
+
* Appends a `$project` stage with inclusion (`1`) for each key.
|
|
2394
|
+
* The `_id` field is always included. The output type narrows to
|
|
2395
|
+
* `Pick<TOutput, K | '_id'>`.
|
|
1250
2396
|
*
|
|
1251
|
-
* @param
|
|
1252
|
-
* @
|
|
1253
|
-
* @param options - Optional settings such as `upsert`.
|
|
1254
|
-
* @returns The MongoDB `UpdateResult` with match/modify counts.
|
|
2397
|
+
* @param spec - An object mapping field names to `1` for inclusion.
|
|
2398
|
+
* @returns A new pipeline with the `$project` stage appended.
|
|
1255
2399
|
*
|
|
1256
2400
|
* @example
|
|
1257
2401
|
* ```ts
|
|
1258
|
-
* const
|
|
1259
|
-
*
|
|
1260
|
-
*
|
|
2402
|
+
* const namesOnly = await aggregate(users)
|
|
2403
|
+
* .project({ name: 1 })
|
|
2404
|
+
* .toArray()
|
|
2405
|
+
* // [{ _id: ..., name: 'Ada' }, ...]
|
|
2406
|
+
* ```
|
|
2407
|
+
*/
|
|
2408
|
+
project<K extends keyof TOutput & string>(spec: Record<K, 1>): AggregatePipeline<TDef, Prettify<Pick<TOutput, K | ('_id' extends keyof TOutput ? '_id' : never)>>>;
|
|
2409
|
+
/**
|
|
2410
|
+
* Variadic shorthand for {@link project} — pick fields to include.
|
|
2411
|
+
*
|
|
2412
|
+
* Generates a `$project` stage that includes only the listed fields
|
|
2413
|
+
* (plus `_id`). Equivalent to `.project({ field1: 1, field2: 1 })`.
|
|
2414
|
+
*
|
|
2415
|
+
* @param fields - Field names to include in the output.
|
|
2416
|
+
* @returns A new pipeline with the `$project` stage appended.
|
|
2417
|
+
*
|
|
2418
|
+
* @example
|
|
2419
|
+
* ```ts
|
|
2420
|
+
* const namesAndRoles = await aggregate(users)
|
|
2421
|
+
* .pick('name', 'role')
|
|
2422
|
+
* .toArray()
|
|
2423
|
+
* ```
|
|
2424
|
+
*/
|
|
2425
|
+
pick<K extends keyof TOutput & string>(...fields: K[]): AggregatePipeline<TDef, Prettify<Pick<TOutput, K | ('_id' extends keyof TOutput ? '_id' : never)>>>;
|
|
2426
|
+
/**
|
|
2427
|
+
* Exclude specified fields from the output.
|
|
2428
|
+
*
|
|
2429
|
+
* Appends a `$project` stage with exclusion (`0`) for each key.
|
|
2430
|
+
* All other fields pass through. The output type becomes `Omit<TOutput, K>`.
|
|
2431
|
+
*
|
|
2432
|
+
* @param fields - Field names to exclude from the output.
|
|
2433
|
+
* @returns A new pipeline with the `$project` stage appended.
|
|
2434
|
+
*
|
|
2435
|
+
* @example
|
|
2436
|
+
* ```ts
|
|
2437
|
+
* const noAge = await aggregate(users)
|
|
2438
|
+
* .omit('age')
|
|
2439
|
+
* .toArray()
|
|
2440
|
+
* ```
|
|
2441
|
+
*/
|
|
2442
|
+
omit<K extends keyof TOutput & string>(...fields: K[]): AggregatePipeline<TDef, Prettify<Omit<TOutput, K>>>;
|
|
2443
|
+
/**
|
|
2444
|
+
* Group documents by one or more fields with accumulator expressions.
|
|
2445
|
+
*
|
|
2446
|
+
* Accepts accumulators as either a **callback** (recommended) or a plain object.
|
|
2447
|
+
*
|
|
2448
|
+
* The callback receives a typed `AccumulatorBuilder` with full autocomplete
|
|
2449
|
+
* and compile-time field validation. Builder methods resolve return types
|
|
2450
|
+
* to the actual field type (`T[K]`), not `unknown`.
|
|
2451
|
+
*
|
|
2452
|
+
* @param field - A field name or array of field names to group by.
|
|
2453
|
+
* @param accumulators - A callback `(acc) => ({ ... })` or plain accumulator object.
|
|
2454
|
+
* @returns A new pipeline with the `$group` stage appended.
|
|
2455
|
+
*
|
|
2456
|
+
* @example
|
|
2457
|
+
* ```ts
|
|
2458
|
+
* // Callback style (recommended — full type safety)
|
|
2459
|
+
* const byRole = await aggregate(users)
|
|
2460
|
+
* .groupBy('role', acc => ({
|
|
2461
|
+
* count: acc.count(),
|
|
2462
|
+
* minSalary: acc.min('salary'), // → number
|
|
2463
|
+
* firstName: acc.first('name'), // → string
|
|
2464
|
+
* }))
|
|
2465
|
+
* .toArray()
|
|
2466
|
+
* ```
|
|
2467
|
+
*
|
|
2468
|
+
* @example
|
|
2469
|
+
* ```ts
|
|
2470
|
+
* // Object style (backward compatible)
|
|
2471
|
+
* const byRole = await aggregate(users)
|
|
2472
|
+
* .groupBy('role', { count: $count() })
|
|
2473
|
+
* .toArray()
|
|
2474
|
+
* ```
|
|
2475
|
+
*
|
|
2476
|
+
* @example
|
|
2477
|
+
* ```ts
|
|
2478
|
+
* // Compound groupBy
|
|
2479
|
+
* const byRoleAndDept = await aggregate(users)
|
|
2480
|
+
* .groupBy(['role', 'dept'], acc => ({ count: acc.count() }))
|
|
2481
|
+
* .toArray()
|
|
2482
|
+
* ```
|
|
2483
|
+
*/
|
|
2484
|
+
groupBy<K extends keyof TOutput & string, TAccum extends Record<string, Accumulator>>(field: K, accumulators: ((acc: AccumulatorBuilder<TOutput>) => TAccum) | TAccum): AggregatePipeline<TDef, GroupByResult<TOutput, K, TAccum>>;
|
|
2485
|
+
groupBy<K extends keyof TOutput & string, TAccum extends Record<string, Accumulator>>(field: K[], accumulators: ((acc: AccumulatorBuilder<TOutput>) => TAccum) | TAccum): AggregatePipeline<TDef, GroupByCompoundResult<TOutput, K, TAccum>>;
|
|
2486
|
+
/**
|
|
2487
|
+
* Add new fields or overwrite existing ones in the output documents.
|
|
2488
|
+
*
|
|
2489
|
+
* **Callback style (recommended)** — the `ExpressionBuilder` provides
|
|
2490
|
+
* autocomplete for field names and infers return types automatically:
|
|
2491
|
+
*
|
|
2492
|
+
* ```ts
|
|
2493
|
+
* employees.aggregate()
|
|
2494
|
+
* .addFields(expr => ({
|
|
2495
|
+
* hireYear: expr.year('hiredAt'), // Expression<number>
|
|
2496
|
+
* isHighPay: expr.gte('salary', 100_000), // Expression<boolean>
|
|
2497
|
+
* }))
|
|
2498
|
+
* ```
|
|
2499
|
+
*
|
|
2500
|
+
* **Raw style** — pass MongoDB expression objects directly. Use an
|
|
2501
|
+
* explicit type parameter to preserve type safety:
|
|
2502
|
+
*
|
|
2503
|
+
* ```ts
|
|
2504
|
+
* // Tier 2: explicit type parameter
|
|
2505
|
+
* .addFields<{ hireYear: number }>({ hireYear: { $year: '$hiredAt' } })
|
|
2506
|
+
*
|
|
2507
|
+
* // Tier 3: no type parameter — fields resolve to unknown
|
|
2508
|
+
* .addFields({ hireYear: { $year: '$hiredAt' } })
|
|
2509
|
+
* ```
|
|
2510
|
+
*
|
|
2511
|
+
* @param fields - A callback `(expr) => ({ ... })` or plain fields object.
|
|
2512
|
+
* @returns A new pipeline with the `$addFields` stage appended.
|
|
2513
|
+
*
|
|
2514
|
+
* @example
|
|
2515
|
+
* ```ts
|
|
2516
|
+
* const enriched = await aggregate(employees)
|
|
2517
|
+
* .addFields(expr => ({
|
|
2518
|
+
* hireYear: expr.year('hiredAt'),
|
|
2519
|
+
* monthlySalary: expr.divide('salary', 12),
|
|
2520
|
+
* }))
|
|
2521
|
+
* .toArray()
|
|
2522
|
+
* ```
|
|
2523
|
+
*/
|
|
2524
|
+
addFields<TFields extends Record<string, Expression>>(fields: (expr: ExpressionBuilder<TOutput>) => TFields): AggregatePipeline<TDef, Prettify<TOutput & InferAddedFields<TFields>>>;
|
|
2525
|
+
addFields<TFields extends Record<string, unknown> = Record<string, unknown>>(fields: TFields): AggregatePipeline<TDef, Prettify<TOutput & TFields>>;
|
|
2526
|
+
/**
|
|
2527
|
+
* Deconstruct an array field, outputting one document per array element.
|
|
2528
|
+
*
|
|
2529
|
+
* Appends an `$unwind` stage. The unwound field's type changes from
|
|
2530
|
+
* `T[]` to `T` in the output type. Documents with empty or missing
|
|
2531
|
+
* arrays are dropped unless `preserveEmpty` is `true`.
|
|
2532
|
+
*
|
|
2533
|
+
* @param field - The name of the array field to unwind.
|
|
2534
|
+
* @param options - Optional settings for the unwind stage.
|
|
2535
|
+
* @param options.preserveEmpty - If `true`, documents with null, missing, or empty arrays are preserved.
|
|
2536
|
+
* @returns A new pipeline with the `$unwind` stage appended.
|
|
2537
|
+
*
|
|
2538
|
+
* @example
|
|
2539
|
+
* ```ts
|
|
2540
|
+
* const flat = await aggregate(orders)
|
|
2541
|
+
* .unwind('items')
|
|
2542
|
+
* .toArray()
|
|
2543
|
+
* // Each result has a single `items` value instead of an array
|
|
2544
|
+
* ```
|
|
2545
|
+
*/
|
|
2546
|
+
unwind<K extends keyof TOutput & string>(field: K, options?: {
|
|
2547
|
+
preserveEmpty?: boolean;
|
|
2548
|
+
}): AggregatePipeline<TDef, UnwindResult<TOutput, K>>;
|
|
2549
|
+
/**
|
|
2550
|
+
* Join documents from another collection via a `$lookup` stage.
|
|
2551
|
+
*
|
|
2552
|
+
* **Forward lookup** — when the field has `.ref()` metadata pointing to
|
|
2553
|
+
* another collection, resolves the target collection and output type
|
|
2554
|
+
* automatically:
|
|
2555
|
+
*
|
|
2556
|
+
* ```ts
|
|
2557
|
+
* books.aggregate().lookup('authorId').toArray()
|
|
2558
|
+
* // Each book gains an `authors: Author[]` array
|
|
2559
|
+
* ```
|
|
2560
|
+
*
|
|
2561
|
+
* **Reverse lookup** — when the foreign key lives on the *other*
|
|
2562
|
+
* collection, pass the collection definition and the `on` field:
|
|
2563
|
+
*
|
|
2564
|
+
* ```ts
|
|
2565
|
+
* users.aggregate().lookup(Books, { on: 'authorId' }).toArray()
|
|
2566
|
+
* // Each user gains a `books: Book[]` array
|
|
2567
|
+
* ```
|
|
2568
|
+
*
|
|
2569
|
+
* Pass `unwind: true` to flatten the joined array into a single object
|
|
2570
|
+
* (adds a `$unwind` stage with `preserveNullAndEmptyArrays: true`).
|
|
2571
|
+
*
|
|
2572
|
+
* @param fieldOrCollection - A ref-bearing field name (forward) or collection definition (reverse).
|
|
2573
|
+
* @param options - `as` alias, `unwind` flag, and `on` (reverse only).
|
|
2574
|
+
* @returns A new pipeline with `$lookup` (and optionally `$unwind`) appended.
|
|
2575
|
+
*
|
|
2576
|
+
* @example
|
|
2577
|
+
* ```ts
|
|
2578
|
+
* // Forward lookup with custom alias and unwind
|
|
2579
|
+
* const results = await aggregate(books)
|
|
2580
|
+
* .lookup('authorId', { as: 'author', unwind: true })
|
|
2581
|
+
* .toArray()
|
|
2582
|
+
* // Each book has a single `author: Author` object
|
|
2583
|
+
* ```
|
|
2584
|
+
*
|
|
2585
|
+
* @example
|
|
2586
|
+
* ```ts
|
|
2587
|
+
* // Reverse lookup: each author gains their books
|
|
2588
|
+
* const results = await aggregate(authors)
|
|
2589
|
+
* .lookup(Books, { on: 'authorId' })
|
|
2590
|
+
* .toArray()
|
|
2591
|
+
* // Each author has a `books: Book[]` array
|
|
2592
|
+
* ```
|
|
2593
|
+
*/
|
|
2594
|
+
lookup<K extends RefFields<TDef>, TAs extends string = CollectionName<ExtractRefCollection<TDef, K>>>(field: K, options: {
|
|
2595
|
+
as?: TAs;
|
|
2596
|
+
unwind: true;
|
|
2597
|
+
}): AggregatePipeline<TDef, Prettify<TOutput & {
|
|
2598
|
+
[P in TAs]: InferDocument<ExtractRefCollection<TDef, K>>;
|
|
2599
|
+
}>>;
|
|
2600
|
+
lookup<K extends RefFields<TDef>, TAs extends string = CollectionName<ExtractRefCollection<TDef, K>>>(field: K, options?: {
|
|
2601
|
+
as?: TAs;
|
|
2602
|
+
unwind?: false;
|
|
2603
|
+
}): AggregatePipeline<TDef, Prettify<TOutput & {
|
|
2604
|
+
[P in TAs]: InferDocument<ExtractRefCollection<TDef, K>>[];
|
|
2605
|
+
}>>;
|
|
2606
|
+
lookup<TForeignDef extends AnyCollection, TAs extends string = CollectionName<TForeignDef>>(from: TForeignDef, options: {
|
|
2607
|
+
on: keyof InferDocument<TForeignDef> & string;
|
|
2608
|
+
as?: TAs;
|
|
2609
|
+
unwind: true;
|
|
2610
|
+
}): AggregatePipeline<TDef, Prettify<TOutput & {
|
|
2611
|
+
[P in TAs]: InferDocument<TForeignDef>;
|
|
2612
|
+
}>>;
|
|
2613
|
+
lookup<TForeignDef extends AnyCollection, TAs extends string = CollectionName<TForeignDef>>(from: TForeignDef, options: {
|
|
2614
|
+
on: keyof InferDocument<TForeignDef> & string;
|
|
2615
|
+
as?: TAs;
|
|
2616
|
+
unwind?: false;
|
|
2617
|
+
}): AggregatePipeline<TDef, Prettify<TOutput & {
|
|
2618
|
+
[P in TAs]: InferDocument<TForeignDef>[];
|
|
2619
|
+
}>>;
|
|
2620
|
+
/**
|
|
2621
|
+
* Count documents per group, sorted by count descending.
|
|
2622
|
+
*
|
|
2623
|
+
* Shorthand for `.groupBy(field, { count: $count() }).sort({ count: -1 })`.
|
|
2624
|
+
*
|
|
2625
|
+
* @param field - The field to group and count by.
|
|
2626
|
+
* @returns A new pipeline producing `{ _id: TOutput[K], count: number }` results.
|
|
2627
|
+
*
|
|
2628
|
+
* @example
|
|
2629
|
+
* ```ts
|
|
2630
|
+
* const roleCounts = await aggregate(users)
|
|
2631
|
+
* .countBy('role')
|
|
2632
|
+
* .toArray()
|
|
2633
|
+
* // [{ _id: 'user', count: 3 }, { _id: 'admin', count: 2 }]
|
|
1261
2634
|
* ```
|
|
1262
2635
|
*/
|
|
1263
|
-
|
|
2636
|
+
countBy<K extends keyof TOutput & string>(field: K): AggregatePipeline<TDef, Prettify<{
|
|
2637
|
+
_id: TOutput[K];
|
|
2638
|
+
count: number;
|
|
2639
|
+
}>>;
|
|
1264
2640
|
/**
|
|
1265
|
-
*
|
|
2641
|
+
* Sum a numeric field per group, sorted by total descending.
|
|
1266
2642
|
*
|
|
1267
|
-
*
|
|
1268
|
-
* `returnDocument: 'before'` to get the pre-update snapshot. The returned
|
|
1269
|
-
* document is validated against the collection's Zod schema using the same
|
|
1270
|
-
* resolution logic as {@link findOne}.
|
|
2643
|
+
* Shorthand for `.groupBy(field, { total: $sum('$sumField') }).sort({ total: -1 })`.
|
|
1271
2644
|
*
|
|
1272
|
-
* @param
|
|
1273
|
-
* @param
|
|
1274
|
-
* @
|
|
1275
|
-
* @returns The matched document (before or after update), or `null` if no document matches.
|
|
1276
|
-
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
2645
|
+
* @param field - The field to group by.
|
|
2646
|
+
* @param sumField - The numeric field to sum.
|
|
2647
|
+
* @returns A new pipeline producing `{ _id: TOutput[K], total: number }` results.
|
|
1277
2648
|
*
|
|
1278
2649
|
* @example
|
|
1279
2650
|
* ```ts
|
|
1280
|
-
* const
|
|
1281
|
-
*
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
* )
|
|
1285
|
-
* if (user) console.log(user.role) // 'admin' (returned after update)
|
|
2651
|
+
* const revenueByCategory = await aggregate(orders)
|
|
2652
|
+
* .sumBy('category', 'amount')
|
|
2653
|
+
* .toArray()
|
|
2654
|
+
* // [{ _id: 'electronics', total: 5000 }, ...]
|
|
1286
2655
|
* ```
|
|
1287
2656
|
*/
|
|
1288
|
-
|
|
2657
|
+
sumBy<K extends keyof TOutput & string, S extends keyof TOutput & string>(field: K, sumField: S): AggregatePipeline<TDef, Prettify<{
|
|
2658
|
+
_id: TOutput[K];
|
|
2659
|
+
total: number;
|
|
2660
|
+
}>>;
|
|
1289
2661
|
/**
|
|
1290
|
-
*
|
|
2662
|
+
* Sort by a single field with a friendly direction name.
|
|
1291
2663
|
*
|
|
1292
|
-
*
|
|
1293
|
-
* No validation is performed — the document is deleted directly through
|
|
1294
|
-
* the MongoDB driver.
|
|
2664
|
+
* Shorthand for `.sort({ [field]: direction === 'desc' ? -1 : 1 })`.
|
|
1295
2665
|
*
|
|
1296
|
-
* @param
|
|
1297
|
-
* @
|
|
2666
|
+
* @param field - The field to sort by.
|
|
2667
|
+
* @param direction - Sort direction: `'asc'` (default) or `'desc'`.
|
|
2668
|
+
* @returns A new pipeline with the `$sort` stage appended.
|
|
1298
2669
|
*
|
|
1299
2670
|
* @example
|
|
1300
2671
|
* ```ts
|
|
1301
|
-
* const
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
2672
|
+
* const youngest = await aggregate(users)
|
|
2673
|
+
* .sortBy('age')
|
|
2674
|
+
* .toArray()
|
|
1304
2675
|
* ```
|
|
1305
2676
|
*/
|
|
1306
|
-
|
|
2677
|
+
sortBy(field: keyof TOutput & string, direction?: 'asc' | 'desc'): AggregatePipeline<TDef, TOutput>;
|
|
1307
2678
|
/**
|
|
1308
|
-
*
|
|
2679
|
+
* Return the top N documents sorted by a field descending.
|
|
1309
2680
|
*
|
|
1310
|
-
*
|
|
1311
|
-
* No validation is performed — documents are deleted directly through
|
|
1312
|
-
* the MongoDB driver.
|
|
2681
|
+
* Shorthand for `.sort({ [by]: -1 }).limit(n)`.
|
|
1313
2682
|
*
|
|
1314
|
-
* @param
|
|
1315
|
-
* @
|
|
2683
|
+
* @param n - The number of documents to return.
|
|
2684
|
+
* @param options - An object with a `by` field specifying the sort key.
|
|
2685
|
+
* @returns A new pipeline with `$sort` and `$limit` stages appended.
|
|
1316
2686
|
*
|
|
1317
2687
|
* @example
|
|
1318
2688
|
* ```ts
|
|
1319
|
-
* const
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
2689
|
+
* const top3 = await aggregate(users)
|
|
2690
|
+
* .top(3, { by: 'score' })
|
|
2691
|
+
* .toArray()
|
|
1322
2692
|
* ```
|
|
1323
2693
|
*/
|
|
1324
|
-
|
|
2694
|
+
top(n: number, options: {
|
|
2695
|
+
by: keyof TOutput & string;
|
|
2696
|
+
}): AggregatePipeline<TDef, TOutput>;
|
|
1325
2697
|
/**
|
|
1326
|
-
*
|
|
2698
|
+
* Return the bottom N documents sorted by a field ascending.
|
|
1327
2699
|
*
|
|
1328
|
-
*
|
|
1329
|
-
* The returned document is validated against the collection's Zod schema
|
|
1330
|
-
* using the same resolution logic as {@link findOne}.
|
|
2700
|
+
* Shorthand for `.sort({ [by]: 1 }).limit(n)`.
|
|
1331
2701
|
*
|
|
1332
|
-
* @param
|
|
1333
|
-
* @param options -
|
|
1334
|
-
* @returns
|
|
1335
|
-
* @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.
|
|
2702
|
+
* @param n - The number of documents to return.
|
|
2703
|
+
* @param options - An object with a `by` field specifying the sort key.
|
|
2704
|
+
* @returns A new pipeline with `$sort` and `$limit` stages appended.
|
|
1336
2705
|
*
|
|
1337
2706
|
* @example
|
|
1338
2707
|
* ```ts
|
|
1339
|
-
* const
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
2708
|
+
* const bottom3 = await aggregate(users)
|
|
2709
|
+
* .bottom(3, { by: 'score' })
|
|
2710
|
+
* .toArray()
|
|
1342
2711
|
* ```
|
|
1343
2712
|
*/
|
|
1344
|
-
|
|
2713
|
+
bottom(n: number, options: {
|
|
2714
|
+
by: keyof TOutput & string;
|
|
2715
|
+
}): AggregatePipeline<TDef, TOutput>;
|
|
1345
2716
|
}
|
|
2717
|
+
/**
|
|
2718
|
+
* Create a new aggregation pipeline for a collection.
|
|
2719
|
+
*
|
|
2720
|
+
* Returns an empty pipeline that can be extended with stage methods
|
|
2721
|
+
* like `raw()`, or future typed stages (`match`, `project`, etc.).
|
|
2722
|
+
*
|
|
2723
|
+
* @param handle - A typed collection handle obtained from `db.use()`.
|
|
2724
|
+
* @returns A new empty `AggregatePipeline` whose output type is the collection's document type.
|
|
2725
|
+
*
|
|
2726
|
+
* @example
|
|
2727
|
+
* ```ts
|
|
2728
|
+
* const users = db.use(Users)
|
|
2729
|
+
* const admins = await aggregate(users)
|
|
2730
|
+
* .raw({ $match: { role: 'admin' } })
|
|
2731
|
+
* .toArray()
|
|
2732
|
+
* ```
|
|
2733
|
+
*/
|
|
2734
|
+
declare function aggregate<TDef extends AnyCollection>(handle: CollectionHandle<TDef>): AggregatePipeline<TDef, InferDocument<TDef>>;
|
|
1346
2735
|
|
|
1347
2736
|
/**
|
|
1348
2737
|
* Wraps a MongoDB `MongoClient` and `Db`, providing typed collection access
|
|
@@ -1376,13 +2765,28 @@ declare class Database {
|
|
|
1376
2765
|
* @param def - A collection definition created by `collection()`.
|
|
1377
2766
|
* @returns A typed collection handle for CRUD operations.
|
|
1378
2767
|
*/
|
|
1379
|
-
use<TShape extends z.core.$ZodShape>(def: CollectionDefinition<TShape>): CollectionHandle<CollectionDefinition<TShape>>;
|
|
2768
|
+
use<TName extends string, TShape extends z.core.$ZodShape, TIndexes extends readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[]>(def: CollectionDefinition<TName, TShape, TIndexes>): CollectionHandle<CollectionDefinition<TName, TShape, TIndexes>>;
|
|
1380
2769
|
/**
|
|
1381
|
-
* Synchronize indexes
|
|
2770
|
+
* Synchronize indexes for all registered collections with MongoDB.
|
|
2771
|
+
*
|
|
2772
|
+
* Iterates every collection registered via {@link use} and calls
|
|
2773
|
+
* {@link syncIndexes} on each one. Returns a record keyed by collection
|
|
2774
|
+
* name with the sync result for each.
|
|
1382
2775
|
*
|
|
1383
|
-
*
|
|
2776
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
2777
|
+
* @returns A record mapping collection names to their sync results.
|
|
2778
|
+
*
|
|
2779
|
+
* @example
|
|
2780
|
+
* ```ts
|
|
2781
|
+
* const db = createClient('mongodb://localhost:27017', 'myapp')
|
|
2782
|
+
* db.use(Users)
|
|
2783
|
+
* db.use(Posts)
|
|
2784
|
+
* const results = await db.syncIndexes()
|
|
2785
|
+
* console.log(results['users'].created) // ['email_1']
|
|
2786
|
+
* console.log(results['posts'].created) // ['title_1']
|
|
2787
|
+
* ```
|
|
1384
2788
|
*/
|
|
1385
|
-
syncIndexes(): Promise<
|
|
2789
|
+
syncIndexes(options?: SyncIndexesOptions): Promise<Record<string, SyncIndexesResult>>;
|
|
1386
2790
|
/**
|
|
1387
2791
|
* Execute a function within a MongoDB transaction with auto-commit/rollback.
|
|
1388
2792
|
*
|
|
@@ -1457,7 +2861,9 @@ declare function extractFieldIndexes(shape: z.core.$ZodShape): FieldIndexDefinit
|
|
|
1457
2861
|
* })
|
|
1458
2862
|
* ```
|
|
1459
2863
|
*/
|
|
1460
|
-
declare function collection<TShape extends z.core.$ZodShape>(name:
|
|
2864
|
+
declare function collection<TName extends string, TShape extends z.core.$ZodShape, const TIndexes extends readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[]>(name: TName, shape: TShape, options?: Omit<CollectionOptions<Extract<keyof TShape, string>>, 'indexes'> & {
|
|
2865
|
+
indexes?: TIndexes;
|
|
2866
|
+
}): CollectionDefinition<TName, TShape, [...TIndexes]>;
|
|
1461
2867
|
|
|
1462
2868
|
type IndexDirection = 1 | -1;
|
|
1463
2869
|
type CompoundIndexOptions = NonNullable<CompoundIndexDefinition['options']>;
|
|
@@ -1485,9 +2891,27 @@ declare class IndexBuilder<TKeys extends string> {
|
|
|
1485
2891
|
readonly options: CompoundIndexOptions;
|
|
1486
2892
|
constructor(fields: Record<TKeys, IndexDirection>);
|
|
1487
2893
|
private _clone;
|
|
1488
|
-
unique():
|
|
1489
|
-
sparse():
|
|
1490
|
-
|
|
2894
|
+
unique(): this;
|
|
2895
|
+
sparse(): this;
|
|
2896
|
+
/**
|
|
2897
|
+
* Set a custom name for this index, preserving the literal type.
|
|
2898
|
+
*
|
|
2899
|
+
* The returned builder carries the literal name type via an intersection,
|
|
2900
|
+
* enabling type-safe `.hint()` on cursors that only accepts declared names.
|
|
2901
|
+
*
|
|
2902
|
+
* @param name - The index name.
|
|
2903
|
+
* @returns A new IndexBuilder with the name recorded at the type level.
|
|
2904
|
+
*
|
|
2905
|
+
* @example
|
|
2906
|
+
* ```ts
|
|
2907
|
+
* index({ email: 1, role: -1 }).name('email_role_idx')
|
|
2908
|
+
* ```
|
|
2909
|
+
*/
|
|
2910
|
+
name<TName extends string>(name: TName): IndexBuilder<TKeys> & {
|
|
2911
|
+
readonly options: {
|
|
2912
|
+
readonly name: TName;
|
|
2913
|
+
};
|
|
2914
|
+
};
|
|
1491
2915
|
}
|
|
1492
2916
|
/**
|
|
1493
2917
|
* Create a compound index definition with a fluent builder API.
|
|
@@ -1652,6 +3076,231 @@ declare function oid(value: ObjectId): ObjectId;
|
|
|
1652
3076
|
*/
|
|
1653
3077
|
declare function isOid(value: unknown): value is ObjectId;
|
|
1654
3078
|
|
|
3079
|
+
/**
|
|
3080
|
+
* A normalized index specification ready for comparison and creation.
|
|
3081
|
+
*
|
|
3082
|
+
* `key` maps field paths to direction (`1`, `-1`, or `'text'`).
|
|
3083
|
+
* `options` holds MongoDB index options (`unique`, `sparse`, etc.).
|
|
3084
|
+
*
|
|
3085
|
+
* @example
|
|
3086
|
+
* ```ts
|
|
3087
|
+
* const spec: IndexSpec = {
|
|
3088
|
+
* key: { email: 1 },
|
|
3089
|
+
* options: { unique: true },
|
|
3090
|
+
* }
|
|
3091
|
+
* ```
|
|
3092
|
+
*/
|
|
3093
|
+
type IndexSpec = {
|
|
3094
|
+
key: Record<string, 1 | -1 | 'text'>;
|
|
3095
|
+
options: Record<string, unknown>;
|
|
3096
|
+
};
|
|
3097
|
+
/**
|
|
3098
|
+
* Convert a field-level index definition to a normalized {@link IndexSpec}.
|
|
3099
|
+
*
|
|
3100
|
+
* Maps schema metadata (`text`, `descending`, `unique`, `sparse`, `expireAfter`,
|
|
3101
|
+
* `partial`) to their MongoDB driver equivalents.
|
|
3102
|
+
*
|
|
3103
|
+
* @param def - A field index definition extracted from schema metadata.
|
|
3104
|
+
* @returns A normalized index spec with key and options.
|
|
3105
|
+
*
|
|
3106
|
+
* @example
|
|
3107
|
+
* ```ts
|
|
3108
|
+
* const spec = toFieldIndexSpec({ field: 'email', indexed: true, unique: true })
|
|
3109
|
+
* // => { key: { email: 1 }, options: { unique: true } }
|
|
3110
|
+
*
|
|
3111
|
+
* const ttl = toFieldIndexSpec({ field: 'expiresAt', indexed: true, expireAfter: 3600 })
|
|
3112
|
+
* // => { key: { expiresAt: 1 }, options: { expireAfterSeconds: 3600 } }
|
|
3113
|
+
* ```
|
|
3114
|
+
*/
|
|
3115
|
+
declare function toFieldIndexSpec(def: FieldIndexDefinition): IndexSpec;
|
|
3116
|
+
/**
|
|
3117
|
+
* Convert a compound index definition to a normalized {@link IndexSpec}.
|
|
3118
|
+
*
|
|
3119
|
+
* Copies the `fields` map directly to `key` and maps builder options
|
|
3120
|
+
* (`unique`, `sparse`, `name`, `partial`) to MongoDB driver equivalents.
|
|
3121
|
+
*
|
|
3122
|
+
* @param def - A compound index definition from the collection options.
|
|
3123
|
+
* @returns A normalized index spec with key and options.
|
|
3124
|
+
*
|
|
3125
|
+
* @example
|
|
3126
|
+
* ```ts
|
|
3127
|
+
* const spec = toCompoundIndexSpec({
|
|
3128
|
+
* fields: { email: 1, role: -1 },
|
|
3129
|
+
* options: { unique: true, name: 'email_role_idx' },
|
|
3130
|
+
* })
|
|
3131
|
+
* // => { key: { email: 1, role: -1 }, options: { unique: true, name: 'email_role_idx' } }
|
|
3132
|
+
* ```
|
|
3133
|
+
*/
|
|
3134
|
+
declare function toCompoundIndexSpec(def: CompoundIndexDefinition): IndexSpec;
|
|
3135
|
+
/**
|
|
3136
|
+
* Produce a stable, deterministic string from an index key for comparison.
|
|
3137
|
+
*
|
|
3138
|
+
* Entries are sorted alphabetically by field name and formatted as
|
|
3139
|
+
* `field:direction` pairs joined by commas. Two index keys that should be
|
|
3140
|
+
* considered the same will always produce the same string.
|
|
3141
|
+
*
|
|
3142
|
+
* @param key - An index key mapping field names to direction.
|
|
3143
|
+
* @returns A string like `'email:1,role:-1'`.
|
|
3144
|
+
*
|
|
3145
|
+
* @example
|
|
3146
|
+
* ```ts
|
|
3147
|
+
* serializeIndexKey({ email: 1, role: -1 })
|
|
3148
|
+
* // => 'email:1,role:-1'
|
|
3149
|
+
*
|
|
3150
|
+
* serializeIndexKey({ name: 'text' })
|
|
3151
|
+
* // => 'name:text'
|
|
3152
|
+
* ```
|
|
3153
|
+
*/
|
|
3154
|
+
declare function serializeIndexKey(key: Record<string, 1 | -1 | 'text'>): string;
|
|
3155
|
+
|
|
3156
|
+
/**
|
|
3157
|
+
* Structural constraint for the handle argument of {@link syncIndexes}.
|
|
3158
|
+
*
|
|
3159
|
+
* Uses a structural type rather than `CollectionHandle<AnyCollection>` to avoid
|
|
3160
|
+
* TypeScript variance issues with `exactOptionalPropertyTypes`. Any collection
|
|
3161
|
+
* handle returned by `db.use()` satisfies this constraint.
|
|
3162
|
+
*/
|
|
3163
|
+
type SyncableHandle = {
|
|
3164
|
+
readonly definition: {
|
|
3165
|
+
readonly fieldIndexes: FieldIndexDefinition[];
|
|
3166
|
+
readonly compoundIndexes: readonly CompoundIndexDefinition[];
|
|
3167
|
+
};
|
|
3168
|
+
readonly native: Collection<any>;
|
|
3169
|
+
};
|
|
3170
|
+
/**
|
|
3171
|
+
* Extract the comparable options from a MongoDB index info object.
|
|
3172
|
+
*
|
|
3173
|
+
* Pulls only the keys listed in {@link COMPARABLE_OPTION_KEYS} from the raw
|
|
3174
|
+
* index info returned by `listIndexes()`. Keys whose value is `undefined`
|
|
3175
|
+
* are omitted so that JSON comparison works correctly.
|
|
3176
|
+
*
|
|
3177
|
+
* @param info - A raw MongoDB index info object.
|
|
3178
|
+
* @returns A plain object with only the relevant option keys.
|
|
3179
|
+
*
|
|
3180
|
+
* @example
|
|
3181
|
+
* ```ts
|
|
3182
|
+
* const opts = extractComparableOptions({ v: 2, unique: true, key: { email: 1 } })
|
|
3183
|
+
* // => { unique: true }
|
|
3184
|
+
* ```
|
|
3185
|
+
*/
|
|
3186
|
+
declare function extractComparableOptions(info: Record<string, unknown>): Record<string, unknown>;
|
|
3187
|
+
/**
|
|
3188
|
+
* Generate the default MongoDB index name from a key spec.
|
|
3189
|
+
*
|
|
3190
|
+
* MongoDB names indexes by joining `field_direction` pairs with underscores.
|
|
3191
|
+
* For example, `{ email: 1, role: -1 }` becomes `'email_1_role_-1'`.
|
|
3192
|
+
*
|
|
3193
|
+
* @param key - An index key mapping field names to direction.
|
|
3194
|
+
* @returns The generated index name string.
|
|
3195
|
+
*
|
|
3196
|
+
* @example
|
|
3197
|
+
* ```ts
|
|
3198
|
+
* generateIndexName({ email: 1 })
|
|
3199
|
+
* // => 'email_1'
|
|
3200
|
+
*
|
|
3201
|
+
* generateIndexName({ email: 1, role: -1 })
|
|
3202
|
+
* // => 'email_1_role_-1'
|
|
3203
|
+
* ```
|
|
3204
|
+
*/
|
|
3205
|
+
declare function generateIndexName(key: Record<string, 1 | -1 | 'text'>): string;
|
|
3206
|
+
/**
|
|
3207
|
+
* Synchronize the indexes declared in a collection's schema with MongoDB.
|
|
3208
|
+
*
|
|
3209
|
+
* Compares the desired indexes (from field-level and compound index definitions)
|
|
3210
|
+
* with the indexes that currently exist in MongoDB, then creates, drops, or
|
|
3211
|
+
* reports differences depending on the options.
|
|
3212
|
+
*
|
|
3213
|
+
* **Algorithm:**
|
|
3214
|
+
* 1. Build desired specs from field indexes and compound indexes.
|
|
3215
|
+
* 2. Fetch existing indexes from MongoDB via `listIndexes()`.
|
|
3216
|
+
* 3. For each desired spec, compare against existing:
|
|
3217
|
+
* - Missing → create (unless `dryRun`).
|
|
3218
|
+
* - Present with same options → skip.
|
|
3219
|
+
* - Present with different options → stale (or drop+recreate if `dropOrphaned`).
|
|
3220
|
+
* 4. For each existing index not in desired set (excluding `_id_`):
|
|
3221
|
+
* - If `dropOrphaned` → drop (unless `dryRun`).
|
|
3222
|
+
* 5. Return a summary of all actions taken.
|
|
3223
|
+
*
|
|
3224
|
+
* @param handle - A collection handle created by `db.use()`.
|
|
3225
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
3226
|
+
* @returns A summary of created, dropped, skipped, and stale indexes.
|
|
3227
|
+
*
|
|
3228
|
+
* @example
|
|
3229
|
+
* ```ts
|
|
3230
|
+
* import { syncIndexes } from '@zodmon/core'
|
|
3231
|
+
*
|
|
3232
|
+
* const users = db.use(Users)
|
|
3233
|
+
* const result = await syncIndexes(users)
|
|
3234
|
+
* console.log('Created:', result.created)
|
|
3235
|
+
* console.log('Stale:', result.stale.map(s => s.name))
|
|
3236
|
+
* ```
|
|
3237
|
+
*
|
|
3238
|
+
* @example
|
|
3239
|
+
* ```ts
|
|
3240
|
+
* // Dry run — no changes made
|
|
3241
|
+
* const diff = await syncIndexes(users, { dryRun: true })
|
|
3242
|
+
* console.log('Would create:', diff.created)
|
|
3243
|
+
* console.log('Would drop:', diff.dropped)
|
|
3244
|
+
* ```
|
|
3245
|
+
*/
|
|
3246
|
+
declare function syncIndexes(handle: SyncableHandle, options?: SyncIndexesOptions): Promise<SyncIndexesResult>;
|
|
3247
|
+
|
|
3248
|
+
/**
|
|
3249
|
+
* Structural constraint for the definition argument of {@link checkUnindexedFields}.
|
|
3250
|
+
*
|
|
3251
|
+
* Uses a structural type rather than `CollectionDefinition<z.core.$ZodShape>` to avoid
|
|
3252
|
+
* TypeScript variance issues with `exactOptionalPropertyTypes`. Any collection
|
|
3253
|
+
* definition returned by `collection()` satisfies this constraint.
|
|
3254
|
+
*/
|
|
3255
|
+
type WarnableDefinition = {
|
|
3256
|
+
readonly name: string;
|
|
3257
|
+
readonly fieldIndexes: FieldIndexDefinition[];
|
|
3258
|
+
readonly compoundIndexes: readonly CompoundIndexDefinition[];
|
|
3259
|
+
readonly options: {
|
|
3260
|
+
warnUnindexedQueries?: boolean;
|
|
3261
|
+
};
|
|
3262
|
+
};
|
|
3263
|
+
/**
|
|
3264
|
+
* Warn about unindexed fields used in a query filter.
|
|
3265
|
+
*
|
|
3266
|
+
* When `warnUnindexedQueries` is enabled on a collection definition, this
|
|
3267
|
+
* function checks each top-level field in the filter against the collection's
|
|
3268
|
+
* declared indexes. Fields that are not covered by any index produce a
|
|
3269
|
+
* `console.warn` message to help identify queries that may cause full
|
|
3270
|
+
* collection scans in development.
|
|
3271
|
+
*
|
|
3272
|
+
* **Covered fields:**
|
|
3273
|
+
* - `_id` (always indexed by MongoDB)
|
|
3274
|
+
* - Fields with `.index()`, `.unique()`, `.text()`, or `.expireAfter()` (field-level indexes)
|
|
3275
|
+
* - The **first** field of each compound index (prefix matching)
|
|
3276
|
+
*
|
|
3277
|
+
* **Skipped keys:**
|
|
3278
|
+
* - MongoDB operators: `$or`, `$and`, `$nor`, `$text`, `$where`, `$expr`, `$comment`
|
|
3279
|
+
*
|
|
3280
|
+
* This function is a no-op (zero overhead) when `warnUnindexedQueries` is not
|
|
3281
|
+
* explicitly set to `true`.
|
|
3282
|
+
*
|
|
3283
|
+
* @param definition - The collection definition containing index metadata.
|
|
3284
|
+
* @param filter - The query filter to check for unindexed fields.
|
|
3285
|
+
*
|
|
3286
|
+
* @example
|
|
3287
|
+
* ```ts
|
|
3288
|
+
* import { collection, checkUnindexedFields } from '@zodmon/core'
|
|
3289
|
+
*
|
|
3290
|
+
* const Users = collection('users', {
|
|
3291
|
+
* email: z.string().unique(),
|
|
3292
|
+
* name: z.string(),
|
|
3293
|
+
* }, { warnUnindexedQueries: true })
|
|
3294
|
+
*
|
|
3295
|
+
* // No warning — email is indexed
|
|
3296
|
+
* checkUnindexedFields(Users, { email: 'ada@example.com' })
|
|
3297
|
+
*
|
|
3298
|
+
* // Warns: "[zodmon] warn: query on 'users' uses unindexed field 'name'"
|
|
3299
|
+
* checkUnindexedFields(Users, { name: 'Ada' })
|
|
3300
|
+
* ```
|
|
3301
|
+
*/
|
|
3302
|
+
declare function checkUnindexedFields(definition: WarnableDefinition, filter: Record<string, unknown>): void;
|
|
3303
|
+
|
|
1655
3304
|
/**
|
|
1656
3305
|
* Convenience namespace that groups all query operators under a single import.
|
|
1657
3306
|
*
|
|
@@ -1687,11 +3336,11 @@ declare const $: {
|
|
|
1687
3336
|
readonly lte: <V>(value: V) => {
|
|
1688
3337
|
$lte: V;
|
|
1689
3338
|
};
|
|
1690
|
-
readonly in: <V>(values: V[]) => {
|
|
1691
|
-
$in: V[];
|
|
3339
|
+
readonly in: <V>(values: readonly V[]) => {
|
|
3340
|
+
$in: readonly V[];
|
|
1692
3341
|
};
|
|
1693
|
-
readonly nin: <V>(values: V[]) => {
|
|
1694
|
-
$nin: V[];
|
|
3342
|
+
readonly nin: <V>(values: readonly V[]) => {
|
|
3343
|
+
$nin: readonly V[];
|
|
1695
3344
|
};
|
|
1696
3345
|
readonly exists: (flag?: boolean) => {
|
|
1697
3346
|
$exists: boolean;
|
|
@@ -1783,8 +3432,8 @@ declare const $lte: <V>(value: V) => {
|
|
|
1783
3432
|
* users.find({ role: $in(['admin', 'moderator']) })
|
|
1784
3433
|
* ```
|
|
1785
3434
|
*/
|
|
1786
|
-
declare const $in: <V>(values: V[]) => {
|
|
1787
|
-
$in: V[];
|
|
3435
|
+
declare const $in: <V>(values: readonly V[]) => {
|
|
3436
|
+
$in: readonly V[];
|
|
1788
3437
|
};
|
|
1789
3438
|
/**
|
|
1790
3439
|
* Matches none of the values in the specified array.
|
|
@@ -1794,8 +3443,8 @@ declare const $in: <V>(values: V[]) => {
|
|
|
1794
3443
|
* users.find({ role: $nin(['banned', 'suspended']) })
|
|
1795
3444
|
* ```
|
|
1796
3445
|
*/
|
|
1797
|
-
declare const $nin: <V>(values: V[]) => {
|
|
1798
|
-
$nin: V[];
|
|
3446
|
+
declare const $nin: <V>(values: readonly V[]) => {
|
|
3447
|
+
$nin: readonly V[];
|
|
1799
3448
|
};
|
|
1800
3449
|
/**
|
|
1801
3450
|
* Matches documents where the field exists (or does not exist).
|
|
@@ -1895,84 +3544,6 @@ declare const $nor: <T>(...filters: NoInfer<TypedFilter<T>>[]) => TypedFilter<T>
|
|
|
1895
3544
|
*/
|
|
1896
3545
|
declare const raw: <T = any>(filter: Record<string, unknown>) => TypedFilter<T>;
|
|
1897
3546
|
|
|
1898
|
-
/**
|
|
1899
|
-
* Type-level marker that carries the target collection type through the
|
|
1900
|
-
* type system. Intersected with the schema return type by `.ref()` so
|
|
1901
|
-
* that `RefFields<T>` (future) can extract ref relationships.
|
|
1902
|
-
*
|
|
1903
|
-
* This is a phantom brand — no runtime value has this property.
|
|
1904
|
-
*/
|
|
1905
|
-
type RefMarker<TCollection extends AnyCollection = AnyCollection> = {
|
|
1906
|
-
readonly _ref: TCollection;
|
|
1907
|
-
};
|
|
1908
|
-
/**
|
|
1909
|
-
* Metadata stored in the WeakMap sidecar for schemas marked with `.ref()`.
|
|
1910
|
-
* Holds a reference to the target collection definition object.
|
|
1911
|
-
*/
|
|
1912
|
-
type RefMetadata = {
|
|
1913
|
-
readonly collection: AnyCollection;
|
|
1914
|
-
};
|
|
1915
|
-
/**
|
|
1916
|
-
* Module augmentation: adds `.ref()` to all `ZodType` schemas.
|
|
1917
|
-
*
|
|
1918
|
-
* The intersection constraint `this['_zod']['output'] extends InferDocument<TCollection>['_id']`
|
|
1919
|
-
* ensures compile-time type safety: the field's output type must match the
|
|
1920
|
-
* target collection's `_id` type. Mismatches produce a type error.
|
|
1921
|
-
*
|
|
1922
|
-
* Supports both default ObjectId `_id` and custom `_id` types (string, nanoid, etc.):
|
|
1923
|
-
* - `objectId().ref(Users)` compiles when Users has ObjectId `_id`
|
|
1924
|
-
* - `z.string().ref(Orgs)` compiles when Orgs has string `_id`
|
|
1925
|
-
* - `z.string().ref(Users)` is a type error (string ≠ ObjectId)
|
|
1926
|
-
* - `objectId().ref(Orgs)` is a type error (ObjectId ≠ string)
|
|
1927
|
-
*/
|
|
1928
|
-
declare module 'zod' {
|
|
1929
|
-
interface ZodType {
|
|
1930
|
-
/**
|
|
1931
|
-
* Declare a typed foreign key reference to another collection.
|
|
1932
|
-
*
|
|
1933
|
-
* Stores the target collection definition in metadata for runtime
|
|
1934
|
-
* populate resolution, and brands the return type with
|
|
1935
|
-
* `RefMarker<TCollection>` so `RefFields<T>` can extract refs
|
|
1936
|
-
* at the type level.
|
|
1937
|
-
*
|
|
1938
|
-
* The field's output type must match the target collection's `_id` type.
|
|
1939
|
-
* Mismatched types produce a compile error.
|
|
1940
|
-
*
|
|
1941
|
-
* Apply `.ref()` before wrapper methods like `.optional()` or `.nullable()`:
|
|
1942
|
-
* `objectId().ref(Users).optional()` — not `objectId().optional().ref(Users)`.
|
|
1943
|
-
*
|
|
1944
|
-
* @param collection - The target collection definition object.
|
|
1945
|
-
* @returns The same schema instance, branded with the ref marker.
|
|
1946
|
-
*
|
|
1947
|
-
* @example
|
|
1948
|
-
* ```ts
|
|
1949
|
-
* const Posts = collection('posts', {
|
|
1950
|
-
* authorId: objectId().ref(Users),
|
|
1951
|
-
* title: z.string(),
|
|
1952
|
-
* })
|
|
1953
|
-
* ```
|
|
1954
|
-
*/
|
|
1955
|
-
ref<TCollection extends AnyCollection>(collection: TCollection & (this['_zod']['output'] extends InferDocument<TCollection>['_id'] ? unknown : never)): this & RefMarker<TCollection>;
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
/**
|
|
1959
|
-
* Retrieve the ref metadata attached to a Zod schema, if any.
|
|
1960
|
-
*
|
|
1961
|
-
* Returns `undefined` when the schema was never marked with `.ref()`.
|
|
1962
|
-
*
|
|
1963
|
-
* @param schema - The Zod schema to inspect. Accepts `unknown` for
|
|
1964
|
-
* convenience; non-object values safely return `undefined`.
|
|
1965
|
-
* @returns The {@link RefMetadata} for the schema, or `undefined`.
|
|
1966
|
-
*
|
|
1967
|
-
* @example
|
|
1968
|
-
* ```ts
|
|
1969
|
-
* const authorId = objectId().ref(Users)
|
|
1970
|
-
* const meta = getRefMetadata(authorId)
|
|
1971
|
-
* // => { collection: Users }
|
|
1972
|
-
* ```
|
|
1973
|
-
*/
|
|
1974
|
-
declare function getRefMetadata(schema: unknown): RefMetadata | undefined;
|
|
1975
|
-
|
|
1976
3547
|
/**
|
|
1977
3548
|
* Shorthand for `CollectionHandle<TDef>`.
|
|
1978
3549
|
*
|
|
@@ -2038,4 +3609,4 @@ type UpdateFilterOf<TDef extends AnyCollection> = TypedUpdateFilter<InferDocumen
|
|
|
2038
3609
|
*/
|
|
2039
3610
|
type SortOf<TDef extends AnyCollection> = TypedSort<InferDocument<TDef>>;
|
|
2040
3611
|
|
|
2041
|
-
export { $, $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AddToSetEach, type AddToSetFields, type AnyCollection, type ArrayElement, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, type CurrentDateFields, type CursorPage, type CursorPaginateOptions, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FilterOf, type FindOneAndDeleteOptions, type FindOneAndUpdateOptions, type FindOneOptions, type FindOptions, type HandleOf, type IncFields, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type InferInsert, type OffsetPage, type OffsetPaginateOptions, type PopFields, type PullFields, type PushFields, type PushModifiers, type RefMarker, type RefMetadata, type RenameFields, type ResolvedShape, type SetFields, type SortOf, type TypedFilter, TypedFindCursor, type TypedSort, type TypedUpdateFilter, type UnsetFields, type UpdateFilterOf, type UpdateOptions, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, collection, createClient, deleteMany, deleteOne, extractDbName, extractFieldIndexes, find, findOne, findOneAndDelete, findOneAndUpdate, findOneOrThrow, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, isOid, objectId, oid, raw, updateMany, updateOne };
|
|
3612
|
+
export { $, $addToSet, $and, $avg, $count, $eq, $exists, $first, $gt, $gte, $in, $last, $lt, $lte, $max, $min, $ne, $nin, $nor, $not, $or, $push, $regex, $sum, type Accumulator, type AccumulatorBuilder, type AddToSetEach, type AddToSetFields, AggregatePipeline, type AnyCollection, type ArrayElement, type CollectionDefinition, CollectionHandle, type CollectionName, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, type CurrentDateFields, type CursorPage, type CursorPaginateOptions, Database, type DotPathType, type DotPaths, type Expression, type ExpressionBuilder, type ExtractRefCollection, type FieldIndexDefinition, type FieldRef, type FieldRefType, type FilterOf, type FindOneAndDeleteOptions, type FindOneAndUpdateOptions, type FindOneOptions, type FindOptions, type GroupByCompoundResult, type GroupByResult, type HandleOf, type IncFields, IndexBuilder, type IndexMetadata, type IndexNames, type IndexOptions, type IndexSpec, type InferAccumulator, type InferAccumulators, type InferAddedFields, type InferDocument, type InferExpression, type InferInsert, type NarrowFromFilter, type OffsetPage, type OffsetPaginateOptions, type PopFields, type Prettify, type PullFields, type PushFields, type PushModifiers, type RefFields, type RefMarker, type RefMetadata, type RenameFields, type ResolvedShape, type SetFields, type SortOf, type StaleIndex, type SyncIndexesOptions, type SyncIndexesResult, type TypedFilter, TypedFindCursor, type TypedSort, type TypedUpdateFilter, type UnsetFields, type UnwindResult, type UpdateFilterOf, type UpdateOptions, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, aggregate, checkUnindexedFields, collection, createAccumulatorBuilder, createClient, createExpressionBuilder, deleteMany, deleteOne, extractComparableOptions, extractDbName, extractFieldIndexes, find, findOne, findOneAndDelete, findOneAndUpdate, findOneOrThrow, generateIndexName, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, isOid, objectId, oid, raw, serializeIndexKey, syncIndexes, toCompoundIndexSpec, toFieldIndexSpec, updateMany, updateOne };
|