@zodmon/core 0.10.0 → 0.12.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 +991 -234
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1914 -344
- package/dist/index.d.ts +1914 -344
- package/dist/index.js +980 -234
- 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, Document, DeleteResult, FindCursor,
|
|
1
|
+
import { ObjectId, Document, DeleteResult, Collection, FindCursor, ClientSession, UpdateResult, MongoClientOptions } from 'mongodb';
|
|
2
2
|
import { ZodPipe, ZodCustom, ZodTransform, z, ZodDefault } from 'zod';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -260,6 +260,8 @@ type InferInsert<TDef extends {
|
|
|
260
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
261
|
readonly name: TName;
|
|
262
262
|
readonly schema: z.ZodObject<ResolvedShape<TShape>>;
|
|
263
|
+
/** Strict validation variant that throws on unknown fields. */
|
|
264
|
+
readonly strictSchema: z.ZodObject<ResolvedShape<TShape>>;
|
|
263
265
|
readonly shape: TShape;
|
|
264
266
|
readonly fieldIndexes: FieldIndexDefinition[];
|
|
265
267
|
readonly compoundIndexes: TIndexes;
|
|
@@ -319,6 +321,40 @@ type ExtractNames<T extends readonly {
|
|
|
319
321
|
type Prettify<T> = {
|
|
320
322
|
[K in keyof T]: T[K];
|
|
321
323
|
} & {};
|
|
324
|
+
/**
|
|
325
|
+
* Generates all valid dot-separated field paths for a document type.
|
|
326
|
+
*
|
|
327
|
+
* Recursively traverses nested objects and unwraps arrays to match
|
|
328
|
+
* MongoDB's implicit array traversal semantics. Both top-level keys
|
|
329
|
+
* and nested paths are included in the resulting union.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```ts
|
|
333
|
+
* type Doc = { address: { city: string }; tags: Array<{ label: string }> }
|
|
334
|
+
* type Paths = DotPath<Doc>
|
|
335
|
+
* // ^? 'address' | 'address.city' | 'tags' | 'tags.label'
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
type DotPath<T> = T extends ReadonlyArray<infer E> ? DotPath<E> : T extends Record<string, unknown> ? {
|
|
339
|
+
[K in keyof T & string]: T[K] extends ReadonlyArray<infer E> ? E extends Record<string, unknown> ? K | `${K}.${DotPath<E>}` : K : T[K] extends Record<string, unknown> ? K | `${K}.${DotPath<T[K]>}` : K;
|
|
340
|
+
}[keyof T & string] : never;
|
|
341
|
+
/**
|
|
342
|
+
* Resolves the value type at a dot-separated path within a document type.
|
|
343
|
+
*
|
|
344
|
+
* Splits the path on `.` and recursively descends into nested types.
|
|
345
|
+
* Unwraps arrays at intermediate levels to match MongoDB's implicit
|
|
346
|
+
* array traversal. Returns `never` for invalid paths.
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```ts
|
|
350
|
+
* type Doc = { address: { city: string }; tags: Array<{ label: string }> }
|
|
351
|
+
* type City = DotPathValue<Doc, 'address.city'>
|
|
352
|
+
* // ^? string
|
|
353
|
+
* type Label = DotPathValue<Doc, 'tags.label'>
|
|
354
|
+
* // ^? string
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
type DotPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? T[K] extends ReadonlyArray<infer E> ? DotPathValue<E, Rest> : DotPathValue<T[K], Rest> : never : P extends keyof T ? T[P] : never;
|
|
322
358
|
|
|
323
359
|
/**
|
|
324
360
|
* Type-level marker that carries the target collection type through the
|
|
@@ -397,6 +433,48 @@ declare module 'zod' {
|
|
|
397
433
|
* ```
|
|
398
434
|
*/
|
|
399
435
|
declare function getRefMetadata(schema: unknown): RefMetadata | undefined;
|
|
436
|
+
/**
|
|
437
|
+
* Peel Zod wrappers (ZodOptional, ZodNullable, ZodDefault, ZodArray)
|
|
438
|
+
* to reach the inner schema type. Used to detect RefMarker on wrapped
|
|
439
|
+
* fields like `objectId().ref(X).optional()` or `z.array(objectId().ref(X))`.
|
|
440
|
+
*
|
|
441
|
+
* Bounded by wrapper depth (1-3 layers) — no circular risk.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```ts
|
|
445
|
+
* // ZodOptional<ZodObjectId & RefMarker<typeof Users>> → ZodObjectId & RefMarker<typeof Users>
|
|
446
|
+
* type Inner = UnwrapRef<typeof schema>
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
type UnwrapRef<T> = T extends z.ZodOptional<infer U> ? UnwrapRef<U> : T extends z.ZodNullable<infer U> ? UnwrapRef<U> : T extends z.ZodDefault<infer U> ? UnwrapRef<U> : T extends z.ZodArray<infer E> ? UnwrapRef<E> : T;
|
|
450
|
+
/**
|
|
451
|
+
* Extract field names that carry `.ref()` metadata from a collection definition.
|
|
452
|
+
*
|
|
453
|
+
* Unwraps ZodArray, ZodOptional, ZodNullable, ZodDefault to detect refs on
|
|
454
|
+
* inner schemas. Works for bare refs, array refs, and optional refs.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* type PostRefs = RefFields<typeof Posts>
|
|
459
|
+
* // ^? 'authorId' | 'categoryIds' | 'reviewerId'
|
|
460
|
+
* ```
|
|
461
|
+
*/
|
|
462
|
+
type RefFields<TDef extends AnyCollection> = {
|
|
463
|
+
[K in keyof TDef['shape'] & string]: UnwrapRef<TDef['shape'][K]> extends RefMarker ? K : never;
|
|
464
|
+
}[keyof TDef['shape'] & string];
|
|
465
|
+
/**
|
|
466
|
+
* Given a collection definition and a ref-bearing field key, extract
|
|
467
|
+
* the target collection type from the `RefMarker<TCollection>` phantom.
|
|
468
|
+
*
|
|
469
|
+
* Unwraps Zod wrappers to reach the inner RefMarker.
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* ```ts
|
|
473
|
+
* type Target = ExtractRefCollection<typeof Posts, 'authorId'>
|
|
474
|
+
* // ^? typeof Users
|
|
475
|
+
* ```
|
|
476
|
+
*/
|
|
477
|
+
type ExtractRefCollection<TDef extends AnyCollection, K extends RefFields<TDef>> = UnwrapRef<TDef['shape'][K]> extends RefMarker<infer TCol> ? TCol : never;
|
|
400
478
|
|
|
401
479
|
/**
|
|
402
480
|
* Branded wrapper for accumulator expressions used inside `groupBy`.
|
|
@@ -448,21 +526,25 @@ type InferAccumulators<T extends Record<string, Accumulator>> = {
|
|
|
448
526
|
[K in keyof T]: InferAccumulator<T[K]>;
|
|
449
527
|
};
|
|
450
528
|
/**
|
|
451
|
-
* Field reference string prefixed with `$`, constrained to
|
|
529
|
+
* Field reference string prefixed with `$`, constrained to dot-paths of `T`.
|
|
530
|
+
*
|
|
531
|
+
* Supports both top-level fields (`$price`) and nested dot-paths
|
|
532
|
+
* (`$address.city`, `$address.geo.lat`).
|
|
452
533
|
*
|
|
453
534
|
* @example
|
|
454
535
|
* ```ts
|
|
455
|
-
* type OrderRef = FieldRef<{ price: number;
|
|
456
|
-
* // ^? '$price' | '$
|
|
536
|
+
* type OrderRef = FieldRef<{ price: number; address: { city: string } }>
|
|
537
|
+
* // ^? '$price' | '$address' | '$address.city'
|
|
457
538
|
* ```
|
|
458
539
|
*/
|
|
459
|
-
type FieldRef<T> = `$${
|
|
540
|
+
type FieldRef<T> = `$${DotPath<T>}`;
|
|
460
541
|
/**
|
|
461
542
|
* Typed accumulator factory passed to the `groupBy` callback.
|
|
462
543
|
*
|
|
463
|
-
* Each method constrains field names to `
|
|
464
|
-
* IDE autocomplete and compile-time validation
|
|
465
|
-
*
|
|
544
|
+
* Each method constrains field names to `DotPath<T>`, providing
|
|
545
|
+
* IDE autocomplete and compile-time validation for both top-level
|
|
546
|
+
* and nested dot-path fields. Return types resolve to the actual
|
|
547
|
+
* field type via `DotPathValue<T, F>`, not `unknown`.
|
|
466
548
|
*
|
|
467
549
|
* @typeParam T - The current pipeline output document type.
|
|
468
550
|
*
|
|
@@ -470,10 +552,10 @@ type FieldRef<T> = `$${keyof T & string}`;
|
|
|
470
552
|
* ```ts
|
|
471
553
|
* users.aggregate()
|
|
472
554
|
* .groupBy('role', acc => ({
|
|
473
|
-
* minSalary: acc.min('salary'),
|
|
474
|
-
* firstName: acc.first('name'),
|
|
475
|
-
*
|
|
476
|
-
* count: acc.count(),
|
|
555
|
+
* minSalary: acc.min('salary'), // Accumulator<number>
|
|
556
|
+
* firstName: acc.first('name'), // Accumulator<string>
|
|
557
|
+
* allCities: acc.push('address.city'), // Accumulator<string[]>
|
|
558
|
+
* count: acc.count(), // Accumulator<number>
|
|
477
559
|
* }))
|
|
478
560
|
* ```
|
|
479
561
|
*/
|
|
@@ -481,64 +563,109 @@ type AccumulatorBuilder<T> = {
|
|
|
481
563
|
/** Count documents in each group. Always returns `Accumulator<number>`. */
|
|
482
564
|
count(): Accumulator<number>;
|
|
483
565
|
/** Sum a numeric field. Always returns `Accumulator<number>`. */
|
|
484
|
-
sum
|
|
566
|
+
sum(field: DotPath<T>): Accumulator<number>;
|
|
485
567
|
/** Sum a literal number per document. Always returns `Accumulator<number>`. */
|
|
486
568
|
sum(value: number): Accumulator<number>;
|
|
487
569
|
/** Average a numeric field. Always returns `Accumulator<number>`. */
|
|
488
|
-
avg
|
|
489
|
-
/** Minimum value of a field. Returns `Accumulator<T
|
|
490
|
-
min<
|
|
491
|
-
/** Maximum value of a field. Returns `Accumulator<T
|
|
492
|
-
max<
|
|
493
|
-
/** First value in each group (by document order). Returns `Accumulator<T
|
|
494
|
-
first<
|
|
495
|
-
/** Last value in each group (by document order). Returns `Accumulator<T
|
|
496
|
-
last<
|
|
497
|
-
/** Collect values into an array (with duplicates). Returns `Accumulator<T[
|
|
498
|
-
push<
|
|
499
|
-
/** Collect unique values into an array. Returns `Accumulator<T[
|
|
500
|
-
addToSet<
|
|
570
|
+
avg(field: DotPath<T>): Accumulator<number>;
|
|
571
|
+
/** Minimum value of a field. Returns `Accumulator<DotPathValue<T, F>>`. */
|
|
572
|
+
min<F extends DotPath<T>>(field: F): Accumulator<DotPathValue<T, F>>;
|
|
573
|
+
/** Maximum value of a field. Returns `Accumulator<DotPathValue<T, F>>`. */
|
|
574
|
+
max<F extends DotPath<T>>(field: F): Accumulator<DotPathValue<T, F>>;
|
|
575
|
+
/** First value in each group (by document order). Returns `Accumulator<DotPathValue<T, F>>`. */
|
|
576
|
+
first<F extends DotPath<T>>(field: F): Accumulator<DotPathValue<T, F>>;
|
|
577
|
+
/** Last value in each group (by document order). Returns `Accumulator<DotPathValue<T, F>>`. */
|
|
578
|
+
last<F extends DotPath<T>>(field: F): Accumulator<DotPathValue<T, F>>;
|
|
579
|
+
/** Collect values into an array (with duplicates). Returns `Accumulator<DotPathValue<T, F>[]>`. */
|
|
580
|
+
push<F extends DotPath<T>>(field: F): Accumulator<DotPathValue<T, F>[]>;
|
|
581
|
+
/** Collect unique values into an array. Returns `Accumulator<DotPathValue<T, F>[]>`. */
|
|
582
|
+
addToSet<F extends DotPath<T>>(field: F): Accumulator<DotPathValue<T, F>[]>;
|
|
501
583
|
};
|
|
502
584
|
/**
|
|
503
585
|
* Resolves the document field type from a `$`-prefixed field reference.
|
|
504
586
|
*
|
|
587
|
+
* Supports both top-level refs (`$price`) and dot-path refs
|
|
588
|
+
* (`$address.city`), resolving through nested objects and arrays.
|
|
589
|
+
*
|
|
505
590
|
* @example
|
|
506
591
|
* ```ts
|
|
507
|
-
* type Order = { price: number;
|
|
592
|
+
* type Order = { price: number; address: { city: string } }
|
|
508
593
|
* type PriceType = FieldRefType<Order, '$price'>
|
|
509
594
|
* // ^? number
|
|
595
|
+
* type CityType = FieldRefType<Order, '$address.city'>
|
|
596
|
+
* // ^? string
|
|
510
597
|
* ```
|
|
511
598
|
*/
|
|
512
|
-
type FieldRefType<T, F extends string> = F extends `$${infer K}` ? K extends
|
|
599
|
+
type FieldRefType<T, F extends string> = F extends `$${infer K}` ? DotPathValue<T, K> extends never ? never : DotPathValue<T, K> : never;
|
|
513
600
|
/**
|
|
514
601
|
* Output shape of a single-field `groupBy` stage.
|
|
515
602
|
*
|
|
516
603
|
* The `_id` field holds the grouped-by field's value, and the rest of the
|
|
517
|
-
* shape comes from the inferred accumulator types.
|
|
604
|
+
* shape comes from the inferred accumulator types. Supports dot-path keys
|
|
605
|
+
* for grouping by nested fields.
|
|
518
606
|
*
|
|
519
607
|
* @example
|
|
520
608
|
* ```ts
|
|
521
609
|
* type Result = GroupByResult<Order, 'status', { count: Accumulator<number> }>
|
|
522
610
|
* // ^? { _id: string; count: number }
|
|
611
|
+
*
|
|
612
|
+
* type Nested = GroupByResult<Order, 'address.city', { count: Accumulator<number> }>
|
|
613
|
+
* // ^? { _id: string; count: number }
|
|
523
614
|
* ```
|
|
524
615
|
*/
|
|
525
|
-
type GroupByResult<T, K extends
|
|
526
|
-
_id: T
|
|
616
|
+
type GroupByResult<T, K extends DotPath<T>, TAccum extends Record<string, Accumulator>> = Prettify<{
|
|
617
|
+
_id: DotPathValue<T, K & string>;
|
|
527
618
|
} & InferAccumulators<TAccum>>;
|
|
619
|
+
/**
|
|
620
|
+
* Replace dots with underscores in a string type.
|
|
621
|
+
*
|
|
622
|
+
* Used to sanitize dot-path keys for compound `groupBy` `_id` sub-documents,
|
|
623
|
+
* since MongoDB forbids dots in field names.
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```ts
|
|
627
|
+
* type R = ReplaceDots<'address.city'>
|
|
628
|
+
* // ^? 'address_city'
|
|
629
|
+
* ```
|
|
630
|
+
*/
|
|
631
|
+
type ReplaceDots<S extends string> = S extends `${infer A}.${infer B}` ? `${A}_${ReplaceDots<B>}` : S;
|
|
528
632
|
/**
|
|
529
633
|
* Output shape of a compound (multi-field) `groupBy` stage.
|
|
530
634
|
*
|
|
531
635
|
* The `_id` field is an object with one property per grouped field,
|
|
532
636
|
* and the rest of the shape comes from the inferred accumulator types.
|
|
637
|
+
* Dot-path keys are sanitized (dots replaced with underscores) because
|
|
638
|
+
* MongoDB forbids dots in field names.
|
|
533
639
|
*
|
|
534
640
|
* @example
|
|
535
641
|
* ```ts
|
|
536
642
|
* type Result = GroupByCompoundResult<Order, 'status' | 'region', { count: Accumulator<number> }>
|
|
537
|
-
* // ^? { _id:
|
|
643
|
+
* // ^? { _id: { status: string; region: string }; count: number }
|
|
644
|
+
*
|
|
645
|
+
* type Nested = GroupByCompoundResult<Order, 'name' | 'address.city', { count: Accumulator<number> }>
|
|
646
|
+
* // ^? { _id: { name: string; address_city: string }; count: number }
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
type GroupByCompoundResult<T, K extends DotPath<T>, TAccum extends Record<string, Accumulator>> = Prettify<{
|
|
650
|
+
_id: Prettify<{
|
|
651
|
+
[P in K & string as ReplaceDots<P>]: DotPathValue<T, P>;
|
|
652
|
+
}>;
|
|
653
|
+
} & InferAccumulators<TAccum>>;
|
|
654
|
+
/**
|
|
655
|
+
* Output shape of a null-key `groupBy` stage (global aggregation).
|
|
656
|
+
*
|
|
657
|
+
* When `groupBy(null, ...)` is used, MongoDB collapses all documents
|
|
658
|
+
* into a single group with `_id: null`. The rest of the shape comes
|
|
659
|
+
* from the inferred accumulator types.
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```ts
|
|
663
|
+
* type Result = GroupByNullResult<{ total: Accumulator<number> }>
|
|
664
|
+
* // ^? { _id: null; total: number }
|
|
538
665
|
* ```
|
|
539
666
|
*/
|
|
540
|
-
type
|
|
541
|
-
_id:
|
|
667
|
+
type GroupByNullResult<TAccum extends Record<string, Accumulator>> = Prettify<{
|
|
668
|
+
_id: null;
|
|
542
669
|
} & InferAccumulators<TAccum>>;
|
|
543
670
|
/**
|
|
544
671
|
* Output shape after unwinding an array field.
|
|
@@ -591,29 +718,6 @@ type NarrowFromFilter<T, F> = {
|
|
|
591
718
|
$nin: ReadonlyArray<infer V extends T[K]>;
|
|
592
719
|
} ? Exclude<T[K], V> : T[K] : T[K];
|
|
593
720
|
};
|
|
594
|
-
/**
|
|
595
|
-
* Extract field keys from a collection definition whose schema fields
|
|
596
|
-
* carry `RefMarker` (i.e. fields that have `.ref()` metadata).
|
|
597
|
-
*
|
|
598
|
-
* @example
|
|
599
|
-
* ```ts
|
|
600
|
-
* type EmpRefs = RefFields<typeof Employees> // → 'departmentId'
|
|
601
|
-
* ```
|
|
602
|
-
*/
|
|
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];
|
|
606
|
-
/**
|
|
607
|
-
* Given a collection definition and a ref-bearing field key, extract
|
|
608
|
-
* the target collection type from the `RefMarker<TCollection>` phantom.
|
|
609
|
-
*
|
|
610
|
-
* @example
|
|
611
|
-
* ```ts
|
|
612
|
-
* type Target = ExtractRefCollection<typeof Employees, 'departmentId'>
|
|
613
|
-
* // → typeof Departments
|
|
614
|
-
* ```
|
|
615
|
-
*/
|
|
616
|
-
type ExtractRefCollection<TDef extends AnyCollection, K extends RefFields<TDef>> = TDef['shape'][K] extends RefMarker<infer TCol> ? TCol : never;
|
|
617
721
|
/**
|
|
618
722
|
* Extract the collection name string literal from a collection definition.
|
|
619
723
|
*
|
|
@@ -670,12 +774,44 @@ type InferExpression<T> = T extends Expression<infer R> ? R : unknown;
|
|
|
670
774
|
type InferAddedFields<T extends Record<string, unknown>> = {
|
|
671
775
|
[K in keyof T]: InferExpression<T[K]>;
|
|
672
776
|
};
|
|
777
|
+
/**
|
|
778
|
+
* Accepts either a dot-path field reference or a pre-built `Expression<R>`.
|
|
779
|
+
*
|
|
780
|
+
* Used throughout `ExpressionBuilder` to allow method inputs to be either a
|
|
781
|
+
* field name string (auto-prefixed with `$` at runtime) or the result of a
|
|
782
|
+
* previous expression call (embedded directly into the pipeline stage).
|
|
783
|
+
*
|
|
784
|
+
* @typeParam T - The current pipeline document type.
|
|
785
|
+
* @typeParam R - The runtime value type produced by this input.
|
|
786
|
+
*
|
|
787
|
+
* @example
|
|
788
|
+
* ```ts
|
|
789
|
+
* // Field path (common case)
|
|
790
|
+
* expr.round('salary', 2)
|
|
791
|
+
*
|
|
792
|
+
* // Composed expression
|
|
793
|
+
* expr.round(expr.multiply('salary', 0.1), 2)
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
type FieldOrExpr<T, R = unknown> = DotPath<T> | Expression<R>;
|
|
797
|
+
/**
|
|
798
|
+
* Extracts the element type from a readonly or mutable array type.
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* ```ts
|
|
802
|
+
* type Item = ArrayElement<string[]> // → string
|
|
803
|
+
* type Num = ArrayElement<number[]> // → number
|
|
804
|
+
* type N = ArrayElement<string> // → never
|
|
805
|
+
* ```
|
|
806
|
+
*/
|
|
807
|
+
type ArrayElement<T> = T extends ReadonlyArray<infer E> ? E : never;
|
|
673
808
|
/**
|
|
674
809
|
* Typed expression factory passed to the `addFields` callback.
|
|
675
810
|
*
|
|
676
|
-
* Each method
|
|
677
|
-
*
|
|
678
|
-
*
|
|
811
|
+
* Each method accepts either a `DotPath<T>` field name string (auto-prefixed
|
|
812
|
+
* with `$` at runtime) or the result of a previous expression call
|
|
813
|
+
* (`Expression<R>`, embedded directly). This enables arbitrarily deep
|
|
814
|
+
* expression composition without `.raw()`.
|
|
679
815
|
*
|
|
680
816
|
* @typeParam T - The current pipeline output document type.
|
|
681
817
|
*
|
|
@@ -683,65 +819,181 @@ type InferAddedFields<T extends Record<string, unknown>> = {
|
|
|
683
819
|
* ```ts
|
|
684
820
|
* employees.aggregate()
|
|
685
821
|
* .addFields(expr => ({
|
|
686
|
-
* hireYear: expr.year('hiredAt'),
|
|
687
|
-
*
|
|
688
|
-
*
|
|
822
|
+
* hireYear: expr.year('hiredAt'),
|
|
823
|
+
* bonus: expr.round(expr.multiply('salary', 0.1), 2),
|
|
824
|
+
* profitMargin: expr.cond(
|
|
825
|
+
* expr.eq('revenue', 0),
|
|
826
|
+
* 0,
|
|
827
|
+
* expr.round(expr.divide(expr.subtract('revenue', 'cost'), 'revenue'), 4),
|
|
828
|
+
* ),
|
|
689
829
|
* }))
|
|
690
830
|
* ```
|
|
691
831
|
*/
|
|
692
832
|
type ExpressionBuilder<T> = {
|
|
693
|
-
/** Add
|
|
694
|
-
add<
|
|
695
|
-
/** Subtract
|
|
696
|
-
subtract<
|
|
697
|
-
/** Multiply
|
|
698
|
-
multiply<
|
|
699
|
-
/** Divide a
|
|
700
|
-
divide<
|
|
701
|
-
/** Modulo of a
|
|
702
|
-
mod<
|
|
703
|
-
/** Absolute value of a numeric field. Returns `Expression<number>`. */
|
|
704
|
-
abs<
|
|
705
|
-
/** Ceiling of a numeric field. Returns `Expression<number>`. */
|
|
706
|
-
ceil<
|
|
707
|
-
/** Floor of a numeric field. Returns `Expression<number>`. */
|
|
708
|
-
floor<
|
|
709
|
-
/** Round
|
|
710
|
-
round
|
|
711
|
-
/** Concatenate field values and
|
|
712
|
-
concat(...parts: Array<
|
|
713
|
-
/** Convert a string field to lowercase. Returns `Expression<string>`. */
|
|
714
|
-
toLower<
|
|
715
|
-
/** Convert a string field to uppercase. Returns `Expression<string>`. */
|
|
716
|
-
toUpper<
|
|
717
|
-
/** Trim whitespace from a string field. Returns `Expression<string>`. */
|
|
718
|
-
trim<
|
|
719
|
-
/** Extract a substring
|
|
720
|
-
substr
|
|
721
|
-
/**
|
|
722
|
-
|
|
723
|
-
/** Test
|
|
724
|
-
|
|
725
|
-
/** Test
|
|
726
|
-
|
|
727
|
-
/** Test
|
|
728
|
-
|
|
729
|
-
/** Test if a field is
|
|
730
|
-
|
|
731
|
-
/** Test if a field is
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
|
|
737
|
-
/**
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
|
|
741
|
-
/**
|
|
742
|
-
|
|
743
|
-
/**
|
|
744
|
-
|
|
833
|
+
/** Add two numeric values. Returns `Expression<number>`. */
|
|
834
|
+
add(a: FieldOrExpr<T, number>, b: FieldOrExpr<T, number> | number): Expression<number>;
|
|
835
|
+
/** Subtract `b` from `a`. Returns `Expression<number>`. */
|
|
836
|
+
subtract(a: FieldOrExpr<T, number>, b: FieldOrExpr<T, number> | number): Expression<number>;
|
|
837
|
+
/** Multiply two numeric values. Returns `Expression<number>`. */
|
|
838
|
+
multiply(a: FieldOrExpr<T, number>, b: FieldOrExpr<T, number> | number): Expression<number>;
|
|
839
|
+
/** Divide `a` by `b`. Returns `Expression<number>`. */
|
|
840
|
+
divide(a: FieldOrExpr<T, number>, b: FieldOrExpr<T, number> | number): Expression<number>;
|
|
841
|
+
/** Modulo of `a` divided by `b`. Returns `Expression<number>`. */
|
|
842
|
+
mod(a: FieldOrExpr<T, number>, b: FieldOrExpr<T, number> | number): Expression<number>;
|
|
843
|
+
/** Absolute value of a numeric field or expression. Returns `Expression<number>`. */
|
|
844
|
+
abs(field: FieldOrExpr<T, number>): Expression<number>;
|
|
845
|
+
/** Ceiling (round up) of a numeric field or expression. Returns `Expression<number>`. */
|
|
846
|
+
ceil(field: FieldOrExpr<T, number>): Expression<number>;
|
|
847
|
+
/** Floor (round down) of a numeric field or expression. Returns `Expression<number>`. */
|
|
848
|
+
floor(field: FieldOrExpr<T, number>): Expression<number>;
|
|
849
|
+
/** Round to `place` decimal places (default 0). Returns `Expression<number>`. */
|
|
850
|
+
round(field: FieldOrExpr<T, number>, place?: number): Expression<number>;
|
|
851
|
+
/** Concatenate field values, literal strings, and string expressions. Returns `Expression<string>`. */
|
|
852
|
+
concat(...parts: Array<DotPath<T> | string | Expression<string>>): Expression<string>;
|
|
853
|
+
/** Convert a string field or expression to lowercase. Returns `Expression<string>`. */
|
|
854
|
+
toLower(field: FieldOrExpr<T, string>): Expression<string>;
|
|
855
|
+
/** Convert a string field or expression to uppercase. Returns `Expression<string>`. */
|
|
856
|
+
toUpper(field: FieldOrExpr<T, string>): Expression<string>;
|
|
857
|
+
/** Trim whitespace from a string field or expression. Returns `Expression<string>`. */
|
|
858
|
+
trim(field: FieldOrExpr<T, string>): Expression<string>;
|
|
859
|
+
/** Extract a byte-based substring. Returns `Expression<string>`. */
|
|
860
|
+
substr(field: FieldOrExpr<T, string>, start: number, length: number): Expression<string>;
|
|
861
|
+
/** Convert any value (number, date, ObjectId, etc.) to its string representation. Returns `Expression<string>`. */
|
|
862
|
+
toString(field: FieldOrExpr<T, unknown>): Expression<string>;
|
|
863
|
+
/** Test equality of a field against a value (type-safe value). Returns `Expression<boolean>`. */
|
|
864
|
+
eq<F extends DotPath<T>>(field: F, value: DotPathValue<T, F>): Expression<boolean>;
|
|
865
|
+
/** Test equality of a field against a field reference expression (field-vs-field). Returns `Expression<boolean>`. */
|
|
866
|
+
eq(field: DotPath<T>, value: Expression<unknown>): Expression<boolean>;
|
|
867
|
+
/** Test equality with a composed expression as the left-hand side. Returns `Expression<boolean>`. */
|
|
868
|
+
eq(field: Expression<unknown>, value: unknown): Expression<boolean>;
|
|
869
|
+
/** Test if a field is greater than a value (type-safe). Returns `Expression<boolean>`. */
|
|
870
|
+
gt<F extends DotPath<T>>(field: F, value: DotPathValue<T, F>): Expression<boolean>;
|
|
871
|
+
/** Test if a field is greater than a field reference expression (field-vs-field). Returns `Expression<boolean>`. */
|
|
872
|
+
gt(field: DotPath<T>, value: Expression<unknown>): Expression<boolean>;
|
|
873
|
+
/** Test if a composed expression is greater than a value. Returns `Expression<boolean>`. */
|
|
874
|
+
gt(field: Expression<unknown>, value: unknown): Expression<boolean>;
|
|
875
|
+
/** Test if a field is greater than or equal to a value (type-safe). Returns `Expression<boolean>`. */
|
|
876
|
+
gte<F extends DotPath<T>>(field: F, value: DotPathValue<T, F>): Expression<boolean>;
|
|
877
|
+
/** Test if a field is greater than or equal to a field reference expression (field-vs-field). Returns `Expression<boolean>`. */
|
|
878
|
+
gte(field: DotPath<T>, value: Expression<unknown>): Expression<boolean>;
|
|
879
|
+
/** Test if a composed expression is greater than or equal to a value. Returns `Expression<boolean>`. */
|
|
880
|
+
gte(field: Expression<unknown>, value: unknown): Expression<boolean>;
|
|
881
|
+
/** Test if a field is less than a value (type-safe). Returns `Expression<boolean>`. */
|
|
882
|
+
lt<F extends DotPath<T>>(field: F, value: DotPathValue<T, F>): Expression<boolean>;
|
|
883
|
+
/** Test if a field is less than a field reference expression (field-vs-field). Returns `Expression<boolean>`. */
|
|
884
|
+
lt(field: DotPath<T>, value: Expression<unknown>): Expression<boolean>;
|
|
885
|
+
/** Test if a composed expression is less than a value. Returns `Expression<boolean>`. */
|
|
886
|
+
lt(field: Expression<unknown>, value: unknown): Expression<boolean>;
|
|
887
|
+
/** Test if a field is less than or equal to a value (type-safe). Returns `Expression<boolean>`. */
|
|
888
|
+
lte<F extends DotPath<T>>(field: F, value: DotPathValue<T, F>): Expression<boolean>;
|
|
889
|
+
/** Test if a field is less than or equal to a field reference expression (field-vs-field). Returns `Expression<boolean>`. */
|
|
890
|
+
lte(field: DotPath<T>, value: Expression<unknown>): Expression<boolean>;
|
|
891
|
+
/** Test if a composed expression is less than or equal to a value. Returns `Expression<boolean>`. */
|
|
892
|
+
lte(field: Expression<unknown>, value: unknown): Expression<boolean>;
|
|
893
|
+
/** Test if a field is not equal to a value (type-safe). Returns `Expression<boolean>`. */
|
|
894
|
+
ne<F extends DotPath<T>>(field: F, value: DotPathValue<T, F>): Expression<boolean>;
|
|
895
|
+
/** Test if a field is not equal to a field reference expression (field-vs-field). Returns `Expression<boolean>`. */
|
|
896
|
+
ne(field: DotPath<T>, value: Expression<unknown>): Expression<boolean>;
|
|
897
|
+
/** Test if a composed expression is not equal to a value. Returns `Expression<boolean>`. */
|
|
898
|
+
ne(field: Expression<unknown>, value: unknown): Expression<boolean>;
|
|
899
|
+
/** Extract year from a date field or expression. Returns `Expression<number>`. */
|
|
900
|
+
year(field: FieldOrExpr<T, Date>): Expression<number>;
|
|
901
|
+
/** Extract month (1-12) from a date field or expression. Returns `Expression<number>`. */
|
|
902
|
+
month(field: FieldOrExpr<T, Date>): Expression<number>;
|
|
903
|
+
/** Extract day of month (1-31) from a date field or expression. Returns `Expression<number>`. */
|
|
904
|
+
dayOfMonth(field: FieldOrExpr<T, Date>): Expression<number>;
|
|
905
|
+
/** Extract day of week (1=Sunday … 7=Saturday) from a date field or expression. Returns `Expression<number>`. */
|
|
906
|
+
dayOfWeek(field: FieldOrExpr<T, Date>): Expression<number>;
|
|
907
|
+
/**
|
|
908
|
+
* Format a date field or expression as a string.
|
|
909
|
+
*
|
|
910
|
+
* Uses MongoDB format specifiers: `%Y` year, `%m` month (01-12), `%d` day (01-31), etc.
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* expr.dateToString('createdAt', '%Y-%m') // Expression<string>
|
|
915
|
+
* ```
|
|
916
|
+
*/
|
|
917
|
+
dateToString(field: FieldOrExpr<T, Date>, format: string): Expression<string>;
|
|
918
|
+
/** Current timestamp at query start (`$$NOW`). Returns `Expression<Date>`. */
|
|
919
|
+
now(): Expression<Date>;
|
|
920
|
+
/** Count elements in an array field or expression. Returns `Expression<number>`. */
|
|
921
|
+
size(field: FieldOrExpr<T, unknown[]>): Expression<number>;
|
|
922
|
+
/**
|
|
923
|
+
* Check if a value is present in an array.
|
|
924
|
+
*
|
|
925
|
+
* Named `inArray` to distinguish from the filter-level `$in` operator.
|
|
926
|
+
* The second argument may be a field/expression producing an array, or a literal array.
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* ```ts
|
|
930
|
+
* expr.inArray(expr.dayOfWeek('createdAt'), [1, 7]) // Expression<boolean>
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
933
|
+
inArray(value: FieldOrExpr<T, unknown>, array: FieldOrExpr<T, unknown[]> | ReadonlyArray<unknown>): Expression<boolean>;
|
|
934
|
+
/**
|
|
935
|
+
* Return the element at `index` from an array field (zero-based).
|
|
936
|
+
*
|
|
937
|
+
* When called with a typed field path, the element type is inferred from the schema.
|
|
938
|
+
* When called with a composed expression, the element type is `unknown`.
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* ```ts
|
|
942
|
+
* // items: Item[] in schema → Expression<Item>
|
|
943
|
+
* expr.arrayElemAt('items', 0)
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
arrayElemAt<F extends DotPath<T>>(field: F, index: number): Expression<ArrayElement<DotPathValue<T, F>>>;
|
|
947
|
+
/** Return the element at `index` from an array expression. Returns `Expression<unknown>`. */
|
|
948
|
+
arrayElemAt(field: Expression<unknown[]>, index: number): Expression<unknown>;
|
|
949
|
+
/**
|
|
950
|
+
* Conditional expression: if `condition` is true return `thenValue`, else return `elseValue`.
|
|
951
|
+
*
|
|
952
|
+
* Both `thenValue` and `elseValue` accept literal values or composed `Expression<R>` results.
|
|
953
|
+
* Returns `Expression<TThen | TElse>`.
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* ```ts
|
|
957
|
+
* expr.cond(expr.eq('revenue', 0), 0, expr.round(expr.divide('profit', 'revenue'), 4))
|
|
958
|
+
* ```
|
|
959
|
+
*/
|
|
960
|
+
cond<TThen, TElse>(condition: Expression<boolean>, thenValue: TThen | Expression<TThen>, elseValue: TElse | Expression<TElse>): Expression<TThen | TElse>;
|
|
961
|
+
/** Replace null/missing field with a default. Returns `Expression<NonNullable<DotPathValue<T, F>> | TDefault>`. */
|
|
962
|
+
ifNull<F extends DotPath<T>, TDefault>(field: F, fallback: TDefault): Expression<NonNullable<DotPathValue<T, F>> | TDefault>;
|
|
963
|
+
/** Replace null/missing expression result with a default. Returns `Expression<NonNullable<R> | TDefault>`. */
|
|
964
|
+
ifNull<R, TDefault>(field: Expression<R>, fallback: TDefault): Expression<NonNullable<R> | TDefault>;
|
|
965
|
+
/**
|
|
966
|
+
* Multi-branch conditional. Evaluates `branches` in order and returns the first matching `then` value,
|
|
967
|
+
* or `fallback` if no branch matches.
|
|
968
|
+
*
|
|
969
|
+
* TypeScript infers `TThen` as the union of all `then` value types and the fallback type.
|
|
970
|
+
*
|
|
971
|
+
* @example
|
|
972
|
+
* ```ts
|
|
973
|
+
* expr.switch([
|
|
974
|
+
* { case: expr.gte('spend', 10_000), then: 'VIP' },
|
|
975
|
+
* { case: expr.gte('spend', 5_000), then: 'Gold' },
|
|
976
|
+
* ], 'Bronze')
|
|
977
|
+
* // → Expression<'VIP' | 'Gold' | 'Bronze'>
|
|
978
|
+
* ```
|
|
979
|
+
*/
|
|
980
|
+
switch<TThen>(branches: ReadonlyArray<{
|
|
981
|
+
case: Expression<boolean>;
|
|
982
|
+
then: TThen | Expression<TThen>;
|
|
983
|
+
}>, fallback: TThen | Expression<TThen>): Expression<TThen>;
|
|
984
|
+
/**
|
|
985
|
+
* Create a typed field reference for use as a comparison operand.
|
|
986
|
+
*
|
|
987
|
+
* Use when the right-hand side of a comparison should be a document
|
|
988
|
+
* field rather than a literal value (field-vs-field comparison).
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* ```ts
|
|
992
|
+
* // Compare two fields in the same document
|
|
993
|
+
* .match({}, (expr) => expr.gt('totalAmount', expr.field('refundedAmount')))
|
|
994
|
+
* ```
|
|
995
|
+
*/
|
|
996
|
+
field<F extends DotPath<T>>(name: F): Expression<DotPathValue<T, F>>;
|
|
745
997
|
};
|
|
746
998
|
|
|
747
999
|
/**
|
|
@@ -792,139 +1044,169 @@ declare const $avg: (field: `$${string}`) => Accumulator<number>;
|
|
|
792
1044
|
/**
|
|
793
1045
|
* Returns the minimum value across documents in each group.
|
|
794
1046
|
*
|
|
795
|
-
*
|
|
796
|
-
*
|
|
1047
|
+
* Three tiers of type safety:
|
|
1048
|
+
* - **Tier 1 (document type):** `$min<Doc>('$address.city')` — resolves via `FieldRefType`.
|
|
1049
|
+
* - **Tier 2 (explicit scalar):** `$min<number>('$price')` — manual result type.
|
|
1050
|
+
* - **Tier 3 (untyped):** `$min('$price')` — defaults to `unknown`.
|
|
797
1051
|
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
* @
|
|
1052
|
+
* For best type safety, prefer the callback builder (`acc.min('price')`).
|
|
1053
|
+
*
|
|
1054
|
+
* @param field - A `$field` reference (supports dot-paths with document type param).
|
|
1055
|
+
* @returns An `Accumulator` with the resolved result type.
|
|
801
1056
|
*
|
|
802
1057
|
* @example
|
|
803
1058
|
* ```ts
|
|
1059
|
+
* // Tier 1 — document type with dot-path
|
|
1060
|
+
* $min<Doc>('$address.city') // Accumulator<string>
|
|
1061
|
+
*
|
|
804
1062
|
* // Tier 2 — explicit result type
|
|
805
|
-
*
|
|
806
|
-
* .groupBy('category', { cheapest: $min<number>('$price') })
|
|
1063
|
+
* $min<number>('$price') // Accumulator<number>
|
|
807
1064
|
*
|
|
808
|
-
* // Tier
|
|
809
|
-
*
|
|
810
|
-
* .groupBy('category', acc => ({ cheapest: acc.min('price') }))
|
|
1065
|
+
* // Tier 3 — untyped
|
|
1066
|
+
* $min('$price') // Accumulator<unknown>
|
|
811
1067
|
* ```
|
|
812
1068
|
*/
|
|
813
|
-
declare
|
|
1069
|
+
declare function $min<T extends Record<string, unknown>>(field: FieldRef<T>): Accumulator<FieldRefType<T, FieldRef<T>>>;
|
|
1070
|
+
declare function $min<R = unknown>(field: `$${string}`): Accumulator<R>;
|
|
814
1071
|
/**
|
|
815
1072
|
* Returns the maximum value across documents in each group.
|
|
816
1073
|
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
1074
|
+
* Three tiers of type safety:
|
|
1075
|
+
* - **Tier 1 (document type):** `$max<Doc>('$address.city')` — resolves via `FieldRefType`.
|
|
1076
|
+
* - **Tier 2 (explicit scalar):** `$max<number>('$price')` — manual result type.
|
|
1077
|
+
* - **Tier 3 (untyped):** `$max('$price')` — defaults to `unknown`.
|
|
1078
|
+
*
|
|
1079
|
+
* For best type safety, prefer the callback builder (`acc.max('price')`).
|
|
819
1080
|
*
|
|
820
|
-
* @
|
|
821
|
-
* @
|
|
822
|
-
* @returns An `Accumulator<R>`.
|
|
1081
|
+
* @param field - A `$field` reference (supports dot-paths with document type param).
|
|
1082
|
+
* @returns An `Accumulator` with the resolved result type.
|
|
823
1083
|
*
|
|
824
1084
|
* @example
|
|
825
1085
|
* ```ts
|
|
1086
|
+
* // Tier 1 — document type with dot-path
|
|
1087
|
+
* $max<Doc>('$tags.label') // Accumulator<string>
|
|
1088
|
+
*
|
|
826
1089
|
* // Tier 2 — explicit result type
|
|
827
|
-
*
|
|
828
|
-
* .groupBy('category', { priciest: $max<number>('$price') })
|
|
1090
|
+
* $max<number>('$price') // Accumulator<number>
|
|
829
1091
|
*
|
|
830
|
-
* // Tier
|
|
831
|
-
*
|
|
832
|
-
* .groupBy('category', acc => ({ priciest: acc.max('price') }))
|
|
1092
|
+
* // Tier 3 — untyped
|
|
1093
|
+
* $max('$price') // Accumulator<unknown>
|
|
833
1094
|
* ```
|
|
834
1095
|
*/
|
|
835
|
-
declare
|
|
1096
|
+
declare function $max<T extends Record<string, unknown>>(field: FieldRef<T>): Accumulator<FieldRefType<T, FieldRef<T>>>;
|
|
1097
|
+
declare function $max<R = unknown>(field: `$${string}`): Accumulator<R>;
|
|
836
1098
|
/**
|
|
837
1099
|
* Returns the first value in each group according to the document order.
|
|
838
1100
|
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
1101
|
+
* Three tiers of type safety:
|
|
1102
|
+
* - **Tier 1 (document type):** `$first<Doc>('$address.city')` — resolves via `FieldRefType`.
|
|
1103
|
+
* - **Tier 2 (explicit scalar):** `$first<Date>('$createdAt')` — manual result type.
|
|
1104
|
+
* - **Tier 3 (untyped):** `$first('$name')` — defaults to `unknown`.
|
|
841
1105
|
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
* @
|
|
1106
|
+
* For best type safety, prefer the callback builder (`acc.first('name')`).
|
|
1107
|
+
*
|
|
1108
|
+
* @param field - A `$field` reference (supports dot-paths with document type param).
|
|
1109
|
+
* @returns An `Accumulator` with the resolved result type.
|
|
845
1110
|
*
|
|
846
1111
|
* @example
|
|
847
1112
|
* ```ts
|
|
1113
|
+
* // Tier 1 — document type with dot-path
|
|
1114
|
+
* $first<Doc>('$address.city') // Accumulator<string>
|
|
1115
|
+
*
|
|
848
1116
|
* // Tier 2 — explicit result type
|
|
849
|
-
*
|
|
850
|
-
* .sort({ createdAt: 1 })
|
|
851
|
-
* .groupBy('status', { earliest: $first<Date>('$createdAt') })
|
|
1117
|
+
* $first<Date>('$createdAt') // Accumulator<Date>
|
|
852
1118
|
*
|
|
853
|
-
* // Tier
|
|
854
|
-
*
|
|
855
|
-
* .sort({ createdAt: 1 })
|
|
856
|
-
* .groupBy('status', acc => ({ earliest: acc.first('createdAt') }))
|
|
1119
|
+
* // Tier 3 — untyped
|
|
1120
|
+
* $first('$name') // Accumulator<unknown>
|
|
857
1121
|
* ```
|
|
858
1122
|
*/
|
|
859
|
-
declare
|
|
1123
|
+
declare function $first<T extends Record<string, unknown>>(field: FieldRef<T>): Accumulator<FieldRefType<T, FieldRef<T>>>;
|
|
1124
|
+
declare function $first<R = unknown>(field: `$${string}`): Accumulator<R>;
|
|
860
1125
|
/**
|
|
861
1126
|
* Returns the last value in each group according to the document order.
|
|
862
1127
|
*
|
|
863
|
-
*
|
|
864
|
-
*
|
|
1128
|
+
* Three tiers of type safety:
|
|
1129
|
+
* - **Tier 1 (document type):** `$last<Doc>('$address.city')` — resolves via `FieldRefType`.
|
|
1130
|
+
* - **Tier 2 (explicit scalar):** `$last<Date>('$createdAt')` — manual result type.
|
|
1131
|
+
* - **Tier 3 (untyped):** `$last('$name')` — defaults to `unknown`.
|
|
1132
|
+
*
|
|
1133
|
+
* For best type safety, prefer the callback builder (`acc.last('name')`).
|
|
865
1134
|
*
|
|
866
|
-
* @
|
|
867
|
-
* @
|
|
868
|
-
* @returns An `Accumulator<R>`.
|
|
1135
|
+
* @param field - A `$field` reference (supports dot-paths with document type param).
|
|
1136
|
+
* @returns An `Accumulator` with the resolved result type.
|
|
869
1137
|
*
|
|
870
1138
|
* @example
|
|
871
1139
|
* ```ts
|
|
1140
|
+
* // Tier 1 — document type with dot-path
|
|
1141
|
+
* $last<Doc>('$address.city') // Accumulator<string>
|
|
1142
|
+
*
|
|
872
1143
|
* // Tier 2 — explicit result type
|
|
873
|
-
*
|
|
874
|
-
* .sort({ createdAt: -1 })
|
|
875
|
-
* .groupBy('status', { latest: $last<Date>('$createdAt') })
|
|
1144
|
+
* $last<Date>('$createdAt') // Accumulator<Date>
|
|
876
1145
|
*
|
|
877
|
-
* // Tier
|
|
878
|
-
*
|
|
879
|
-
* .sort({ createdAt: -1 })
|
|
880
|
-
* .groupBy('status', acc => ({ latest: acc.last('createdAt') }))
|
|
1146
|
+
* // Tier 3 — untyped
|
|
1147
|
+
* $last('$name') // Accumulator<unknown>
|
|
881
1148
|
* ```
|
|
882
1149
|
*/
|
|
883
|
-
declare
|
|
1150
|
+
declare function $last<T extends Record<string, unknown>>(field: FieldRef<T>): Accumulator<FieldRefType<T, FieldRef<T>>>;
|
|
1151
|
+
declare function $last<R = unknown>(field: `$${string}`): Accumulator<R>;
|
|
884
1152
|
/**
|
|
885
1153
|
* Pushes values into an array for each group.
|
|
886
1154
|
*
|
|
887
1155
|
* May contain duplicates. Use `$addToSet` for unique values.
|
|
888
|
-
* For full type safety, prefer the callback builder (`acc.push('name')`).
|
|
889
1156
|
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
*
|
|
1157
|
+
* Three tiers of type safety:
|
|
1158
|
+
* - **Tier 1 (document type):** `$push<Doc>('$address.city')` — resolves via `FieldRefType`.
|
|
1159
|
+
* - **Tier 2 (explicit scalar):** `$push<string>('$name')` — manual element type.
|
|
1160
|
+
* - **Tier 3 (untyped):** `$push('$name')` — defaults to `unknown[]`.
|
|
1161
|
+
*
|
|
1162
|
+
* For best type safety, prefer the callback builder (`acc.push('name')`).
|
|
1163
|
+
*
|
|
1164
|
+
* @param field - A `$field` reference (supports dot-paths with document type param).
|
|
1165
|
+
* @returns An `Accumulator` with the resolved array result type.
|
|
893
1166
|
*
|
|
894
1167
|
* @example
|
|
895
1168
|
* ```ts
|
|
1169
|
+
* // Tier 1 — document type with dot-path
|
|
1170
|
+
* $push<Doc>('$address.city') // Accumulator<string[]>
|
|
1171
|
+
*
|
|
896
1172
|
* // Tier 2 — explicit element type
|
|
897
|
-
*
|
|
898
|
-
* .groupBy('status', { items: $push<string>('$name') })
|
|
1173
|
+
* $push<string>('$name') // Accumulator<string[]>
|
|
899
1174
|
*
|
|
900
|
-
* // Tier
|
|
901
|
-
*
|
|
902
|
-
* .groupBy('status', acc => ({ items: acc.push('name') }))
|
|
1175
|
+
* // Tier 3 — untyped
|
|
1176
|
+
* $push('$name') // Accumulator<unknown[]>
|
|
903
1177
|
* ```
|
|
904
1178
|
*/
|
|
905
|
-
declare
|
|
1179
|
+
declare function $push<T extends Record<string, unknown>>(field: FieldRef<T>): Accumulator<FieldRefType<T, FieldRef<T>>[]>;
|
|
1180
|
+
declare function $push<R = unknown>(field: `$${string}`): Accumulator<R[]>;
|
|
906
1181
|
/**
|
|
907
1182
|
* Collects unique values into an array for each group (set semantics).
|
|
908
1183
|
*
|
|
909
1184
|
* Like `$push` but deduplicates.
|
|
910
|
-
* For full type safety, prefer the callback builder (`acc.addToSet('tag')`).
|
|
911
1185
|
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
1186
|
+
* Three tiers of type safety:
|
|
1187
|
+
* - **Tier 1 (document type):** `$addToSet<Doc>('$address.zip')` — resolves via `FieldRefType`.
|
|
1188
|
+
* - **Tier 2 (explicit scalar):** `$addToSet<string>('$tag')` — manual element type.
|
|
1189
|
+
* - **Tier 3 (untyped):** `$addToSet('$tag')` — defaults to `unknown[]`.
|
|
1190
|
+
*
|
|
1191
|
+
* For best type safety, prefer the callback builder (`acc.addToSet('tag')`).
|
|
1192
|
+
*
|
|
1193
|
+
* @param field - A `$field` reference (supports dot-paths with document type param).
|
|
1194
|
+
* @returns An `Accumulator` with the resolved array result type.
|
|
915
1195
|
*
|
|
916
1196
|
* @example
|
|
917
1197
|
* ```ts
|
|
1198
|
+
* // Tier 1 — document type with dot-path
|
|
1199
|
+
* $addToSet<Doc>('$address.zip') // Accumulator<string[]>
|
|
1200
|
+
*
|
|
918
1201
|
* // Tier 2 — explicit element type
|
|
919
|
-
*
|
|
920
|
-
* .groupBy('category', { uniqueTags: $addToSet<string>('$tag') })
|
|
1202
|
+
* $addToSet<string>('$tag') // Accumulator<string[]>
|
|
921
1203
|
*
|
|
922
|
-
* // Tier
|
|
923
|
-
*
|
|
924
|
-
* .groupBy('category', acc => ({ uniqueTags: acc.addToSet('tag') }))
|
|
1204
|
+
* // Tier 3 — untyped
|
|
1205
|
+
* $addToSet('$tag') // Accumulator<unknown[]>
|
|
925
1206
|
* ```
|
|
926
1207
|
*/
|
|
927
|
-
declare
|
|
1208
|
+
declare function $addToSet<T extends Record<string, unknown>>(field: FieldRef<T>): Accumulator<FieldRefType<T, FieldRef<T>>[]>;
|
|
1209
|
+
declare function $addToSet<R = unknown>(field: `$${string}`): Accumulator<R[]>;
|
|
928
1210
|
/**
|
|
929
1211
|
* Create a typed accumulator builder for use inside `groupBy` callbacks.
|
|
930
1212
|
*
|
|
@@ -947,19 +1229,20 @@ declare function createAccumulatorBuilder<T>(): AccumulatorBuilder<T>;
|
|
|
947
1229
|
/**
|
|
948
1230
|
* Create a typed expression builder for use inside `addFields` callbacks.
|
|
949
1231
|
*
|
|
950
|
-
* The builder
|
|
951
|
-
*
|
|
952
|
-
* `AggregatePipeline.addFields` — most users
|
|
953
|
-
* the callback parameter rather than calling
|
|
1232
|
+
* The builder accepts either a field name string (auto-prefixed with `$`) or a
|
|
1233
|
+
* pre-built `Expression<R>` from another builder method, enabling composition.
|
|
1234
|
+
* Primarily used internally by `AggregatePipeline.addFields` — most users
|
|
1235
|
+
* interact with the builder via the callback parameter rather than calling
|
|
1236
|
+
* this directly.
|
|
954
1237
|
*
|
|
955
1238
|
* @typeParam T - The current pipeline output document type.
|
|
956
1239
|
* @returns An `ExpressionBuilder<T>` with methods for each MongoDB expression operator.
|
|
957
1240
|
*
|
|
958
1241
|
* @example
|
|
959
1242
|
* ```ts
|
|
960
|
-
* const expr = createExpressionBuilder<{ salary: number;
|
|
961
|
-
* expr.year('hiredAt')
|
|
962
|
-
* expr.multiply('salary', 0.
|
|
1243
|
+
* const expr = createExpressionBuilder<{ salary: number; hiredAt: Date }>()
|
|
1244
|
+
* expr.year('hiredAt') // { __expr: true, value: { $year: '$hiredAt' } }
|
|
1245
|
+
* expr.round(expr.multiply('salary', 0.1), 2) // { __expr: true, value: { $round: [{ $multiply: ['$salary', 0.1] }, 2] } }
|
|
963
1246
|
* ```
|
|
964
1247
|
*/
|
|
965
1248
|
declare function createExpressionBuilder<T>(): ExpressionBuilder<T>;
|
|
@@ -1005,40 +1288,21 @@ type ComparisonOperators<V> = {
|
|
|
1005
1288
|
} & (V extends string ? {
|
|
1006
1289
|
$regex?: RegExp | string;
|
|
1007
1290
|
} : unknown);
|
|
1008
|
-
/** Depth counter for limiting dot-notation recursion. Index = current depth, value = next depth. */
|
|
1009
|
-
type Prev = [never, 0, 1, 2];
|
|
1010
|
-
/**
|
|
1011
|
-
* Generates a union of all valid dot-separated paths for nested object fields in `T`.
|
|
1012
|
-
*
|
|
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.
|
|
1016
|
-
*
|
|
1017
|
-
* @example
|
|
1018
|
-
* ```ts
|
|
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'
|
|
1022
|
-
* ```
|
|
1023
|
-
*/
|
|
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];
|
|
1027
1291
|
/**
|
|
1028
|
-
*
|
|
1292
|
+
* Dot-separated paths for nested fields, excluding top-level keys.
|
|
1029
1293
|
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1294
|
+
* Filters out `keyof T & string` because top-level fields are already
|
|
1295
|
+
* handled by slot (A) of {@link TypedFilter}. Keeping them separate
|
|
1296
|
+
* preserves the intersection structure for lazy TypeScript evaluation.
|
|
1032
1297
|
*
|
|
1033
1298
|
* @example
|
|
1034
1299
|
* ```ts
|
|
1035
|
-
* type User = {
|
|
1036
|
-
*
|
|
1037
|
-
* //
|
|
1038
|
-
* // DotPathType<User, 'address.geo.lat'> = number
|
|
1300
|
+
* type User = { name: string; address: { city: string } }
|
|
1301
|
+
* type Paths = DotSubPaths<User>
|
|
1302
|
+
* // ^? 'address.city'
|
|
1039
1303
|
* ```
|
|
1040
1304
|
*/
|
|
1041
|
-
type
|
|
1305
|
+
type DotSubPaths<T> = Exclude<DotPath<T>, keyof T & string>;
|
|
1042
1306
|
/**
|
|
1043
1307
|
* Strict type-safe MongoDB filter query type.
|
|
1044
1308
|
*
|
|
@@ -1049,7 +1313,7 @@ type DotPathType<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K
|
|
|
1049
1313
|
* Supports three forms of filter expressions:
|
|
1050
1314
|
* - **Direct field values** (implicit `$eq`): `{ name: 'Alice' }`
|
|
1051
1315
|
* - **Comparison operators**: `{ age: { $gt: 25 } }` or `{ age: $gt(25) }`
|
|
1052
|
-
* - **Dot notation**
|
|
1316
|
+
* - **Dot notation** including nested and array-traversed fields: `{ 'address.city': 'NYC' }`
|
|
1053
1317
|
*
|
|
1054
1318
|
* Logical operators `$and`, `$or`, and `$nor` accept arrays of `TypedFilter<T>`
|
|
1055
1319
|
* for composing complex queries.
|
|
@@ -1078,7 +1342,7 @@ type DotPathType<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K
|
|
|
1078
1342
|
type TypedFilter<T> = {
|
|
1079
1343
|
[K in keyof T]?: T[K] | ComparisonOperators<T[K]>;
|
|
1080
1344
|
} & {
|
|
1081
|
-
[P in
|
|
1345
|
+
[P in DotSubPaths<T>]?: DotPathValue<T, P> | ComparisonOperators<DotPathValue<T, P>>;
|
|
1082
1346
|
} & {
|
|
1083
1347
|
/** Joins clauses with a logical AND. Matches documents that satisfy all filters. */
|
|
1084
1348
|
$and?: TypedFilter<T>[];
|
|
@@ -1239,72 +1503,602 @@ type CursorPage<TDoc> = {
|
|
|
1239
1503
|
endCursor: string | null;
|
|
1240
1504
|
};
|
|
1241
1505
|
|
|
1506
|
+
type IncludeValue = 1 | true;
|
|
1507
|
+
type ExcludeValue = 0 | false;
|
|
1242
1508
|
/**
|
|
1243
|
-
*
|
|
1509
|
+
* Inclusion projection — maps document fields to `1 | true`.
|
|
1244
1510
|
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1511
|
+
* Allows `_id: 0 | false` to suppress the `_id` field from the result.
|
|
1512
|
+
* All other fields only accept inclusion values (`1` or `true`).
|
|
1247
1513
|
*
|
|
1248
1514
|
* @example
|
|
1249
1515
|
* ```ts
|
|
1250
|
-
*
|
|
1516
|
+
* type UserInclusion = InclusionProjection<User>
|
|
1517
|
+
* const proj: UserInclusion = { name: 1, email: 1 }
|
|
1518
|
+
* const withoutId: UserInclusion = { name: 1, _id: 0 }
|
|
1251
1519
|
* ```
|
|
1252
1520
|
*/
|
|
1253
|
-
type
|
|
1521
|
+
type InclusionProjection<T> = {
|
|
1522
|
+
[K in keyof T & string]?: K extends '_id' ? IncludeValue | ExcludeValue : IncludeValue;
|
|
1523
|
+
};
|
|
1254
1524
|
/**
|
|
1255
|
-
*
|
|
1525
|
+
* Exclusion projection — maps document fields to `0 | false`.
|
|
1256
1526
|
*
|
|
1257
|
-
*
|
|
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.
|
|
1527
|
+
* Removes the specified fields from the result, keeping all others.
|
|
1261
1528
|
*
|
|
1262
|
-
*
|
|
1529
|
+
* @example
|
|
1530
|
+
* ```ts
|
|
1531
|
+
* type UserExclusion = ExclusionProjection<User>
|
|
1532
|
+
* const proj: UserExclusion = { email: 0, age: 0 }
|
|
1533
|
+
* ```
|
|
1534
|
+
*/
|
|
1535
|
+
type ExclusionProjection<T> = {
|
|
1536
|
+
[K in keyof T & string]?: ExcludeValue;
|
|
1537
|
+
};
|
|
1538
|
+
/**
|
|
1539
|
+
* Union of inclusion or exclusion projection for a document type `T`.
|
|
1263
1540
|
*
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1541
|
+
* MongoDB projections must be either all-inclusion or all-exclusion (with the
|
|
1542
|
+
* exception of `_id`, which can always be excluded). This type enforces that
|
|
1543
|
+
* constraint at the type level.
|
|
1266
1544
|
*
|
|
1267
1545
|
* @example
|
|
1268
1546
|
* ```ts
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
* .toArray()
|
|
1547
|
+
* type UserProjection = TypedProjection<User>
|
|
1548
|
+
* const include: UserProjection = { name: 1, email: 1 }
|
|
1549
|
+
* const exclude: UserProjection = { email: 0, age: 0 }
|
|
1273
1550
|
* ```
|
|
1274
1551
|
*/
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1552
|
+
type TypedProjection<T> = InclusionProjection<T> | ExclusionProjection<T>;
|
|
1553
|
+
/** True if any key in `P` (excluding `_id`) has value `1 | true`. */
|
|
1554
|
+
type IsInclusion<P> = true extends {
|
|
1555
|
+
[K in keyof P]: K extends '_id' ? never : P[K] extends IncludeValue ? true : never;
|
|
1556
|
+
}[keyof P] ? true : false;
|
|
1557
|
+
/** Keys of `P` whose value is `1 | true` (excluding `_id`). */
|
|
1558
|
+
type IncludedKeys<P> = {
|
|
1559
|
+
[K in keyof P]: K extends '_id' ? never : P[K] extends IncludeValue ? K : never;
|
|
1560
|
+
}[keyof P];
|
|
1561
|
+
/** Keys of `P` whose value is `0 | false`. */
|
|
1562
|
+
type ExcludedKeys<P> = {
|
|
1563
|
+
[K in keyof P]: P[K] extends ExcludeValue ? K : never;
|
|
1564
|
+
}[keyof P];
|
|
1565
|
+
/** True if `P` has `_id` set to `0 | false`. */
|
|
1566
|
+
type IsIdSuppressed<P> = '_id' extends keyof P ? P['_id'] extends ExcludeValue ? true : false : false;
|
|
1567
|
+
/**
|
|
1568
|
+
* Computes the result type after applying a projection `P` to document type `T`.
|
|
1569
|
+
*
|
|
1570
|
+
* For inclusion projections (`{ name: 1 }`), returns only the included fields
|
|
1571
|
+
* plus `_id` (unless `_id: 0` is specified). For exclusion projections
|
|
1572
|
+
* (`{ email: 0 }`), returns all fields except the excluded ones.
|
|
1573
|
+
*
|
|
1574
|
+
* @example
|
|
1575
|
+
* ```ts
|
|
1576
|
+
* type User = { _id: ObjectId; name: string; email: string; age: number }
|
|
1577
|
+
*
|
|
1578
|
+
* // Inclusion: picks name + _id
|
|
1579
|
+
* type A = ProjectionResult<User, { name: 1 }>
|
|
1580
|
+
* // ^? { _id: ObjectId; name: string }
|
|
1581
|
+
*
|
|
1582
|
+
* // Inclusion with _id suppressed
|
|
1583
|
+
* type B = ProjectionResult<User, { name: 1; _id: 0 }>
|
|
1584
|
+
* // ^? { name: string }
|
|
1585
|
+
*
|
|
1586
|
+
* // Exclusion: drops email
|
|
1587
|
+
* type C = ProjectionResult<User, { email: 0 }>
|
|
1588
|
+
* // ^? { _id: ObjectId; name: string; age: number }
|
|
1589
|
+
* ```
|
|
1590
|
+
*/
|
|
1591
|
+
type ProjectionResult<T, P> = IsInclusion<P> extends true ? IsIdSuppressed<P> extends true ? Prettify<Pick<T, IncludedKeys<P> & keyof T>> : Prettify<Pick<T, (IncludedKeys<P> | '_id') & keyof T>> : IsIdSuppressed<P> extends true ? Prettify<Omit<T, ExcludedKeys<P>>> : Prettify<Omit<T, Exclude<ExcludedKeys<P>, '_id'>>>;
|
|
1592
|
+
/**
|
|
1593
|
+
* Returns `true` if the projection is an inclusion projection.
|
|
1594
|
+
*
|
|
1595
|
+
* A projection is considered inclusion when any key other than `_id` has
|
|
1596
|
+
* a value of `1` or `true`. This mirrors the MongoDB rule: you cannot mix
|
|
1597
|
+
* inclusion and exclusion (except for `_id`, which may always be suppressed).
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* ```ts
|
|
1601
|
+
* isInclusionProjection({ name: 1 }) // true
|
|
1602
|
+
* isInclusionProjection({ name: 1, _id: 0 }) // true
|
|
1603
|
+
* isInclusionProjection({ email: 0 }) // false
|
|
1604
|
+
* ```
|
|
1605
|
+
*/
|
|
1606
|
+
declare function isInclusionProjection(projection: Record<string, 0 | 1 | boolean>): boolean;
|
|
1607
|
+
/**
|
|
1608
|
+
* Derives a scoped Zod schema by applying a MongoDB-style projection.
|
|
1609
|
+
*
|
|
1610
|
+
* For inclusion projections (`{ name: 1, email: 1 }`), returns a schema with
|
|
1611
|
+
* only the specified fields plus `_id` (unless `_id: 0`). For exclusion
|
|
1612
|
+
* projections (`{ email: 0 }`), returns a schema with the specified fields
|
|
1613
|
+
* removed.
|
|
1614
|
+
*
|
|
1615
|
+
* Keys not present in the original schema are silently ignored.
|
|
1616
|
+
*
|
|
1617
|
+
* @example
|
|
1618
|
+
* ```ts
|
|
1619
|
+
* const userSchema = z.object({
|
|
1620
|
+
* _id: z.string(),
|
|
1621
|
+
* name: z.string(),
|
|
1622
|
+
* email: z.string(),
|
|
1623
|
+
* age: z.number(),
|
|
1624
|
+
* })
|
|
1625
|
+
*
|
|
1626
|
+
* // Inclusion: picks name + email + _id
|
|
1627
|
+
* const projected = deriveProjectedSchema(userSchema, { name: 1, email: 1 })
|
|
1628
|
+
*
|
|
1629
|
+
* // Exclusion: drops email
|
|
1630
|
+
* const excluded = deriveProjectedSchema(userSchema, { email: 0 })
|
|
1631
|
+
* ```
|
|
1632
|
+
*/
|
|
1633
|
+
declare function deriveProjectedSchema(schema: z.ZodObject<z.core.$ZodShape>, projection: Record<string, 0 | 1 | boolean>): z.ZodObject<z.core.$ZodShape>;
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* Typed return value from {@link PopulateRefBuilder.project}.
|
|
1637
|
+
*
|
|
1638
|
+
* Carries the projection type `P` for compile-time narrowing in populate calls.
|
|
1639
|
+
* Callers never construct this directly — it is returned by the builder and
|
|
1640
|
+
* consumed by the `.populate()` overload to narrow the result type.
|
|
1641
|
+
*
|
|
1642
|
+
* @example
|
|
1643
|
+
* ```ts
|
|
1644
|
+
* // P is inferred as { name: 1; email: 1 } — post.authorId is narrowed accordingly
|
|
1645
|
+
* const post = await posts.findOne({}).populate('authorId', (b) => b.project({ name: 1, email: 1 }))
|
|
1646
|
+
* ```
|
|
1647
|
+
*/
|
|
1648
|
+
type PopulateProjectionConfig<P> = {
|
|
1649
|
+
readonly projection: P;
|
|
1650
|
+
};
|
|
1651
|
+
/**
|
|
1652
|
+
* Fluent builder passed to populate projection callbacks.
|
|
1653
|
+
*
|
|
1654
|
+
* Call `.project()` to specify which fields to fetch from the referenced collection.
|
|
1655
|
+
* The projection type `P` flows back to the `.populate()` overload to narrow the
|
|
1656
|
+
* result type at compile time.
|
|
1657
|
+
*
|
|
1658
|
+
* @typeParam TDoc - The referenced document type (e.g. `InferDocument<typeof Users>`).
|
|
1659
|
+
*
|
|
1660
|
+
* @example
|
|
1661
|
+
* ```ts
|
|
1662
|
+
* posts.findOne({}).populate('authorId', (b) => b.project({ name: 1, email: 1 }))
|
|
1663
|
+
* // post.authorId is narrowed to { _id: ObjectId; name: string; email: string }
|
|
1664
|
+
* ```
|
|
1665
|
+
*/
|
|
1666
|
+
declare class PopulateRefBuilder<TDoc> {
|
|
1292
1667
|
/**
|
|
1293
|
-
*
|
|
1668
|
+
* Declare a projection to apply when fetching the referenced documents.
|
|
1294
1669
|
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1670
|
+
* Supported: inclusion (`{ name: 1 }`), exclusion (`{ email: 0 }`), or
|
|
1671
|
+
* `_id` suppression (`{ name: 1, _id: 0 }`).
|
|
1297
1672
|
*
|
|
1298
|
-
* @param
|
|
1299
|
-
* @returns
|
|
1673
|
+
* @param projection - MongoDB-style inclusion or exclusion projection.
|
|
1674
|
+
* @returns A config object carrying the projection type for compile-time narrowing.
|
|
1300
1675
|
*
|
|
1301
1676
|
* @example
|
|
1302
1677
|
* ```ts
|
|
1303
|
-
*
|
|
1678
|
+
* (b) => b.project({ name: 1, email: 1 })
|
|
1679
|
+
* (b) => b.project({ password: 0 })
|
|
1304
1680
|
* ```
|
|
1305
1681
|
*/
|
|
1306
|
-
|
|
1307
|
-
|
|
1682
|
+
project<P extends TypedProjection<TDoc>>(projection: P): PopulateProjectionConfig<P>;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* Resolve the populated type for a ref field, preserving wrappers.
|
|
1687
|
+
*
|
|
1688
|
+
* Transforms the field type from ID to document based on the Zod wrapper:
|
|
1689
|
+
* - `ObjectId` → `InferDocument<Target>`
|
|
1690
|
+
* - `ObjectId[]` → `InferDocument<Target>[]`
|
|
1691
|
+
* - `ObjectId | undefined` → `InferDocument<Target> | undefined`
|
|
1692
|
+
* - `ObjectId | null` → `InferDocument<Target> | null`
|
|
1693
|
+
*
|
|
1694
|
+
* @example
|
|
1695
|
+
* ```ts
|
|
1696
|
+
* type Author = PopulateField<typeof Posts, 'authorId'>
|
|
1697
|
+
* // ^? { _id: ObjectId; name: string; email: string; companyId: ObjectId }
|
|
1698
|
+
*
|
|
1699
|
+
* type Cats = PopulateField<typeof Posts, 'categoryIds'>
|
|
1700
|
+
* // ^? { _id: ObjectId; title: string }[]
|
|
1701
|
+
* ```
|
|
1702
|
+
*/
|
|
1703
|
+
type PopulateField<TDef extends AnyCollection, K extends RefFields<TDef>> = TDef['shape'][K] extends z.ZodArray<infer _E> ? InferDocument<ExtractRefCollection<TDef, K>>[] : TDef['shape'][K] extends z.ZodOptional<infer _U> ? InferDocument<ExtractRefCollection<TDef, K>> | undefined : TDef['shape'][K] extends z.ZodNullable<infer _U> ? InferDocument<ExtractRefCollection<TDef, K>> | null : InferDocument<ExtractRefCollection<TDef, K>>;
|
|
1704
|
+
/**
|
|
1705
|
+
* Document type with specified ref fields replaced by their populated types.
|
|
1706
|
+
*
|
|
1707
|
+
* @example
|
|
1708
|
+
* ```ts
|
|
1709
|
+
* type PostWithAuthor = Populated<typeof Posts, 'authorId'>
|
|
1710
|
+
* // ^? { _id: ObjectId; title: string; authorId: User; categoryIds: ObjectId[]; ... }
|
|
1711
|
+
*
|
|
1712
|
+
* type PostFull = Populated<typeof Posts, 'authorId' | 'categoryIds'>
|
|
1713
|
+
* // ^? { ...; authorId: User; categoryIds: Category[]; ... }
|
|
1714
|
+
* ```
|
|
1715
|
+
*/
|
|
1716
|
+
type Populated<TDef extends AnyCollection, K extends RefFields<TDef>> = Prettify<Omit<InferDocument<TDef>, K> & {
|
|
1717
|
+
[P in K]: PopulateField<TDef, P>;
|
|
1718
|
+
}>;
|
|
1719
|
+
/**
|
|
1720
|
+
* Apply a single populate step to the output type.
|
|
1721
|
+
*
|
|
1722
|
+
* Without rename: replaces the field type in-place.
|
|
1723
|
+
* With rename: removes the old key and adds the new key with the populated type.
|
|
1724
|
+
*
|
|
1725
|
+
* @example
|
|
1726
|
+
* ```ts
|
|
1727
|
+
* // No rename: authorId stays, type changes to User
|
|
1728
|
+
* type A = ApplyPopulate<PostDoc, typeof Posts, 'authorId'>
|
|
1729
|
+
*
|
|
1730
|
+
* // Rename: authorId removed, author added with User type
|
|
1731
|
+
* type B = ApplyPopulate<PostDoc, typeof Posts, 'authorId', 'author'>
|
|
1732
|
+
* ```
|
|
1733
|
+
*/
|
|
1734
|
+
type ApplyPopulate<T, TDef extends AnyCollection, K extends RefFields<TDef>, TAs extends string = K> = Prettify<Omit<T, K> & {
|
|
1735
|
+
[P in TAs]: PopulateField<TDef, K>;
|
|
1736
|
+
}>;
|
|
1737
|
+
/**
|
|
1738
|
+
* Resolve the projected populated type for a ref field, preserving wrappers.
|
|
1739
|
+
*
|
|
1740
|
+
* Like {@link PopulateField} but applies `ProjectionResult<T, P>` to narrow
|
|
1741
|
+
* the document type to only the projected fields.
|
|
1742
|
+
*
|
|
1743
|
+
* - `ObjectId` → `ProjectionResult<InferDocument<Target>, P>`
|
|
1744
|
+
* - `ObjectId[]` → `ProjectionResult<InferDocument<Target>, P>[]`
|
|
1745
|
+
* - `ObjectId | undefined` → `ProjectionResult<InferDocument<Target>, P> | undefined`
|
|
1746
|
+
* - `ObjectId | null` → `ProjectionResult<InferDocument<Target>, P> | null`
|
|
1747
|
+
*
|
|
1748
|
+
* @example
|
|
1749
|
+
* ```ts
|
|
1750
|
+
* type Author = PopulateFieldProjected<typeof Posts, 'authorId', { name: 1; email: 1 }>
|
|
1751
|
+
* // ^? { _id: ObjectId; name: string; email: string }
|
|
1752
|
+
* ```
|
|
1753
|
+
*/
|
|
1754
|
+
type PopulateFieldProjected<TDef extends AnyCollection, K extends RefFields<TDef>, P> = TDef['shape'][K] extends z.ZodArray<infer _E> ? ProjectionResult<InferDocument<ExtractRefCollection<TDef, K>>, P>[] : TDef['shape'][K] extends z.ZodOptional<infer _U> ? ProjectionResult<InferDocument<ExtractRefCollection<TDef, K>>, P> | undefined : TDef['shape'][K] extends z.ZodNullable<infer _U> ? ProjectionResult<InferDocument<ExtractRefCollection<TDef, K>>, P> | null : ProjectionResult<InferDocument<ExtractRefCollection<TDef, K>>, P>;
|
|
1755
|
+
/**
|
|
1756
|
+
* Apply a projected populate step to the output type.
|
|
1757
|
+
*
|
|
1758
|
+
* Like {@link ApplyPopulate} but uses {@link PopulateFieldProjected} to narrow
|
|
1759
|
+
* the result to only the projected fields. Use `TAs` to rename the output field.
|
|
1760
|
+
*
|
|
1761
|
+
* @example
|
|
1762
|
+
* ```ts
|
|
1763
|
+
* type Result = ApplyPopulateProjected<PostDoc, typeof Posts, 'authorId', { name: 1; email: 1 }>
|
|
1764
|
+
* // ^? { ...; authorId: { _id: ObjectId; name: string; email: string } }
|
|
1765
|
+
* ```
|
|
1766
|
+
*/
|
|
1767
|
+
type ApplyPopulateProjected<T, TDef extends AnyCollection, K extends RefFields<TDef>, P, TAs extends string = K> = Prettify<Omit<T, K> & {
|
|
1768
|
+
[F in TAs]: PopulateFieldProjected<TDef, K, P>;
|
|
1769
|
+
}>;
|
|
1770
|
+
/**
|
|
1771
|
+
* Apply a populate at an arbitrary nesting depth.
|
|
1772
|
+
*
|
|
1773
|
+
* Splits the dot-path and recursively navigates into the output type.
|
|
1774
|
+
* If a parent field is an array, the element type is transformed.
|
|
1775
|
+
*
|
|
1776
|
+
* @example
|
|
1777
|
+
* ```ts
|
|
1778
|
+
* // Nested: author.companyId → author.company
|
|
1779
|
+
* type Result = DeepPopulate<PostWithAuthor, 'author.companyId', 'companyId', 'company', Company>
|
|
1780
|
+
* ```
|
|
1781
|
+
*/
|
|
1782
|
+
type DeepPopulate<T, Path extends string, LeafField extends string, TAs extends string, PopType> = Path extends `${infer Parent}.${infer Rest}` ? Parent extends keyof T ? T[Parent] extends (infer E)[] ? Prettify<Omit<T, Parent> & {
|
|
1783
|
+
[P in Parent]: DeepPopulate<E, Rest, LeafField, TAs, PopType>[];
|
|
1784
|
+
}> : Prettify<Omit<T, Parent> & {
|
|
1785
|
+
[P in Parent]: DeepPopulate<T[Parent], Rest, LeafField, TAs, PopType>;
|
|
1786
|
+
}> : never : Prettify<Omit<T, LeafField> & {
|
|
1787
|
+
[P in TAs]: PopType;
|
|
1788
|
+
}>;
|
|
1789
|
+
/**
|
|
1790
|
+
* Valid nested populate paths given the current TPopMap.
|
|
1791
|
+
*
|
|
1792
|
+
* Produces a union of `parentPath.refField` strings where `parentPath`
|
|
1793
|
+
* is a key in TPopMap and `refField` is a ref-bearing field in that
|
|
1794
|
+
* parent's collection definition.
|
|
1795
|
+
*
|
|
1796
|
+
* @example
|
|
1797
|
+
* ```ts
|
|
1798
|
+
* // If TPopMap = { author: typeof Users }, produces:
|
|
1799
|
+
* // 'author.companyId' | 'author.<other ref fields>'
|
|
1800
|
+
* type Paths = NestedRefPath<{ author: typeof Users }>
|
|
1801
|
+
* ```
|
|
1802
|
+
*/
|
|
1803
|
+
type NestedRefPath<TPopMap extends Record<string, AnyCollection>> = {
|
|
1804
|
+
[Path in keyof TPopMap & string]: `${Path}.${RefFields<TPopMap[Path]>}`;
|
|
1805
|
+
}[keyof TPopMap & string];
|
|
1806
|
+
/**
|
|
1807
|
+
* Runtime data for a single populate step.
|
|
1808
|
+
*
|
|
1809
|
+
* Built by each `.populate()` call and consumed by `executePopulate()`.
|
|
1810
|
+
*/
|
|
1811
|
+
type PopulateStep = {
|
|
1812
|
+
/** The original field path from the root collection. */
|
|
1813
|
+
readonly originalPath: string;
|
|
1814
|
+
/** The leaf field name in the target shape. */
|
|
1815
|
+
readonly leafField: string;
|
|
1816
|
+
/** The output field name (renamed or original). */
|
|
1817
|
+
readonly as: string;
|
|
1818
|
+
/** Parent path in the output document for nesting, or undefined for top-level. */
|
|
1819
|
+
readonly parentOutputPath: string | undefined;
|
|
1820
|
+
/** The target collection definition resolved from RefMarker metadata. */
|
|
1821
|
+
readonly targetCollection: AnyCollection;
|
|
1822
|
+
/** Whether the field is an array ref. */
|
|
1823
|
+
readonly isArray: boolean;
|
|
1824
|
+
/** MongoDB projection spec to pass to the `$in` query. Omit to fetch the full document. */
|
|
1825
|
+
readonly projection?: Record<string, 0 | 1 | boolean>;
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* Extract the parent segment from a dot-separated path.
|
|
1830
|
+
*
|
|
1831
|
+
* @example
|
|
1832
|
+
* ```ts
|
|
1833
|
+
* type P = ExtractParent<'author.companyId'> // 'author'
|
|
1834
|
+
* ```
|
|
1835
|
+
*/
|
|
1836
|
+
type ExtractParent$1<T extends string> = T extends `${infer P}.${string}` ? P : never;
|
|
1837
|
+
/**
|
|
1838
|
+
* Extract the leaf segment from a dot-separated path.
|
|
1839
|
+
*
|
|
1840
|
+
* @example
|
|
1841
|
+
* ```ts
|
|
1842
|
+
* type L = ExtractLeaf<'author.companyId'> // 'companyId'
|
|
1843
|
+
* ```
|
|
1844
|
+
*/
|
|
1845
|
+
type ExtractLeaf$1<T extends string> = T extends `${string}.${infer L}` ? L : never;
|
|
1846
|
+
/**
|
|
1847
|
+
* Cursor with chained populate steps, wrapping a {@link TypedFindCursor}.
|
|
1848
|
+
*
|
|
1849
|
+
* Created by calling `.populate()` on a `TypedFindCursor`. Exposes only
|
|
1850
|
+
* `.populate()` (for additional fields) and terminal methods (`toArray`,
|
|
1851
|
+
* async iteration). Cursor modifiers (`sort`, `skip`, `limit`) are not
|
|
1852
|
+
* available -- they must be called before `.populate()`.
|
|
1853
|
+
*
|
|
1854
|
+
* @typeParam TDef - The root collection definition type.
|
|
1855
|
+
* @typeParam TOutput - The current output document type after populate transforms.
|
|
1856
|
+
* @typeParam TPopMap - Map of populated alias names to their collection definitions,
|
|
1857
|
+
* used to resolve nested populate paths.
|
|
1858
|
+
*
|
|
1859
|
+
* @example
|
|
1860
|
+
* ```ts
|
|
1861
|
+
* const posts = await db.use(Posts)
|
|
1862
|
+
* .find({ published: true })
|
|
1863
|
+
* .sort({ createdAt: -1 })
|
|
1864
|
+
* .limit(10)
|
|
1865
|
+
* .populate('authorId', 'author')
|
|
1866
|
+
* .populate('categoryIds')
|
|
1867
|
+
* .toArray()
|
|
1868
|
+
* ```
|
|
1869
|
+
*/
|
|
1870
|
+
declare class PopulateCursor<TDef extends AnyCollection, TOutput, TPopMap extends Record<string, AnyCollection> = Record<string, never>> {
|
|
1871
|
+
private readonly cursor;
|
|
1872
|
+
private readonly definition;
|
|
1873
|
+
private readonly steps;
|
|
1874
|
+
private readonly nativeCollection;
|
|
1875
|
+
/** @internal */
|
|
1876
|
+
constructor(cursor: TypedFindCursor<TDef>, definition: TDef, steps: readonly PopulateStep[], nativeCollection: Collection<InferDocument<TDef>>);
|
|
1877
|
+
/**
|
|
1878
|
+
* Populate a top-level ref field, keeping the original field name.
|
|
1879
|
+
*
|
|
1880
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
1881
|
+
* @returns A new cursor with the field type replaced by the referenced document.
|
|
1882
|
+
*
|
|
1883
|
+
* @example
|
|
1884
|
+
* ```ts
|
|
1885
|
+
* const posts = await db.use(Posts)
|
|
1886
|
+
* .find({})
|
|
1887
|
+
* .populate('authorId')
|
|
1888
|
+
* .toArray()
|
|
1889
|
+
* // posts[0].authorId is now a User document instead of ObjectId
|
|
1890
|
+
* ```
|
|
1891
|
+
*/
|
|
1892
|
+
populate<K extends RefFields<TDef>>(field: K): PopulateCursor<TDef, ApplyPopulate<TOutput, TDef, K>, TPopMap & {
|
|
1893
|
+
[P in K]: ExtractRefCollection<TDef, K>;
|
|
1894
|
+
}>;
|
|
1895
|
+
/**
|
|
1896
|
+
* Populate a top-level ref field with a projection, keeping the original field name.
|
|
1897
|
+
*
|
|
1898
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
1899
|
+
* @param configure - Callback receiving a builder; call `.project()` to set the projection.
|
|
1900
|
+
* @returns A new cursor with the field type narrowed to only the projected fields.
|
|
1901
|
+
*
|
|
1902
|
+
* @example
|
|
1903
|
+
* ```ts
|
|
1904
|
+
* const posts = await db.use(Posts)
|
|
1905
|
+
* .find({})
|
|
1906
|
+
* .populate('authorId', (b) => b.project({ name: 1, email: 1 }))
|
|
1907
|
+
* .toArray()
|
|
1908
|
+
* // posts[0].authorId is { _id: ObjectId; name: string; email: string }
|
|
1909
|
+
* ```
|
|
1910
|
+
*/
|
|
1911
|
+
populate<K extends RefFields<TDef>, P>(field: K, configure: (b: PopulateRefBuilder<InferDocument<ExtractRefCollection<TDef, K>>>) => PopulateProjectionConfig<P>): PopulateCursor<TDef, ApplyPopulateProjected<TOutput, TDef, K, P>, TPopMap & {
|
|
1912
|
+
[F in K]: ExtractRefCollection<TDef, K>;
|
|
1913
|
+
}>;
|
|
1914
|
+
/**
|
|
1915
|
+
* Populate a top-level ref field and rename it in the output.
|
|
1916
|
+
*
|
|
1917
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
1918
|
+
* @param as - The new field name in the output document.
|
|
1919
|
+
* @returns A new cursor with the old field removed and the new field added.
|
|
1920
|
+
*
|
|
1921
|
+
* @example
|
|
1922
|
+
* ```ts
|
|
1923
|
+
* const posts = await db.use(Posts)
|
|
1924
|
+
* .find({})
|
|
1925
|
+
* .populate('authorId', 'author')
|
|
1926
|
+
* .toArray()
|
|
1927
|
+
* // posts[0].author is a User document; posts[0].authorId no longer exists
|
|
1928
|
+
* ```
|
|
1929
|
+
*/
|
|
1930
|
+
populate<K extends RefFields<TDef>, TAs extends string>(field: K, as: TAs): PopulateCursor<TDef, ApplyPopulate<TOutput, TDef, K, TAs>, TPopMap & {
|
|
1931
|
+
[P in TAs]: ExtractRefCollection<TDef, K>;
|
|
1932
|
+
}>;
|
|
1933
|
+
/**
|
|
1934
|
+
* Populate a nested ref field, keeping the leaf field name.
|
|
1935
|
+
*
|
|
1936
|
+
* The parent path must have been populated in a previous `.populate()` call.
|
|
1937
|
+
*
|
|
1938
|
+
* @param path - A dot-separated path like `'author.companyId'`.
|
|
1939
|
+
* @returns A new cursor with the nested field type replaced.
|
|
1940
|
+
*
|
|
1941
|
+
* @example
|
|
1942
|
+
* ```ts
|
|
1943
|
+
* const posts = await db.use(Posts)
|
|
1944
|
+
* .find({})
|
|
1945
|
+
* .populate('authorId', 'author')
|
|
1946
|
+
* .populate('author.companyId')
|
|
1947
|
+
* .toArray()
|
|
1948
|
+
* // posts[0].author.companyId is now a Company document
|
|
1949
|
+
* ```
|
|
1950
|
+
*/
|
|
1951
|
+
populate<TPath extends NestedRefPath<TPopMap>>(path: TPath): PopulateCursor<TDef, DeepPopulate<TOutput, `${ExtractParent$1<TPath>}.${ExtractLeaf$1<TPath>}`, ExtractLeaf$1<TPath>, ExtractLeaf$1<TPath>, PopulateField<TPopMap[ExtractParent$1<TPath>], ExtractLeaf$1<TPath> & RefFields<TPopMap[ExtractParent$1<TPath>]>>>, TPopMap & {
|
|
1952
|
+
[P in `${ExtractParent$1<TPath>}.${ExtractLeaf$1<TPath>}`]: ExtractRefCollection<TPopMap[ExtractParent$1<TPath>], ExtractLeaf$1<TPath> & RefFields<TPopMap[ExtractParent$1<TPath>]>>;
|
|
1953
|
+
}>;
|
|
1954
|
+
/**
|
|
1955
|
+
* Populate a nested ref field and rename the leaf in the output.
|
|
1956
|
+
*
|
|
1957
|
+
* The parent path must have been populated in a previous `.populate()` call.
|
|
1958
|
+
*
|
|
1959
|
+
* @param path - A dot-separated path like `'author.companyId'`.
|
|
1960
|
+
* @param as - The new name for the leaf field in the output.
|
|
1961
|
+
* @returns A new cursor with the nested field renamed and typed.
|
|
1962
|
+
*
|
|
1963
|
+
* @example
|
|
1964
|
+
* ```ts
|
|
1965
|
+
* const posts = await db.use(Posts)
|
|
1966
|
+
* .find({})
|
|
1967
|
+
* .populate('authorId', 'author')
|
|
1968
|
+
* .populate('author.companyId', 'company')
|
|
1969
|
+
* .toArray()
|
|
1970
|
+
* // posts[0].author.company is a Company document
|
|
1971
|
+
* ```
|
|
1972
|
+
*/
|
|
1973
|
+
populate<TPath extends NestedRefPath<TPopMap>, TAs extends string>(path: TPath, as: TAs): PopulateCursor<TDef, DeepPopulate<TOutput, `${ExtractParent$1<TPath>}.${TAs}`, ExtractLeaf$1<TPath>, TAs, PopulateField<TPopMap[ExtractParent$1<TPath>], ExtractLeaf$1<TPath> & RefFields<TPopMap[ExtractParent$1<TPath>]>>>, TPopMap & {
|
|
1974
|
+
[P in `${ExtractParent$1<TPath>}.${TAs}`]: ExtractRefCollection<TPopMap[ExtractParent$1<TPath>], ExtractLeaf$1<TPath> & RefFields<TPopMap[ExtractParent$1<TPath>]>>;
|
|
1975
|
+
}>;
|
|
1976
|
+
/**
|
|
1977
|
+
* Execute the query and return all matching documents as a populated array.
|
|
1978
|
+
*
|
|
1979
|
+
* Fetches all documents from the underlying cursor, then applies populate
|
|
1980
|
+
* steps in order using batch `$in` queries (no N+1 problem).
|
|
1981
|
+
*
|
|
1982
|
+
* @returns Array of populated documents.
|
|
1983
|
+
*
|
|
1984
|
+
* @example
|
|
1985
|
+
* ```ts
|
|
1986
|
+
* const posts = await db.use(Posts)
|
|
1987
|
+
* .find({})
|
|
1988
|
+
* .populate('authorId', 'author')
|
|
1989
|
+
* .toArray()
|
|
1990
|
+
* ```
|
|
1991
|
+
*/
|
|
1992
|
+
toArray(): Promise<TOutput[]>;
|
|
1993
|
+
/**
|
|
1994
|
+
* Async iterator for streaming populated documents.
|
|
1995
|
+
*
|
|
1996
|
+
* Fetches all documents first (populate requires the full batch for
|
|
1997
|
+
* efficient `$in` queries), then yields results one at a time.
|
|
1998
|
+
*
|
|
1999
|
+
* @yields Populated documents one at a time.
|
|
2000
|
+
*
|
|
2001
|
+
* @example
|
|
2002
|
+
* ```ts
|
|
2003
|
+
* for await (const post of db.use(Posts).find({}).populate('authorId', 'author')) {
|
|
2004
|
+
* console.log(post.author.name)
|
|
2005
|
+
* }
|
|
2006
|
+
* ```
|
|
2007
|
+
*/
|
|
2008
|
+
[Symbol.asyncIterator](): AsyncGenerator<TOutput>;
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Create a new {@link PopulateCursor} from a {@link TypedFindCursor}.
|
|
2012
|
+
*
|
|
2013
|
+
* This factory function exists to break the circular import between
|
|
2014
|
+
* `query/cursor.ts` and `populate/cursor.ts`. The `TypedFindCursor.populate()`
|
|
2015
|
+
* method uses a lazy `require()` to call this function at runtime.
|
|
2016
|
+
*
|
|
2017
|
+
* @param cursor - The typed find cursor to wrap.
|
|
2018
|
+
* @param definition - The collection definition.
|
|
2019
|
+
* @param steps - Initial populate steps (typically empty).
|
|
2020
|
+
* @returns A new PopulateCursor instance.
|
|
2021
|
+
*
|
|
2022
|
+
* @example
|
|
2023
|
+
* ```ts
|
|
2024
|
+
* const popCursor = createPopulateCursor(typedCursor, Posts, [])
|
|
2025
|
+
* ```
|
|
2026
|
+
*/
|
|
2027
|
+
declare function createPopulateCursor<TDef extends AnyCollection>(cursor: TypedFindCursor<TDef>, definition: TDef, steps: readonly PopulateStep[]): PopulateCursor<TDef, InferDocument<TDef>>;
|
|
2028
|
+
|
|
2029
|
+
/**
|
|
2030
|
+
* Type-safe sort specification for a document type.
|
|
2031
|
+
*
|
|
2032
|
+
* Constrains sort keys to top-level fields of `T` with direction `1` (ascending)
|
|
2033
|
+
* or `-1` (descending). Dot-path sorts deferred to v1.0.
|
|
2034
|
+
*
|
|
2035
|
+
* @example
|
|
2036
|
+
* ```ts
|
|
2037
|
+
* const sort: TypedSort<User> = { name: 1, createdAt: -1 }
|
|
2038
|
+
* ```
|
|
2039
|
+
*/
|
|
2040
|
+
type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
|
|
2041
|
+
/**
|
|
2042
|
+
* Type-safe cursor wrapping MongoDB's `FindCursor`.
|
|
2043
|
+
*
|
|
2044
|
+
* Provides chainable query modifiers (`sort`, `skip`, `limit`, `hint`) that return
|
|
2045
|
+
* `this` for fluent chaining, and terminal methods (`toArray`,
|
|
2046
|
+
* `[Symbol.asyncIterator]`) that validate each document against the
|
|
2047
|
+
* collection's Zod schema before returning.
|
|
2048
|
+
*
|
|
2049
|
+
* Created by {@link find} — do not construct directly.
|
|
2050
|
+
*
|
|
2051
|
+
* @typeParam TDef - The collection definition type, used to infer the document type.
|
|
2052
|
+
* @typeParam TIndexNames - Union of declared index names accepted by `.hint()`.
|
|
2053
|
+
* @typeParam TOutput - The output document type, narrowed by `.project()`.
|
|
2054
|
+
*
|
|
2055
|
+
* @example
|
|
2056
|
+
* ```ts
|
|
2057
|
+
* const docs = await find(users, { role: 'admin' })
|
|
2058
|
+
* .sort({ name: 1 })
|
|
2059
|
+
* .limit(10)
|
|
2060
|
+
* .toArray()
|
|
2061
|
+
* ```
|
|
2062
|
+
*/
|
|
2063
|
+
declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends string = string, TOutput = InferDocument<TDef>> {
|
|
2064
|
+
/** @internal */
|
|
2065
|
+
private cursor;
|
|
2066
|
+
/** @internal */
|
|
2067
|
+
readonly definition: TDef;
|
|
2068
|
+
/** @internal */
|
|
2069
|
+
private schema;
|
|
2070
|
+
/** @internal */
|
|
2071
|
+
private collectionName;
|
|
2072
|
+
/** @internal */
|
|
2073
|
+
private mode;
|
|
2074
|
+
/** @internal */
|
|
2075
|
+
readonly nativeCollection: Collection<InferDocument<TDef>>;
|
|
2076
|
+
/** @internal */
|
|
2077
|
+
private readonly filter;
|
|
2078
|
+
/** @internal */
|
|
2079
|
+
private readonly session;
|
|
2080
|
+
/** @internal */
|
|
2081
|
+
private sortSpec;
|
|
2082
|
+
/** @internal */
|
|
2083
|
+
private projectedSchema;
|
|
2084
|
+
/** @internal */
|
|
2085
|
+
constructor(cursor: FindCursor<InferDocument<TDef>>, definition: TDef, mode: ValidationMode | false, nativeCollection: Collection<InferDocument<TDef>>, filter: any, session?: ClientSession);
|
|
2086
|
+
/**
|
|
2087
|
+
* Set the sort order for the query.
|
|
2088
|
+
*
|
|
2089
|
+
* Only top-level document fields are accepted as sort keys.
|
|
2090
|
+
* Values must be `1` (ascending) or `-1` (descending).
|
|
2091
|
+
*
|
|
2092
|
+
* @param spec - Sort specification mapping field names to sort direction.
|
|
2093
|
+
* @returns `this` for chaining.
|
|
2094
|
+
*
|
|
2095
|
+
* @example
|
|
2096
|
+
* ```ts
|
|
2097
|
+
* find(users, {}).sort({ name: 1, age: -1 }).toArray()
|
|
2098
|
+
* ```
|
|
2099
|
+
*/
|
|
2100
|
+
sort(spec: TypedSort<InferDocument<TDef>>): this;
|
|
2101
|
+
/**
|
|
1308
2102
|
* Skip the first `n` documents in the result set.
|
|
1309
2103
|
*
|
|
1310
2104
|
* @param n - Number of documents to skip.
|
|
@@ -1348,6 +2142,100 @@ declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends st
|
|
|
1348
2142
|
* ```
|
|
1349
2143
|
*/
|
|
1350
2144
|
hint(indexName: TIndexNames): this;
|
|
2145
|
+
/**
|
|
2146
|
+
* Apply a projection to narrow the returned fields.
|
|
2147
|
+
*
|
|
2148
|
+
* Inclusion projections (`{ name: 1 }`) return only the specified fields
|
|
2149
|
+
* plus `_id` (unless `_id: 0`). Exclusion projections (`{ email: 0 }`)
|
|
2150
|
+
* return all fields except those excluded.
|
|
2151
|
+
*
|
|
2152
|
+
* The cursor's output type is narrowed at compile time. A derived Zod
|
|
2153
|
+
* schema is built for runtime validation of the projected fields.
|
|
2154
|
+
*
|
|
2155
|
+
* Projects from the original document type, not from a previous projection.
|
|
2156
|
+
* Calling `.project()` twice overrides the previous projection.
|
|
2157
|
+
*
|
|
2158
|
+
* @param spec - Type-safe projection document.
|
|
2159
|
+
* @returns A new cursor with the narrowed output type.
|
|
2160
|
+
*
|
|
2161
|
+
* @example
|
|
2162
|
+
* ```ts
|
|
2163
|
+
* const names = await find(users, {})
|
|
2164
|
+
* .project({ name: 1 })
|
|
2165
|
+
* .sort({ name: 1 })
|
|
2166
|
+
* .toArray()
|
|
2167
|
+
* // names[0].name ✓
|
|
2168
|
+
* // names[0].email TS error
|
|
2169
|
+
* ```
|
|
2170
|
+
*/
|
|
2171
|
+
project<P extends TypedProjection<InferDocument<TDef>>>(spec: P): TypedFindCursor<TDef, TIndexNames, Prettify<ProjectionResult<InferDocument<TDef>, P>>>;
|
|
2172
|
+
/**
|
|
2173
|
+
* Populate a top-level ref field, keeping the original field name.
|
|
2174
|
+
*
|
|
2175
|
+
* Transitions to a {@link PopulateCursor} that only exposes `.populate()`
|
|
2176
|
+
* and terminal methods. Cursor modifiers (`sort`, `skip`, `limit`) must
|
|
2177
|
+
* be called before `.populate()`.
|
|
2178
|
+
*
|
|
2179
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
2180
|
+
* @returns A populate cursor with the field type replaced by the referenced document.
|
|
2181
|
+
*
|
|
2182
|
+
* @example
|
|
2183
|
+
* ```ts
|
|
2184
|
+
* const posts = await db.use(Posts)
|
|
2185
|
+
* .find({})
|
|
2186
|
+
* .populate('authorId')
|
|
2187
|
+
* .toArray()
|
|
2188
|
+
* ```
|
|
2189
|
+
*/
|
|
2190
|
+
populate<K extends RefFields<TDef>>(field: K): PopulateCursor<TDef, ApplyPopulate<TOutput, TDef, K>, {
|
|
2191
|
+
[P in K]: ExtractRefCollection<TDef, K>;
|
|
2192
|
+
}>;
|
|
2193
|
+
/**
|
|
2194
|
+
* Populate a top-level ref field with a projection, keeping the original field name.
|
|
2195
|
+
*
|
|
2196
|
+
* Transitions to a {@link PopulateCursor}. Cursor modifiers (`sort`, `skip`, `limit`)
|
|
2197
|
+
* must be called before `.populate()`.
|
|
2198
|
+
*
|
|
2199
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
2200
|
+
* @param configure - Callback receiving a builder; call `.project()` to set the projection.
|
|
2201
|
+
* @returns A populate cursor with the field type narrowed to only the projected fields.
|
|
2202
|
+
*
|
|
2203
|
+
* @example
|
|
2204
|
+
* ```ts
|
|
2205
|
+
* const posts = await db.use(Posts)
|
|
2206
|
+
* .find({})
|
|
2207
|
+
* .populate('authorId', (b) => b.project({ name: 1, email: 1 }))
|
|
2208
|
+
* .toArray()
|
|
2209
|
+
* // posts[0].authorId is { _id: ObjectId; name: string; email: string }
|
|
2210
|
+
* ```
|
|
2211
|
+
*/
|
|
2212
|
+
populate<K extends RefFields<TDef>, P>(field: K, configure: (b: PopulateRefBuilder<InferDocument<ExtractRefCollection<TDef, K>>>) => PopulateProjectionConfig<P>): PopulateCursor<TDef, ApplyPopulateProjected<TOutput, TDef, K, P>, {
|
|
2213
|
+
[F in K]: ExtractRefCollection<TDef, K>;
|
|
2214
|
+
}>;
|
|
2215
|
+
/**
|
|
2216
|
+
* Populate a top-level ref field and rename it in the output.
|
|
2217
|
+
*
|
|
2218
|
+
* Transitions to a {@link PopulateCursor} that only exposes `.populate()`
|
|
2219
|
+
* and terminal methods. Cursor modifiers (`sort`, `skip`, `limit`) must
|
|
2220
|
+
* be called before `.populate()`.
|
|
2221
|
+
*
|
|
2222
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
2223
|
+
* @param as - The new field name in the output document.
|
|
2224
|
+
* @returns A populate cursor with the old field removed and the new field added.
|
|
2225
|
+
*
|
|
2226
|
+
* @example
|
|
2227
|
+
* ```ts
|
|
2228
|
+
* const posts = await db.use(Posts)
|
|
2229
|
+
* .find({})
|
|
2230
|
+
* .sort({ createdAt: -1 })
|
|
2231
|
+
* .limit(10)
|
|
2232
|
+
* .populate('authorId', 'author')
|
|
2233
|
+
* .toArray()
|
|
2234
|
+
* ```
|
|
2235
|
+
*/
|
|
2236
|
+
populate<K extends RefFields<TDef>, TAs extends string>(field: K, as: TAs): PopulateCursor<TDef, ApplyPopulate<TOutput, TDef, K, TAs>, {
|
|
2237
|
+
[P in TAs]: ExtractRefCollection<TDef, K>;
|
|
2238
|
+
}>;
|
|
1351
2239
|
/**
|
|
1352
2240
|
* Execute the query with offset-based pagination, returning a page of documents
|
|
1353
2241
|
* with total count and navigation metadata.
|
|
@@ -1367,7 +2255,7 @@ declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends st
|
|
|
1367
2255
|
* console.log(page.total, page.totalPages, page.hasNext)
|
|
1368
2256
|
* ```
|
|
1369
2257
|
*/
|
|
1370
|
-
paginate(opts: OffsetPaginateOptions): Promise<OffsetPage<
|
|
2258
|
+
paginate(opts: OffsetPaginateOptions): Promise<OffsetPage<TOutput>>;
|
|
1371
2259
|
/**
|
|
1372
2260
|
* Execute the query with cursor-based pagination, returning a page of documents
|
|
1373
2261
|
* with opaque cursors for forward/backward navigation.
|
|
@@ -1388,7 +2276,7 @@ declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends st
|
|
|
1388
2276
|
* .paginate({ cursor: first.endCursor, limit: 10 })
|
|
1389
2277
|
* ```
|
|
1390
2278
|
*/
|
|
1391
|
-
paginate(opts: CursorPaginateOptions): Promise<CursorPage<
|
|
2279
|
+
paginate(opts: CursorPaginateOptions): Promise<CursorPage<TOutput>>;
|
|
1392
2280
|
/** @internal Offset pagination implementation. */
|
|
1393
2281
|
private offsetPaginate;
|
|
1394
2282
|
/** @internal Cursor pagination implementation. */
|
|
@@ -1407,7 +2295,7 @@ declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends st
|
|
|
1407
2295
|
* const admins = await find(users, { role: 'admin' }).toArray()
|
|
1408
2296
|
* ```
|
|
1409
2297
|
*/
|
|
1410
|
-
toArray(): Promise<
|
|
2298
|
+
toArray(): Promise<TOutput[]>;
|
|
1411
2299
|
/**
|
|
1412
2300
|
* Async iterator for streaming documents one at a time.
|
|
1413
2301
|
*
|
|
@@ -1424,17 +2312,33 @@ declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends st
|
|
|
1424
2312
|
* }
|
|
1425
2313
|
* ```
|
|
1426
2314
|
*/
|
|
1427
|
-
[Symbol.asyncIterator](): AsyncGenerator<
|
|
2315
|
+
[Symbol.asyncIterator](): AsyncGenerator<TOutput>;
|
|
1428
2316
|
/** @internal Validate a single raw document against the schema. */
|
|
1429
2317
|
private validateDoc;
|
|
1430
2318
|
}
|
|
1431
2319
|
|
|
1432
2320
|
/**
|
|
1433
|
-
* Options for {@link findOne} and {@link findOneOrThrow}.
|
|
2321
|
+
* Options for {@link findOne} and {@link findOneOrThrow} without projection.
|
|
1434
2322
|
*/
|
|
1435
2323
|
type FindOneOptions = {
|
|
1436
|
-
/**
|
|
1437
|
-
|
|
2324
|
+
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
2325
|
+
validate?: ValidationMode | false;
|
|
2326
|
+
};
|
|
2327
|
+
/**
|
|
2328
|
+
* Options for projected {@link findOne} and {@link findOneOrThrow} queries.
|
|
2329
|
+
*
|
|
2330
|
+
* @typeParam T - The document type.
|
|
2331
|
+
* @typeParam P - The projection document type.
|
|
2332
|
+
*
|
|
2333
|
+
* @example
|
|
2334
|
+
* ```ts
|
|
2335
|
+
* const user = await findOne(users, { name: 'Ada' }, { project: { name: 1 } })
|
|
2336
|
+
* // ^? { _id: ObjectId; name: string } | null
|
|
2337
|
+
* ```
|
|
2338
|
+
*/
|
|
2339
|
+
type FindOneProjectionOptions<T, P extends TypedProjection<T>> = {
|
|
2340
|
+
/** Type-safe MongoDB projection — include (`1 | true`) or exclude (`0 | false`) fields. */
|
|
2341
|
+
project: P;
|
|
1438
2342
|
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
1439
2343
|
validate?: ValidationMode | false;
|
|
1440
2344
|
};
|
|
@@ -1447,7 +2351,7 @@ type FindOneOptions = {
|
|
|
1447
2351
|
*
|
|
1448
2352
|
* @param handle - The collection handle to query.
|
|
1449
2353
|
* @param filter - Type-safe filter to match documents.
|
|
1450
|
-
* @param options - Optional
|
|
2354
|
+
* @param options - Optional validation overrides.
|
|
1451
2355
|
* @returns The matched document, or `null` if no document matches.
|
|
1452
2356
|
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1453
2357
|
*
|
|
@@ -1458,6 +2362,26 @@ type FindOneOptions = {
|
|
|
1458
2362
|
* ```
|
|
1459
2363
|
*/
|
|
1460
2364
|
declare function findOne<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef> | null>;
|
|
2365
|
+
/**
|
|
2366
|
+
* Find a single document matching the filter with a type-safe projection.
|
|
2367
|
+
*
|
|
2368
|
+
* Returns only the fields specified by the projection. The return type is
|
|
2369
|
+
* narrowed to reflect which fields are included or excluded.
|
|
2370
|
+
*
|
|
2371
|
+
* @param handle - The collection handle to query.
|
|
2372
|
+
* @param filter - Type-safe filter to match documents.
|
|
2373
|
+
* @param options - Projection and optional validation overrides.
|
|
2374
|
+
* @returns The projected document, or `null` if no document matches.
|
|
2375
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
2376
|
+
*
|
|
2377
|
+
* @example
|
|
2378
|
+
* ```ts
|
|
2379
|
+
* const user = await findOne(users, { name: 'Ada' }, { project: { name: 1 } })
|
|
2380
|
+
* if (user) console.log(user.name) // typed as string
|
|
2381
|
+
* // user.role would be a type error — not in the projection
|
|
2382
|
+
* ```
|
|
2383
|
+
*/
|
|
2384
|
+
declare function findOne<TDef extends AnyCollection, P extends TypedProjection<InferDocument<TDef>>>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options: FindOneProjectionOptions<InferDocument<TDef>, P>): Promise<Prettify<ProjectionResult<InferDocument<TDef>, P>> | null>;
|
|
1461
2385
|
/**
|
|
1462
2386
|
* Find a single document matching the filter, or throw if none exists.
|
|
1463
2387
|
*
|
|
@@ -1466,7 +2390,7 @@ declare function findOne<TDef extends AnyCollection>(handle: CollectionHandle<TD
|
|
|
1466
2390
|
*
|
|
1467
2391
|
* @param handle - The collection handle to query.
|
|
1468
2392
|
* @param filter - Type-safe filter to match documents.
|
|
1469
|
-
* @param options - Optional
|
|
2393
|
+
* @param options - Optional validation overrides.
|
|
1470
2394
|
* @returns The matched document (never null).
|
|
1471
2395
|
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
1472
2396
|
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
@@ -1478,6 +2402,28 @@ declare function findOne<TDef extends AnyCollection>(handle: CollectionHandle<TD
|
|
|
1478
2402
|
* ```
|
|
1479
2403
|
*/
|
|
1480
2404
|
declare function findOneOrThrow<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
|
|
2405
|
+
/**
|
|
2406
|
+
* Find a single document matching the filter with a type-safe projection, or throw if none exists.
|
|
2407
|
+
*
|
|
2408
|
+
* Returns only the fields specified by the projection. The return type is
|
|
2409
|
+
* narrowed to reflect which fields are included or excluded. Throws
|
|
2410
|
+
* {@link ZodmonNotFoundError} instead of returning `null`.
|
|
2411
|
+
*
|
|
2412
|
+
* @param handle - The collection handle to query.
|
|
2413
|
+
* @param filter - Type-safe filter to match documents.
|
|
2414
|
+
* @param options - Projection and optional validation overrides.
|
|
2415
|
+
* @returns The projected document (never null).
|
|
2416
|
+
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
2417
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
2418
|
+
*
|
|
2419
|
+
* @example
|
|
2420
|
+
* ```ts
|
|
2421
|
+
* const user = await findOneOrThrow(users, { name: 'Ada' }, { project: { name: 1 } })
|
|
2422
|
+
* console.log(user.name) // typed as string
|
|
2423
|
+
* // user.role would be a type error — not in the projection
|
|
2424
|
+
* ```
|
|
2425
|
+
*/
|
|
2426
|
+
declare function findOneOrThrow<TDef extends AnyCollection, P extends TypedProjection<InferDocument<TDef>>>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options: FindOneProjectionOptions<InferDocument<TDef>, P>): Promise<Prettify<ProjectionResult<InferDocument<TDef>, P>>>;
|
|
1481
2427
|
/**
|
|
1482
2428
|
* Options for {@link find}.
|
|
1483
2429
|
*/
|
|
@@ -1485,6 +2431,25 @@ type FindOptions = {
|
|
|
1485
2431
|
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
1486
2432
|
validate?: ValidationMode | false;
|
|
1487
2433
|
};
|
|
2434
|
+
/**
|
|
2435
|
+
* Options for projected {@link find} queries.
|
|
2436
|
+
*
|
|
2437
|
+
* @typeParam T - The document type.
|
|
2438
|
+
* @typeParam P - The projection document type.
|
|
2439
|
+
*
|
|
2440
|
+
* @example
|
|
2441
|
+
* ```ts
|
|
2442
|
+
* const admins = await find(users, { role: 'admin' }, { project: { name: 1 } })
|
|
2443
|
+
* .toArray()
|
|
2444
|
+
* // ^? Array<{ _id: ObjectId; name: string }>
|
|
2445
|
+
* ```
|
|
2446
|
+
*/
|
|
2447
|
+
type FindProjectionOptions<T, P extends TypedProjection<T>> = {
|
|
2448
|
+
/** Type-safe MongoDB projection — include (`1 | true`) or exclude (`0 | false`) fields. */
|
|
2449
|
+
project: P;
|
|
2450
|
+
/** Override the collection-level validation mode, or `false` to skip validation entirely. */
|
|
2451
|
+
validate?: ValidationMode | false;
|
|
2452
|
+
};
|
|
1488
2453
|
/**
|
|
1489
2454
|
* Find all documents matching the filter, returning a chainable typed cursor.
|
|
1490
2455
|
*
|
|
@@ -1516,17 +2481,29 @@ type FindOptions = {
|
|
|
1516
2481
|
* ```
|
|
1517
2482
|
*/
|
|
1518
2483
|
declare function find<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef, IndexNames<TDef>>;
|
|
1519
|
-
|
|
1520
2484
|
/**
|
|
1521
|
-
*
|
|
2485
|
+
* Find all documents matching the filter with a type-safe projection, returning a chainable typed cursor.
|
|
2486
|
+
*
|
|
2487
|
+
* The cursor is lazy — no query is executed until a terminal method
|
|
2488
|
+
* (`toArray`, `for await`) is called. The return type is narrowed to
|
|
2489
|
+
* reflect which fields are included or excluded by the projection.
|
|
2490
|
+
*
|
|
2491
|
+
* @param handle - The collection handle to query.
|
|
2492
|
+
* @param filter - Type-safe filter to match documents.
|
|
2493
|
+
* @param options - Projection and optional validation overrides.
|
|
2494
|
+
* @returns A typed cursor whose output type matches the projection.
|
|
1522
2495
|
*
|
|
1523
2496
|
* @example
|
|
1524
2497
|
* ```ts
|
|
1525
|
-
*
|
|
1526
|
-
*
|
|
2498
|
+
* const names = await find(users, { role: 'admin' }, { project: { name: 1 } })
|
|
2499
|
+
* .sort({ name: 1 })
|
|
2500
|
+
* .toArray()
|
|
2501
|
+
* // names[0].name — string
|
|
2502
|
+
* // names[0].role — type error, not in projection
|
|
1527
2503
|
* ```
|
|
1528
2504
|
*/
|
|
1529
|
-
|
|
2505
|
+
declare function find<TDef extends AnyCollection, P extends TypedProjection<InferDocument<TDef>>>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options: FindProjectionOptions<InferDocument<TDef>, P>): TypedFindCursor<TDef, IndexNames<TDef>, Prettify<ProjectionResult<InferDocument<TDef>, P>>>;
|
|
2506
|
+
|
|
1530
2507
|
/**
|
|
1531
2508
|
* Fields valid for `$set`, `$setOnInsert`, `$min`, and `$max` operators.
|
|
1532
2509
|
*
|
|
@@ -1541,7 +2518,7 @@ type ArrayElement<T> = T extends ReadonlyArray<infer E> ? E : never;
|
|
|
1541
2518
|
type SetFields<T> = {
|
|
1542
2519
|
[K in keyof T]?: T[K];
|
|
1543
2520
|
} & {
|
|
1544
|
-
[P in
|
|
2521
|
+
[P in DotSubPaths<T>]?: DotPathValue<T, P>;
|
|
1545
2522
|
};
|
|
1546
2523
|
/**
|
|
1547
2524
|
* Fields valid for the `$inc` operator — only number-typed fields.
|
|
@@ -1556,7 +2533,7 @@ type SetFields<T> = {
|
|
|
1556
2533
|
type IncFields<T> = {
|
|
1557
2534
|
[K in keyof T as NonNullable<T[K]> extends number ? K : never]?: number;
|
|
1558
2535
|
} & {
|
|
1559
|
-
[P in
|
|
2536
|
+
[P in DotSubPaths<T> as DotPathValue<T, P> extends number ? P : never]?: number;
|
|
1560
2537
|
};
|
|
1561
2538
|
/**
|
|
1562
2539
|
* Modifiers for the `$push` operator.
|
|
@@ -1847,59 +2824,310 @@ type SyncIndexesOptions = {
|
|
|
1847
2824
|
* options). When `false` (the default), orphaned and stale indexes are
|
|
1848
2825
|
* reported but left untouched.
|
|
1849
2826
|
*/
|
|
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
|
-
};
|
|
2827
|
+
dropOrphaned?: boolean;
|
|
2828
|
+
};
|
|
2829
|
+
/**
|
|
2830
|
+
* Describes an index whose key matches a desired index but whose options differ.
|
|
2831
|
+
*
|
|
2832
|
+
* Returned in {@link SyncIndexesResult.stale} so the caller can decide whether
|
|
2833
|
+
* to manually reconcile or re-run with `dropOrphaned: true`.
|
|
2834
|
+
*
|
|
2835
|
+
* @example
|
|
2836
|
+
* ```ts
|
|
2837
|
+
* const result = await users.syncIndexes()
|
|
2838
|
+
* for (const s of result.stale) {
|
|
2839
|
+
* console.log(`${s.name}: key=${JSON.stringify(s.key)}`)
|
|
2840
|
+
* console.log(` existing=${JSON.stringify(s.existing)}`)
|
|
2841
|
+
* console.log(` desired=${JSON.stringify(s.desired)}`)
|
|
2842
|
+
* }
|
|
2843
|
+
* ```
|
|
2844
|
+
*/
|
|
2845
|
+
type StaleIndex = {
|
|
2846
|
+
/** The MongoDB index name (e.g. `'email_1'`). */
|
|
2847
|
+
name: string;
|
|
2848
|
+
/** The index key spec (e.g. `{ email: 1 }`). */
|
|
2849
|
+
key: Record<string, 1 | -1 | 'text'>;
|
|
2850
|
+
/** The relevant options currently set on the existing index. */
|
|
2851
|
+
existing: Record<string, unknown>;
|
|
2852
|
+
/** The options the schema declares for this index. */
|
|
2853
|
+
desired: Record<string, unknown>;
|
|
2854
|
+
};
|
|
2855
|
+
/**
|
|
2856
|
+
* The result of a {@link syncIndexes} call.
|
|
2857
|
+
*
|
|
2858
|
+
* Every array contains index names. `stale` contains full details so the
|
|
2859
|
+
* caller can inspect the mismatch.
|
|
2860
|
+
*
|
|
2861
|
+
* @example
|
|
2862
|
+
* ```ts
|
|
2863
|
+
* const result = await users.syncIndexes()
|
|
2864
|
+
* console.log('created:', result.created)
|
|
2865
|
+
* console.log('dropped:', result.dropped)
|
|
2866
|
+
* console.log('skipped:', result.skipped)
|
|
2867
|
+
* console.log('stale:', result.stale.map(s => s.name))
|
|
2868
|
+
* ```
|
|
2869
|
+
*/
|
|
2870
|
+
type SyncIndexesResult = {
|
|
2871
|
+
/** Names of indexes that were created (or would be created in dryRun mode). */
|
|
2872
|
+
created: string[];
|
|
2873
|
+
/** Names of indexes that were dropped (or would be dropped in dryRun mode). */
|
|
2874
|
+
dropped: string[];
|
|
2875
|
+
/** Names of indexes that already existed with matching options — no action taken. */
|
|
2876
|
+
skipped: string[];
|
|
2877
|
+
/** Indexes whose key matches a desired spec but whose options differ. */
|
|
2878
|
+
stale: StaleIndex[];
|
|
2879
|
+
};
|
|
2880
|
+
|
|
2881
|
+
/**
|
|
2882
|
+
* Extract the parent segment from a dot-separated path.
|
|
2883
|
+
*
|
|
2884
|
+
* @example
|
|
2885
|
+
* ```ts
|
|
2886
|
+
* type P = ExtractParent<'author.companyId'> // 'author'
|
|
2887
|
+
* ```
|
|
2888
|
+
*/
|
|
2889
|
+
type ExtractParent<T extends string> = T extends `${infer P}.${string}` ? P : never;
|
|
2890
|
+
/**
|
|
2891
|
+
* Extract the leaf segment from a dot-separated path.
|
|
2892
|
+
*
|
|
2893
|
+
* @example
|
|
2894
|
+
* ```ts
|
|
2895
|
+
* type L = ExtractLeaf<'author.companyId'> // 'companyId'
|
|
2896
|
+
* ```
|
|
2897
|
+
*/
|
|
2898
|
+
type ExtractLeaf<T extends string> = T extends `${string}.${infer L}` ? L : never;
|
|
2899
|
+
/**
|
|
2900
|
+
* Fluent populate builder for findOne queries.
|
|
2901
|
+
*
|
|
2902
|
+
* Implements `PromiseLike` so it can be awaited directly without `.populate()`.
|
|
2903
|
+
* Each `.populate()` call returns a new builder with updated generics.
|
|
2904
|
+
*
|
|
2905
|
+
* @example
|
|
2906
|
+
* ```ts
|
|
2907
|
+
* // Without populate (backward compatible)
|
|
2908
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
2909
|
+
*
|
|
2910
|
+
* // With populate
|
|
2911
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
2912
|
+
* .populate('authorId', 'author')
|
|
2913
|
+
* .populate('author.companyId', 'company')
|
|
2914
|
+
* ```
|
|
2915
|
+
*/
|
|
2916
|
+
declare class PopulateOneQuery<TDef extends AnyCollection, TOutput, TPopMap extends Record<string, AnyCollection> = Record<string, never>> implements PromiseLike<TOutput | null> {
|
|
2917
|
+
private readonly handle;
|
|
2918
|
+
private readonly filter;
|
|
2919
|
+
private readonly options;
|
|
2920
|
+
private readonly steps;
|
|
2921
|
+
constructor(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options: FindOneOptions | undefined, steps?: readonly PopulateStep[]);
|
|
2922
|
+
/**
|
|
2923
|
+
* Populate a top-level ref field, keeping the original field name.
|
|
2924
|
+
*
|
|
2925
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
2926
|
+
* @returns A new builder with the field type replaced by the referenced document.
|
|
2927
|
+
*
|
|
2928
|
+
* @example
|
|
2929
|
+
* ```ts
|
|
2930
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
2931
|
+
* .populate('authorId')
|
|
2932
|
+
* // post.authorId is now a User document instead of ObjectId
|
|
2933
|
+
* ```
|
|
2934
|
+
*/
|
|
2935
|
+
populate<K extends RefFields<TDef>>(field: K): PopulateOneQuery<TDef, ApplyPopulate<TOutput, TDef, K>, TPopMap & {
|
|
2936
|
+
[P in K]: ExtractRefCollection<TDef, K>;
|
|
2937
|
+
}>;
|
|
2938
|
+
/**
|
|
2939
|
+
* Populate a top-level ref field with a projection, keeping the original field name.
|
|
2940
|
+
*
|
|
2941
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
2942
|
+
* @param configure - Callback receiving a builder; call `.project()` to set the projection.
|
|
2943
|
+
* @returns A new builder with the field type narrowed to only the projected fields.
|
|
2944
|
+
*
|
|
2945
|
+
* @example
|
|
2946
|
+
* ```ts
|
|
2947
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
2948
|
+
* .populate('authorId', (b) => b.project({ name: 1, email: 1 }))
|
|
2949
|
+
* // post.authorId is { _id: ObjectId; name: string; email: string }
|
|
2950
|
+
* // post.authorId.age → TS error
|
|
2951
|
+
* ```
|
|
2952
|
+
*/
|
|
2953
|
+
populate<K extends RefFields<TDef>, P>(field: K, configure: (b: PopulateRefBuilder<InferDocument<ExtractRefCollection<TDef, K>>>) => PopulateProjectionConfig<P>): PopulateOneQuery<TDef, ApplyPopulateProjected<TOutput, TDef, K, P>, TPopMap & {
|
|
2954
|
+
[F in K]: ExtractRefCollection<TDef, K>;
|
|
2955
|
+
}>;
|
|
2956
|
+
/**
|
|
2957
|
+
* Populate a top-level ref field and rename it in the output.
|
|
2958
|
+
*
|
|
2959
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
2960
|
+
* @param as - The new field name in the output document.
|
|
2961
|
+
* @returns A new builder with the old field removed and the new field added.
|
|
2962
|
+
*
|
|
2963
|
+
* @example
|
|
2964
|
+
* ```ts
|
|
2965
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
2966
|
+
* .populate('authorId', 'author')
|
|
2967
|
+
* // post.author is a User document; post.authorId no longer exists
|
|
2968
|
+
* ```
|
|
2969
|
+
*/
|
|
2970
|
+
populate<K extends RefFields<TDef>, TAs extends string>(field: K, as: TAs): PopulateOneQuery<TDef, ApplyPopulate<TOutput, TDef, K, TAs>, TPopMap & {
|
|
2971
|
+
[P in TAs]: ExtractRefCollection<TDef, K>;
|
|
2972
|
+
}>;
|
|
2973
|
+
/**
|
|
2974
|
+
* Populate a nested ref field, keeping the leaf field name.
|
|
2975
|
+
*
|
|
2976
|
+
* The parent path must have been populated in a previous `.populate()` call.
|
|
2977
|
+
*
|
|
2978
|
+
* @param path - A dot-separated path like `'author.companyId'`.
|
|
2979
|
+
* @returns A new builder with the nested field type replaced.
|
|
2980
|
+
*
|
|
2981
|
+
* @example
|
|
2982
|
+
* ```ts
|
|
2983
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
2984
|
+
* .populate('authorId', 'author')
|
|
2985
|
+
* .populate('author.companyId')
|
|
2986
|
+
* // post.author.companyId is now a Company document
|
|
2987
|
+
* ```
|
|
2988
|
+
*/
|
|
2989
|
+
populate<TPath extends NestedRefPath<TPopMap>>(path: TPath): PopulateOneQuery<TDef, DeepPopulate<TOutput, `${ExtractParent<TPath>}.${ExtractLeaf<TPath>}`, ExtractLeaf<TPath>, ExtractLeaf<TPath>, PopulateField<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>>, TPopMap & {
|
|
2990
|
+
[P in `${ExtractParent<TPath>}.${ExtractLeaf<TPath>}`]: ExtractRefCollection<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>;
|
|
2991
|
+
}>;
|
|
2992
|
+
/**
|
|
2993
|
+
* Populate a nested ref field and rename the leaf in the output.
|
|
2994
|
+
*
|
|
2995
|
+
* The parent path must have been populated in a previous `.populate()` call.
|
|
2996
|
+
*
|
|
2997
|
+
* @param path - A dot-separated path like `'author.companyId'`.
|
|
2998
|
+
* @param as - The new name for the leaf field in the output.
|
|
2999
|
+
* @returns A new builder with the nested field renamed and typed.
|
|
3000
|
+
*
|
|
3001
|
+
* @example
|
|
3002
|
+
* ```ts
|
|
3003
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
3004
|
+
* .populate('authorId', 'author')
|
|
3005
|
+
* .populate('author.companyId', 'company')
|
|
3006
|
+
* // post.author.company is a Company document
|
|
3007
|
+
* ```
|
|
3008
|
+
*/
|
|
3009
|
+
populate<TPath extends NestedRefPath<TPopMap>, TAs extends string>(path: TPath, as: TAs): PopulateOneQuery<TDef, DeepPopulate<TOutput, `${ExtractParent<TPath>}.${TAs}`, ExtractLeaf<TPath>, TAs, PopulateField<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>>, TPopMap & {
|
|
3010
|
+
[P in `${ExtractParent<TPath>}.${TAs}`]: ExtractRefCollection<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>;
|
|
3011
|
+
}>;
|
|
3012
|
+
/**
|
|
3013
|
+
* Attach fulfillment and rejection handlers to the query promise.
|
|
3014
|
+
*
|
|
3015
|
+
* Executes the base findOne query and applies populate steps if any.
|
|
3016
|
+
* Returns `null` when no document matches the filter.
|
|
3017
|
+
*/
|
|
3018
|
+
then<TResult1 = TOutput | null, TResult2 = never>(onfulfilled?: ((value: TOutput | null) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
3019
|
+
private execute;
|
|
3020
|
+
}
|
|
1878
3021
|
/**
|
|
1879
|
-
*
|
|
3022
|
+
* Fluent populate builder for findOneOrThrow queries.
|
|
1880
3023
|
*
|
|
1881
|
-
*
|
|
1882
|
-
*
|
|
3024
|
+
* Identical to {@link PopulateOneQuery} but resolves to `TOutput` (never null).
|
|
3025
|
+
* Throws {@link ZodmonNotFoundError} when no document matches the filter.
|
|
1883
3026
|
*
|
|
1884
3027
|
* @example
|
|
1885
3028
|
* ```ts
|
|
1886
|
-
* const
|
|
1887
|
-
*
|
|
1888
|
-
*
|
|
1889
|
-
* console.log('skipped:', result.skipped)
|
|
1890
|
-
* console.log('stale:', result.stale.map(s => s.name))
|
|
3029
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3030
|
+
* .populate('authorId', 'author')
|
|
3031
|
+
* // Guaranteed non-null; throws if not found
|
|
1891
3032
|
* ```
|
|
1892
3033
|
*/
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
3034
|
+
declare class PopulateOneOrThrowQuery<TDef extends AnyCollection, TOutput, TPopMap extends Record<string, AnyCollection> = Record<string, never>> implements PromiseLike<TOutput> {
|
|
3035
|
+
private readonly handle;
|
|
3036
|
+
private readonly filter;
|
|
3037
|
+
private readonly options;
|
|
3038
|
+
private readonly steps;
|
|
3039
|
+
constructor(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options: FindOneOptions | undefined, steps?: readonly PopulateStep[]);
|
|
3040
|
+
/**
|
|
3041
|
+
* Populate a top-level ref field, keeping the original field name.
|
|
3042
|
+
*
|
|
3043
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
3044
|
+
* @returns A new builder with the field type replaced by the referenced document.
|
|
3045
|
+
*
|
|
3046
|
+
* @example
|
|
3047
|
+
* ```ts
|
|
3048
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3049
|
+
* .populate('authorId')
|
|
3050
|
+
* ```
|
|
3051
|
+
*/
|
|
3052
|
+
populate<K extends RefFields<TDef>>(field: K): PopulateOneOrThrowQuery<TDef, ApplyPopulate<TOutput, TDef, K>, TPopMap & {
|
|
3053
|
+
[P in K]: ExtractRefCollection<TDef, K>;
|
|
3054
|
+
}>;
|
|
3055
|
+
/**
|
|
3056
|
+
* Populate a top-level ref field with a projection, keeping the original field name.
|
|
3057
|
+
*
|
|
3058
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
3059
|
+
* @param configure - Callback receiving a builder; call `.project()` to set the projection.
|
|
3060
|
+
* @returns A new builder with the field type narrowed to only the projected fields.
|
|
3061
|
+
*
|
|
3062
|
+
* @example
|
|
3063
|
+
* ```ts
|
|
3064
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3065
|
+
* .populate('authorId', (b) => b.project({ name: 1, email: 1 }))
|
|
3066
|
+
* // post.authorId is { _id: ObjectId; name: string; email: string }
|
|
3067
|
+
* // post.authorId.age → TS error
|
|
3068
|
+
* ```
|
|
3069
|
+
*/
|
|
3070
|
+
populate<K extends RefFields<TDef>, P>(field: K, configure: (b: PopulateRefBuilder<InferDocument<ExtractRefCollection<TDef, K>>>) => PopulateProjectionConfig<P>): PopulateOneOrThrowQuery<TDef, ApplyPopulateProjected<TOutput, TDef, K, P>, TPopMap & {
|
|
3071
|
+
[F in K]: ExtractRefCollection<TDef, K>;
|
|
3072
|
+
}>;
|
|
3073
|
+
/**
|
|
3074
|
+
* Populate a top-level ref field and rename it in the output.
|
|
3075
|
+
*
|
|
3076
|
+
* @param field - A ref-bearing field name on the root collection.
|
|
3077
|
+
* @param as - The new field name in the output document.
|
|
3078
|
+
* @returns A new builder with the old field removed and the new field added.
|
|
3079
|
+
*
|
|
3080
|
+
* @example
|
|
3081
|
+
* ```ts
|
|
3082
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3083
|
+
* .populate('authorId', 'author')
|
|
3084
|
+
* ```
|
|
3085
|
+
*/
|
|
3086
|
+
populate<K extends RefFields<TDef>, TAs extends string>(field: K, as: TAs): PopulateOneOrThrowQuery<TDef, ApplyPopulate<TOutput, TDef, K, TAs>, TPopMap & {
|
|
3087
|
+
[P in TAs]: ExtractRefCollection<TDef, K>;
|
|
3088
|
+
}>;
|
|
3089
|
+
/**
|
|
3090
|
+
* Populate a nested ref field, keeping the leaf field name.
|
|
3091
|
+
*
|
|
3092
|
+
* @param path - A dot-separated path like `'author.companyId'`.
|
|
3093
|
+
* @returns A new builder with the nested field type replaced.
|
|
3094
|
+
*
|
|
3095
|
+
* @example
|
|
3096
|
+
* ```ts
|
|
3097
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3098
|
+
* .populate('authorId', 'author')
|
|
3099
|
+
* .populate('author.companyId')
|
|
3100
|
+
* ```
|
|
3101
|
+
*/
|
|
3102
|
+
populate<TPath extends NestedRefPath<TPopMap>>(path: TPath): PopulateOneOrThrowQuery<TDef, DeepPopulate<TOutput, `${ExtractParent<TPath>}.${ExtractLeaf<TPath>}`, ExtractLeaf<TPath>, ExtractLeaf<TPath>, PopulateField<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>>, TPopMap & {
|
|
3103
|
+
[P in `${ExtractParent<TPath>}.${ExtractLeaf<TPath>}`]: ExtractRefCollection<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>;
|
|
3104
|
+
}>;
|
|
3105
|
+
/**
|
|
3106
|
+
* Populate a nested ref field and rename the leaf in the output.
|
|
3107
|
+
*
|
|
3108
|
+
* @param path - A dot-separated path like `'author.companyId'`.
|
|
3109
|
+
* @param as - The new name for the leaf field in the output.
|
|
3110
|
+
* @returns A new builder with the nested field renamed and typed.
|
|
3111
|
+
*
|
|
3112
|
+
* @example
|
|
3113
|
+
* ```ts
|
|
3114
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3115
|
+
* .populate('authorId', 'author')
|
|
3116
|
+
* .populate('author.companyId', 'company')
|
|
3117
|
+
* ```
|
|
3118
|
+
*/
|
|
3119
|
+
populate<TPath extends NestedRefPath<TPopMap>, TAs extends string>(path: TPath, as: TAs): PopulateOneOrThrowQuery<TDef, DeepPopulate<TOutput, `${ExtractParent<TPath>}.${TAs}`, ExtractLeaf<TPath>, TAs, PopulateField<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>>, TPopMap & {
|
|
3120
|
+
[P in `${ExtractParent<TPath>}.${TAs}`]: ExtractRefCollection<TPopMap[ExtractParent<TPath>], ExtractLeaf<TPath> & RefFields<TPopMap[ExtractParent<TPath>]>>;
|
|
3121
|
+
}>;
|
|
3122
|
+
/**
|
|
3123
|
+
* Attach fulfillment and rejection handlers to the query promise.
|
|
3124
|
+
*
|
|
3125
|
+
* Executes the base findOneOrThrow query and applies populate steps if any.
|
|
3126
|
+
* Throws {@link ZodmonNotFoundError} when no document matches.
|
|
3127
|
+
*/
|
|
3128
|
+
then<TResult1 = TOutput, TResult2 = never>(onfulfilled?: ((value: TOutput) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
3129
|
+
private execute;
|
|
3130
|
+
}
|
|
1903
3131
|
|
|
1904
3132
|
/**
|
|
1905
3133
|
* Typed wrapper around a MongoDB driver `Collection`.
|
|
@@ -1916,7 +3144,35 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
1916
3144
|
readonly definition: TDef;
|
|
1917
3145
|
/** The underlying MongoDB driver collection, typed to the inferred document type. */
|
|
1918
3146
|
readonly native: Collection<InferDocument<TDef>>;
|
|
1919
|
-
|
|
3147
|
+
/**
|
|
3148
|
+
* The MongoDB client session bound to this handle, if any.
|
|
3149
|
+
*
|
|
3150
|
+
* When set, all CRUD and aggregation operations performed through this
|
|
3151
|
+
* handle will include the session in their options, enabling transactional
|
|
3152
|
+
* reads and writes. Undefined when no session is bound.
|
|
3153
|
+
*/
|
|
3154
|
+
readonly session: ClientSession | undefined;
|
|
3155
|
+
constructor(definition: TDef, native: Collection<InferDocument<TDef>>, session?: ClientSession);
|
|
3156
|
+
/**
|
|
3157
|
+
* Create a new handle bound to the given MongoDB client session.
|
|
3158
|
+
*
|
|
3159
|
+
* Returns a new {@link CollectionHandle} that shares the same collection
|
|
3160
|
+
* definition and native driver collection, but passes `session` to every
|
|
3161
|
+
* CRUD and aggregation operation. The original handle is not modified.
|
|
3162
|
+
*
|
|
3163
|
+
* @param session - The MongoDB `ClientSession` to bind.
|
|
3164
|
+
* @returns A new handle with the session attached.
|
|
3165
|
+
*
|
|
3166
|
+
* @example
|
|
3167
|
+
* ```ts
|
|
3168
|
+
* const users = db.use(Users)
|
|
3169
|
+
* await db.client.withSession(async (session) => {
|
|
3170
|
+
* const bound = users.withSession(session)
|
|
3171
|
+
* await bound.insertOne({ name: 'Ada' }) // uses session
|
|
3172
|
+
* })
|
|
3173
|
+
* ```
|
|
3174
|
+
*/
|
|
3175
|
+
withSession(session: ClientSession): CollectionHandle<TDef>;
|
|
1920
3176
|
/**
|
|
1921
3177
|
* Insert a single document into the collection.
|
|
1922
3178
|
*
|
|
@@ -1960,13 +3216,12 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
1960
3216
|
/**
|
|
1961
3217
|
* Find a single document matching the filter.
|
|
1962
3218
|
*
|
|
1963
|
-
*
|
|
1964
|
-
*
|
|
1965
|
-
* back to the collection-level default (which defaults to `'strict'`).
|
|
3219
|
+
* Returns a {@link PopulateOneQuery} that can be awaited directly or chained
|
|
3220
|
+
* with `.populate()` calls to resolve foreign key references.
|
|
1966
3221
|
*
|
|
1967
3222
|
* @param filter - Type-safe filter to match documents.
|
|
1968
|
-
* @param options - Optional
|
|
1969
|
-
* @returns
|
|
3223
|
+
* @param options - Optional validation overrides.
|
|
3224
|
+
* @returns A populate builder that resolves to the matched document, or `null`.
|
|
1970
3225
|
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1971
3226
|
*
|
|
1972
3227
|
* @example
|
|
@@ -1975,17 +3230,44 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
1975
3230
|
* const user = await users.findOne({ name: 'Ada' })
|
|
1976
3231
|
* if (user) console.log(user.role)
|
|
1977
3232
|
* ```
|
|
3233
|
+
*
|
|
3234
|
+
* @example
|
|
3235
|
+
* ```ts
|
|
3236
|
+
* const post = await posts.findOne({ title: 'Hello' })
|
|
3237
|
+
* .populate('authorId', 'author')
|
|
3238
|
+
* ```
|
|
3239
|
+
*/
|
|
3240
|
+
findOne(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): PopulateOneQuery<TDef, InferDocument<TDef>>;
|
|
3241
|
+
/**
|
|
3242
|
+
* Find a single document matching the filter with a type-safe projection.
|
|
3243
|
+
*
|
|
3244
|
+
* Returns only the fields specified by the projection. The return type is
|
|
3245
|
+
* narrowed to reflect which fields are included or excluded.
|
|
3246
|
+
* Projected queries do not support `.populate()`.
|
|
3247
|
+
*
|
|
3248
|
+
* @param filter - Type-safe filter to match documents.
|
|
3249
|
+
* @param options - Projection and optional validation overrides.
|
|
3250
|
+
* @returns The projected document, or `null` if no document matches.
|
|
3251
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
3252
|
+
*
|
|
3253
|
+
* @example
|
|
3254
|
+
* ```ts
|
|
3255
|
+
* const users = db.use(Users)
|
|
3256
|
+
* const user = await users.findOne({ name: 'Ada' }, { project: { name: 1 } })
|
|
3257
|
+
* if (user) console.log(user.name) // typed as string
|
|
3258
|
+
* ```
|
|
1978
3259
|
*/
|
|
1979
|
-
findOne(filter: TypedFilter<InferDocument<TDef>>, options
|
|
3260
|
+
findOne<P extends TypedProjection<InferDocument<TDef>>>(filter: TypedFilter<InferDocument<TDef>>, options: FindOneProjectionOptions<InferDocument<TDef>, P>): Promise<Prettify<ProjectionResult<InferDocument<TDef>, P>> | null>;
|
|
1980
3261
|
/**
|
|
1981
3262
|
* Find a single document matching the filter, or throw if none exists.
|
|
1982
3263
|
*
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
3264
|
+
* Returns a {@link PopulateOneOrThrowQuery} that can be awaited directly or
|
|
3265
|
+
* chained with `.populate()` calls. Throws {@link ZodmonNotFoundError}
|
|
3266
|
+
* instead of returning `null` when no document matches.
|
|
1985
3267
|
*
|
|
1986
3268
|
* @param filter - Type-safe filter to match documents.
|
|
1987
|
-
* @param options - Optional
|
|
1988
|
-
* @returns
|
|
3269
|
+
* @param options - Optional validation overrides.
|
|
3270
|
+
* @returns A populate builder that resolves to the matched document (never null).
|
|
1989
3271
|
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
1990
3272
|
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1991
3273
|
*
|
|
@@ -1995,8 +3277,36 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
1995
3277
|
* const user = await users.findOneOrThrow({ name: 'Ada' })
|
|
1996
3278
|
* console.log(user.role) // guaranteed non-null
|
|
1997
3279
|
* ```
|
|
3280
|
+
*
|
|
3281
|
+
* @example
|
|
3282
|
+
* ```ts
|
|
3283
|
+
* const post = await posts.findOneOrThrow({ title: 'Hello' })
|
|
3284
|
+
* .populate('authorId', 'author')
|
|
3285
|
+
* ```
|
|
3286
|
+
*/
|
|
3287
|
+
findOneOrThrow(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): PopulateOneOrThrowQuery<TDef, InferDocument<TDef>>;
|
|
3288
|
+
/**
|
|
3289
|
+
* Find a single document matching the filter with a type-safe projection, or throw if none exists.
|
|
3290
|
+
*
|
|
3291
|
+
* Returns only the fields specified by the projection. The return type is
|
|
3292
|
+
* narrowed to reflect which fields are included or excluded. Throws
|
|
3293
|
+
* {@link ZodmonNotFoundError} instead of returning `null`.
|
|
3294
|
+
* Projected queries do not support `.populate()`.
|
|
3295
|
+
*
|
|
3296
|
+
* @param filter - Type-safe filter to match documents.
|
|
3297
|
+
* @param options - Projection and optional validation overrides.
|
|
3298
|
+
* @returns The projected document (never null).
|
|
3299
|
+
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
3300
|
+
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
3301
|
+
*
|
|
3302
|
+
* @example
|
|
3303
|
+
* ```ts
|
|
3304
|
+
* const users = db.use(Users)
|
|
3305
|
+
* const user = await users.findOneOrThrow({ name: 'Ada' }, { project: { name: 1 } })
|
|
3306
|
+
* console.log(user.name) // typed as string
|
|
3307
|
+
* ```
|
|
1998
3308
|
*/
|
|
1999
|
-
findOneOrThrow(filter: TypedFilter<InferDocument<TDef>>, options
|
|
3309
|
+
findOneOrThrow<P extends TypedProjection<InferDocument<TDef>>>(filter: TypedFilter<InferDocument<TDef>>, options: FindOneProjectionOptions<InferDocument<TDef>, P>): Promise<Prettify<ProjectionResult<InferDocument<TDef>, P>>>;
|
|
2000
3310
|
/**
|
|
2001
3311
|
* Find all documents matching the filter, returning a chainable typed cursor.
|
|
2002
3312
|
*
|
|
@@ -2018,6 +3328,24 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
2018
3328
|
* ```
|
|
2019
3329
|
*/
|
|
2020
3330
|
find(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef, IndexNames<TDef>>;
|
|
3331
|
+
/**
|
|
3332
|
+
* Find all documents matching the filter with a type-safe projection, returning a chainable typed cursor.
|
|
3333
|
+
*
|
|
3334
|
+
* The return type is narrowed to reflect which fields are included or excluded.
|
|
3335
|
+
*
|
|
3336
|
+
* @param filter - Type-safe filter to match documents.
|
|
3337
|
+
* @param options - Projection and optional validation overrides.
|
|
3338
|
+
* @returns A typed cursor whose output type matches the projection.
|
|
3339
|
+
*
|
|
3340
|
+
* @example
|
|
3341
|
+
* ```ts
|
|
3342
|
+
* const users = db.use(Users)
|
|
3343
|
+
* const names = await users.find({ role: 'admin' }, { project: { name: 1 } })
|
|
3344
|
+
* .toArray()
|
|
3345
|
+
* // names[0].name — string
|
|
3346
|
+
* ```
|
|
3347
|
+
*/
|
|
3348
|
+
find<P extends TypedProjection<InferDocument<TDef>>>(filter: TypedFilter<InferDocument<TDef>>, options: FindProjectionOptions<InferDocument<TDef>, P>): TypedFindCursor<TDef, IndexNames<TDef>, Prettify<ProjectionResult<InferDocument<TDef>, P>>>;
|
|
2021
3349
|
/**
|
|
2022
3350
|
* Update a single document matching the filter.
|
|
2023
3351
|
*
|
|
@@ -2213,7 +3541,8 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2213
3541
|
protected readonly definition: TDef;
|
|
2214
3542
|
private readonly nativeCollection;
|
|
2215
3543
|
private readonly stages;
|
|
2216
|
-
|
|
3544
|
+
private readonly session;
|
|
3545
|
+
constructor(definition: TDef, nativeCollection: Collection<InferDocument<TDef>>, stages: Document[], session?: ClientSession);
|
|
2217
3546
|
/**
|
|
2218
3547
|
* Append an arbitrary aggregation stage to the pipeline (escape hatch).
|
|
2219
3548
|
*
|
|
@@ -2329,10 +3658,21 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2329
3658
|
* .toArray()
|
|
2330
3659
|
* // subset[0].role → 'engineer' | 'designer'
|
|
2331
3660
|
* ```
|
|
3661
|
+
*
|
|
3662
|
+
* @example
|
|
3663
|
+
* ```ts
|
|
3664
|
+
* // Field-vs-field comparison via $expr callback
|
|
3665
|
+
* const overRefunded = await orders.aggregate()
|
|
3666
|
+
* .match(
|
|
3667
|
+
* { status: 'completed' },
|
|
3668
|
+
* (expr) => expr.gt('totalAmount', expr.field('refundedAmount')),
|
|
3669
|
+
* )
|
|
3670
|
+
* .toArray()
|
|
3671
|
+
* ```
|
|
2332
3672
|
*/
|
|
2333
3673
|
match<TNarrow extends {
|
|
2334
3674
|
[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>>;
|
|
3675
|
+
} = {}, F extends TypedFilter<TOutput> = TypedFilter<TOutput>>(filter: F, exprCb?: (expr: ExpressionBuilder<TOutput>) => Expression<boolean>): AggregatePipeline<TDef, Prettify<Omit<NarrowFromFilter<TOutput, F>, keyof TNarrow> & TNarrow>>;
|
|
2336
3676
|
/**
|
|
2337
3677
|
* Sort documents by one or more fields.
|
|
2338
3678
|
*
|
|
@@ -2449,7 +3789,7 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2449
3789
|
* and compile-time field validation. Builder methods resolve return types
|
|
2450
3790
|
* to the actual field type (`T[K]`), not `unknown`.
|
|
2451
3791
|
*
|
|
2452
|
-
* @param field - A field name
|
|
3792
|
+
* @param field - A field name, array of field names, or `null` to aggregate all documents into a single group.
|
|
2453
3793
|
* @param accumulators - A callback `(acc) => ({ ... })` or plain accumulator object.
|
|
2454
3794
|
* @returns A new pipeline with the `$group` stage appended.
|
|
2455
3795
|
*
|
|
@@ -2480,9 +3820,22 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2480
3820
|
* .groupBy(['role', 'dept'], acc => ({ count: acc.count() }))
|
|
2481
3821
|
* .toArray()
|
|
2482
3822
|
* ```
|
|
3823
|
+
*
|
|
3824
|
+
* @example
|
|
3825
|
+
* ```ts
|
|
3826
|
+
* // Null groupBy — aggregate all documents into a single summary
|
|
3827
|
+
* const totals = await aggregate(orders)
|
|
3828
|
+
* .groupBy(null, acc => ({
|
|
3829
|
+
* grandTotal: acc.sum('amount'),
|
|
3830
|
+
* orderCount: acc.count(),
|
|
3831
|
+
* }))
|
|
3832
|
+
* .toArray()
|
|
3833
|
+
* // → [{ _id: null, grandTotal: 2740, orderCount: 7 }]
|
|
3834
|
+
* ```
|
|
2483
3835
|
*/
|
|
2484
|
-
groupBy<
|
|
2485
|
-
groupBy<K extends
|
|
3836
|
+
groupBy<TAccum extends Record<string, Accumulator>>(field: null, accumulators: ((acc: AccumulatorBuilder<TOutput>) => TAccum) | TAccum): AggregatePipeline<TDef, GroupByNullResult<TAccum>>;
|
|
3837
|
+
groupBy<K extends DotPath<TOutput>, TAccum extends Record<string, Accumulator>>(field: K, accumulators: ((acc: AccumulatorBuilder<TOutput>) => TAccum) | TAccum): AggregatePipeline<TDef, GroupByResult<TOutput, K, TAccum>>;
|
|
3838
|
+
groupBy<K extends DotPath<TOutput>, TAccum extends Record<string, Accumulator>>(field: K[], accumulators: ((acc: AccumulatorBuilder<TOutput>) => TAccum) | TAccum): AggregatePipeline<TDef, GroupByCompoundResult<TOutput, K, TAccum>>;
|
|
2486
3839
|
/**
|
|
2487
3840
|
* Add new fields or overwrite existing ones in the output documents.
|
|
2488
3841
|
*
|
|
@@ -2617,6 +3970,37 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2617
3970
|
}): AggregatePipeline<TDef, Prettify<TOutput & {
|
|
2618
3971
|
[P in TAs]: InferDocument<TForeignDef>[];
|
|
2619
3972
|
}>>;
|
|
3973
|
+
/**
|
|
3974
|
+
* Run multiple sub-pipelines on the same input documents in parallel.
|
|
3975
|
+
*
|
|
3976
|
+
* Each key in `spec` maps to a callback that receives a fresh `SubPipeline`
|
|
3977
|
+
* starting from `TOutput`. The callback chains stages and returns the terminal
|
|
3978
|
+
* pipeline. Zodmon extracts the accumulated stages at runtime to build the
|
|
3979
|
+
* `$facet` document. The output type is fully inferred — no annotation needed.
|
|
3980
|
+
*
|
|
3981
|
+
* Sub-pipelines support all stage methods including `.raw()` for operators not
|
|
3982
|
+
* yet first-class. Execution methods (`toArray`, `explain`) are not available
|
|
3983
|
+
* inside branches.
|
|
3984
|
+
*
|
|
3985
|
+
* @param spec - An object mapping branch names to sub-pipeline builder callbacks.
|
|
3986
|
+
* @returns A new pipeline whose output is one document with each branch name mapped to an array of results.
|
|
3987
|
+
*
|
|
3988
|
+
* @example
|
|
3989
|
+
* ```ts
|
|
3990
|
+
* const [report] = await aggregate(orders)
|
|
3991
|
+
* .facet({
|
|
3992
|
+
* byCategory: (sub) => sub
|
|
3993
|
+
* .groupBy('category', acc => ({ count: acc.count() }))
|
|
3994
|
+
* .sort({ count: -1 }),
|
|
3995
|
+
* totals: (sub) => sub
|
|
3996
|
+
* .groupBy(null, acc => ({ grandTotal: acc.sum('amount') })),
|
|
3997
|
+
* })
|
|
3998
|
+
* .toArray()
|
|
3999
|
+
* // report.byCategory → { _id: 'electronics' | 'books' | 'clothing'; count: number }[]
|
|
4000
|
+
* // report.totals → { _id: null; grandTotal: number }[]
|
|
4001
|
+
* ```
|
|
4002
|
+
*/
|
|
4003
|
+
facet<TSpec extends Record<string, (sub: SubPipeline<TDef, TOutput>) => AggregatePipeline<TDef, any>>>(spec: TSpec): AggregatePipeline<TDef, Prettify<InferFacetOutput<TSpec>>>;
|
|
2620
4004
|
/**
|
|
2621
4005
|
* Count documents per group, sorted by count descending.
|
|
2622
4006
|
*
|
|
@@ -2713,6 +4097,8 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2713
4097
|
bottom(n: number, options: {
|
|
2714
4098
|
by: keyof TOutput & string;
|
|
2715
4099
|
}): AggregatePipeline<TDef, TOutput>;
|
|
4100
|
+
/** @internal Used by facet() to extract branch stages. Not part of the public API. */
|
|
4101
|
+
getStages(): Document[];
|
|
2716
4102
|
}
|
|
2717
4103
|
/**
|
|
2718
4104
|
* Create a new aggregation pipeline for a collection.
|
|
@@ -2732,6 +4118,99 @@ declare class AggregatePipeline<TDef extends AnyCollection, TOutput> {
|
|
|
2732
4118
|
* ```
|
|
2733
4119
|
*/
|
|
2734
4120
|
declare function aggregate<TDef extends AnyCollection>(handle: CollectionHandle<TDef>): AggregatePipeline<TDef, InferDocument<TDef>>;
|
|
4121
|
+
/**
|
|
4122
|
+
* A sub-pipeline passed to each `.facet()` branch callback.
|
|
4123
|
+
*
|
|
4124
|
+
* All stage-building methods are available. Execution methods (`toArray`,
|
|
4125
|
+
* `explain`, `[Symbol.asyncIterator]`) are omitted — sub-pipelines are
|
|
4126
|
+
* not executed directly; they only accumulate stages for the parent `$facet` stage.
|
|
4127
|
+
*
|
|
4128
|
+
* @typeParam TDef - The collection definition type.
|
|
4129
|
+
* @typeParam TOutput - The current output document type.
|
|
4130
|
+
*
|
|
4131
|
+
* @example
|
|
4132
|
+
* ```ts
|
|
4133
|
+
* .facet({
|
|
4134
|
+
* summary: (sub: SubPipeline<typeof Orders, Order>) => sub.groupBy(null, acc => ({ total: acc.sum('amount') }))
|
|
4135
|
+
* })
|
|
4136
|
+
* ```
|
|
4137
|
+
*/
|
|
4138
|
+
type SubPipeline<TDef extends AnyCollection, TOutput> = Omit<AggregatePipeline<TDef, TOutput>, 'toArray' | 'explain' | typeof Symbol.asyncIterator>;
|
|
4139
|
+
/**
|
|
4140
|
+
* Maps a `.facet()` spec object to its inferred output shape.
|
|
4141
|
+
*
|
|
4142
|
+
* Each key maps to the array of results produced by that branch's terminal pipeline.
|
|
4143
|
+
* Uses `any` for the sub-pipeline parameter to avoid TypeScript contravariance
|
|
4144
|
+
* complications — inference of `TOut` from the return type is what matters.
|
|
4145
|
+
*
|
|
4146
|
+
* @example
|
|
4147
|
+
* ```ts
|
|
4148
|
+
* type Spec = {
|
|
4149
|
+
* byCategory: (sub: SubPipeline<any, any>) => AggregatePipeline<any, { _id: string; count: number }>
|
|
4150
|
+
* }
|
|
4151
|
+
* type Result = InferFacetOutput<Spec>
|
|
4152
|
+
* // ^? { byCategory: { _id: string; count: number }[] }
|
|
4153
|
+
* ```
|
|
4154
|
+
*/
|
|
4155
|
+
type InferFacetOutput<TSpec> = {
|
|
4156
|
+
[K in keyof TSpec]: TSpec[K] extends (sub: any) => AggregatePipeline<any, infer TOut> ? TOut[] : never;
|
|
4157
|
+
};
|
|
4158
|
+
|
|
4159
|
+
/**
|
|
4160
|
+
* The callback signature for {@link Database.transaction}.
|
|
4161
|
+
*
|
|
4162
|
+
* @typeParam T - The return type of the transaction callback.
|
|
4163
|
+
*
|
|
4164
|
+
* @example
|
|
4165
|
+
* ```ts
|
|
4166
|
+
* const fn: TransactionFn<void> = async (tx) => {
|
|
4167
|
+
* const txUsers = tx.use(users)
|
|
4168
|
+
* await txUsers.insertOne({ name: 'Ada' })
|
|
4169
|
+
* }
|
|
4170
|
+
* ```
|
|
4171
|
+
*/
|
|
4172
|
+
type TransactionFn<T> = (tx: TransactionContext) => Promise<T>;
|
|
4173
|
+
/**
|
|
4174
|
+
* Transaction context passed to the {@link Database.transaction} callback.
|
|
4175
|
+
*
|
|
4176
|
+
* Use {@link use} to bind existing collection handles to this transaction's
|
|
4177
|
+
* session. All operations on the returned handle participate in the
|
|
4178
|
+
* transaction -- auto-committed on success, auto-rolled-back on error.
|
|
4179
|
+
*
|
|
4180
|
+
* @example
|
|
4181
|
+
* ```ts
|
|
4182
|
+
* await db.transaction(async (tx) => {
|
|
4183
|
+
* const txUsers = tx.use(users)
|
|
4184
|
+
* const txPosts = tx.use(posts)
|
|
4185
|
+
* const user = await txUsers.insertOne({ name: 'Ada' })
|
|
4186
|
+
* await txPosts.insertOne({ authorId: user._id, title: 'Hello' })
|
|
4187
|
+
* })
|
|
4188
|
+
* ```
|
|
4189
|
+
*/
|
|
4190
|
+
declare class TransactionContext {
|
|
4191
|
+
/** @internal */
|
|
4192
|
+
private readonly session;
|
|
4193
|
+
/** @internal */
|
|
4194
|
+
constructor(session: ClientSession);
|
|
4195
|
+
/**
|
|
4196
|
+
* Bind a collection handle to this transaction's session.
|
|
4197
|
+
*
|
|
4198
|
+
* Returns a cloned handle whose CRUD operations automatically include
|
|
4199
|
+
* the transaction session. The original handle is not modified.
|
|
4200
|
+
*
|
|
4201
|
+
* @param handle - An existing collection handle from `db.use()`.
|
|
4202
|
+
* @returns A new handle bound to the transaction session.
|
|
4203
|
+
*
|
|
4204
|
+
* @example
|
|
4205
|
+
* ```ts
|
|
4206
|
+
* await db.transaction(async (tx) => {
|
|
4207
|
+
* const txUsers = tx.use(users)
|
|
4208
|
+
* await txUsers.insertOne({ name: 'Ada' })
|
|
4209
|
+
* })
|
|
4210
|
+
* ```
|
|
4211
|
+
*/
|
|
4212
|
+
use<TDef extends AnyCollection>(handle: CollectionHandle<TDef>): CollectionHandle<TDef>;
|
|
4213
|
+
}
|
|
2735
4214
|
|
|
2736
4215
|
/**
|
|
2737
4216
|
* Wraps a MongoDB `MongoClient` and `Db`, providing typed collection access
|
|
@@ -2788,11 +4267,40 @@ declare class Database {
|
|
|
2788
4267
|
*/
|
|
2789
4268
|
syncIndexes(options?: SyncIndexesOptions): Promise<Record<string, SyncIndexesResult>>;
|
|
2790
4269
|
/**
|
|
2791
|
-
* Execute a function within a MongoDB transaction
|
|
4270
|
+
* Execute a function within a MongoDB transaction.
|
|
4271
|
+
*
|
|
4272
|
+
* Starts a client session and runs the callback inside
|
|
4273
|
+
* `session.withTransaction()`. The driver handles commit on success,
|
|
4274
|
+
* abort on error, and automatic retries for transient transaction errors.
|
|
4275
|
+
*
|
|
4276
|
+
* The return value of `fn` is forwarded as the return value of this method.
|
|
4277
|
+
*
|
|
4278
|
+
* @param fn - Async callback receiving a {@link TransactionContext}.
|
|
4279
|
+
* @returns The value returned by `fn`.
|
|
4280
|
+
*
|
|
4281
|
+
* @example
|
|
4282
|
+
* ```ts
|
|
4283
|
+
* const user = await db.transaction(async (tx) => {
|
|
4284
|
+
* const txUsers = tx.use(users)
|
|
4285
|
+
* return await txUsers.insertOne({ name: 'Ada' })
|
|
4286
|
+
* })
|
|
4287
|
+
* ```
|
|
2792
4288
|
*
|
|
2793
|
-
*
|
|
4289
|
+
* @example
|
|
4290
|
+
* ```ts
|
|
4291
|
+
* // Rollback on error
|
|
4292
|
+
* try {
|
|
4293
|
+
* await db.transaction(async (tx) => {
|
|
4294
|
+
* const txUsers = tx.use(users)
|
|
4295
|
+
* await txUsers.insertOne({ name: 'Ada' })
|
|
4296
|
+
* throw new Error('abort!')
|
|
4297
|
+
* })
|
|
4298
|
+
* } catch (err) {
|
|
4299
|
+
* // insert was rolled back, err is the original error
|
|
4300
|
+
* }
|
|
4301
|
+
* ```
|
|
2794
4302
|
*/
|
|
2795
|
-
transaction<T>(
|
|
4303
|
+
transaction<T>(fn: TransactionFn<T>): Promise<T>;
|
|
2796
4304
|
/**
|
|
2797
4305
|
* Close the underlying `MongoClient` connection. Safe to call even if
|
|
2798
4306
|
* no connection was established (the driver handles this gracefully).
|
|
@@ -3610,6 +5118,68 @@ type WarnableDefinition = {
|
|
|
3610
5118
|
*/
|
|
3611
5119
|
declare function checkUnindexedFields(definition: WarnableDefinition, filter: Record<string, unknown>): void;
|
|
3612
5120
|
|
|
5121
|
+
/**
|
|
5122
|
+
* Walk through Zod wrappers to find the inner schema with ref metadata.
|
|
5123
|
+
*
|
|
5124
|
+
* Mirrors the type-level `UnwrapRef<T>` at runtime. Peels ZodOptional,
|
|
5125
|
+
* ZodNullable, ZodDefault (via `_zod.def.innerType`), and ZodArray
|
|
5126
|
+
* (via `_zod.def.element`).
|
|
5127
|
+
*
|
|
5128
|
+
* @param schema - The Zod schema to unwrap.
|
|
5129
|
+
* @returns The innermost schema after stripping wrappers.
|
|
5130
|
+
*
|
|
5131
|
+
* @example
|
|
5132
|
+
* ```ts
|
|
5133
|
+
* const inner = unwrapRefSchema(Posts.shape.categoryIds)
|
|
5134
|
+
* const ref = getRefMetadata(inner) // { collection: Categories }
|
|
5135
|
+
* ```
|
|
5136
|
+
*/
|
|
5137
|
+
declare function unwrapRefSchema(schema: z.ZodType): z.ZodType;
|
|
5138
|
+
/**
|
|
5139
|
+
* Resolve a `.populate()` call into a `PopulateStep`.
|
|
5140
|
+
*
|
|
5141
|
+
* For top-level fields, inspects the collection's shape directly.
|
|
5142
|
+
* For nested dot-paths, walks previous steps to find the parent
|
|
5143
|
+
* collection definition, then resolves the leaf field in that shape.
|
|
5144
|
+
*
|
|
5145
|
+
* @param definition - The root collection definition.
|
|
5146
|
+
* @param previousSteps - Steps from earlier `.populate()` calls.
|
|
5147
|
+
* @param path - The field path (e.g. `'authorId'` or `'author.companyId'`).
|
|
5148
|
+
* @param as - The output field name (renamed or original).
|
|
5149
|
+
* @param projection - Optional projection to pass to the `$in` query.
|
|
5150
|
+
* @returns A resolved `PopulateStep`.
|
|
5151
|
+
* @throws When the field has no `.ref()` metadata or the parent path is not populated.
|
|
5152
|
+
*
|
|
5153
|
+
* @example
|
|
5154
|
+
* ```ts
|
|
5155
|
+
* const step = resolvePopulateStep(Posts, [], 'authorId', 'author')
|
|
5156
|
+
* // { leafField: 'authorId', as: 'author', targetCollection: Users, ... }
|
|
5157
|
+
* ```
|
|
5158
|
+
*/
|
|
5159
|
+
declare function resolvePopulateStep(definition: AnyCollection, previousSteps: readonly PopulateStep[], path: string, as: string, projection?: Record<string, 0 | 1 | boolean>): PopulateStep;
|
|
5160
|
+
/**
|
|
5161
|
+
* Execute populate steps against a set of documents.
|
|
5162
|
+
*
|
|
5163
|
+
* Processes steps in order. For each step:
|
|
5164
|
+
* 1. Navigate to the parent level for nested paths
|
|
5165
|
+
* 2. Collect unique IDs from the leaf field
|
|
5166
|
+
* 3. Batch query the target collection with `$in`
|
|
5167
|
+
* 4. Merge results back, applying rename if needed
|
|
5168
|
+
*
|
|
5169
|
+
* Mutates the documents in place and returns them.
|
|
5170
|
+
*
|
|
5171
|
+
* @param documents - The documents to populate (mutated in place).
|
|
5172
|
+
* @param steps - The populate steps to execute in order.
|
|
5173
|
+
* @param getCollection - Function to get a native MongoDB Collection by name.
|
|
5174
|
+
* @returns The populated documents.
|
|
5175
|
+
*
|
|
5176
|
+
* @example
|
|
5177
|
+
* ```ts
|
|
5178
|
+
* const docs = await executePopulate(rawDocs, steps, name => db.collection(name))
|
|
5179
|
+
* ```
|
|
5180
|
+
*/
|
|
5181
|
+
declare function executePopulate(documents: Document[], steps: readonly PopulateStep[], getCollection: (name: string) => Collection): Promise<Document[]>;
|
|
5182
|
+
|
|
3613
5183
|
/**
|
|
3614
5184
|
* Convenience namespace that groups all query operators under a single import.
|
|
3615
5185
|
*
|
|
@@ -3918,4 +5488,4 @@ type UpdateFilterOf<TDef extends AnyCollection> = TypedUpdateFilter<InferDocumen
|
|
|
3918
5488
|
*/
|
|
3919
5489
|
type SortOf<TDef extends AnyCollection> = TypedSort<InferDocument<TDef>>;
|
|
3920
5490
|
|
|
3921
|
-
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
|
|
5491
|
+
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 ApplyPopulate, type ApplyPopulateProjected, type ArrayElement, type CollectionDefinition, CollectionHandle, type CollectionName, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, type CurrentDateFields, type CursorPage, type CursorPaginateOptions, Database, type DeepPopulate, type DotPath, type DotPathValue, type DotSubPaths, type ExclusionProjection, type Expression, type ExpressionBuilder, type ExtractRefCollection, type FieldIndexDefinition, type FieldOrExpr, type FieldRef, type FieldRefType, type FilterOf, type FindOneAndDeleteOptions, type FindOneAndUpdateOptions, type FindOneOptions, type FindOneProjectionOptions, type FindOptions, type FindProjectionOptions, type GroupByCompoundResult, type GroupByNullResult, type GroupByResult, type HandleOf, type IncFields, type InclusionProjection, IndexBuilder, type IndexMetadata, type IndexNames, type IndexOptions, type IndexSpec, type InferAccumulator, type InferAccumulators, type InferAddedFields, type InferDocument, type InferExpression, type InferInsert, type NarrowFromFilter, type NestedRefPath, type OffsetPage, type OffsetPaginateOptions, type PopFields, PopulateCursor, type PopulateField, type PopulateFieldProjected, PopulateOneOrThrowQuery, PopulateOneQuery, type PopulateProjectionConfig, PopulateRefBuilder, type PopulateStep, type Populated, type Prettify, type ProjectionResult, type PullFields, type PushFields, type PushModifiers, type RefFields, type RefMarker, type RefMetadata, type RenameFields, type ReplaceDots, type ResolvedShape, type SetFields, type SortOf, type StaleIndex, type SyncIndexesOptions, type SyncIndexesResult, TransactionContext, type TransactionFn, type TypedFilter, TypedFindCursor, type TypedProjection, type TypedSort, type TypedUpdateFilter, type UnsetFields, type UnwindResult, type UnwrapRef, type UpdateFilterOf, type UpdateOptions, type ValidationMode, type ZodObjectId, ZodmonAuthError, ZodmonBulkWriteError, ZodmonDocValidationError, ZodmonDuplicateKeyError, ZodmonError, ZodmonIndexError, ZodmonNetworkError, ZodmonNotFoundError, ZodmonQueryError, ZodmonTimeoutError, ZodmonValidationError, ZodmonWriteConflictError, aggregate, checkUnindexedFields, collection, createAccumulatorBuilder, createClient, createExpressionBuilder, createPopulateCursor, deleteMany, deleteOne, deriveProjectedSchema, executePopulate, extractComparableOptions, extractDbName, extractFieldIndexes, find, findOne, findOneAndDelete, findOneAndUpdate, findOneOrThrow, generateIndexName, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, isInclusionProjection, isOid, objectId, oid, raw, resolvePopulateStep, serializeIndexKey, syncIndexes, toCompoundIndexSpec, toFieldIndexSpec, unwrapRefSchema, updateMany, updateOne, wrapMongoError };
|