mongoose 8.9.1 → 8.9.3

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/lib/aggregate.js CHANGED
@@ -87,6 +87,24 @@ function Aggregate(pipeline, model) {
87
87
 
88
88
  Aggregate.prototype.options;
89
89
 
90
+ /**
91
+ * Returns default options for this aggregate.
92
+ *
93
+ * @param {Model} model
94
+ * @api private
95
+ */
96
+
97
+ Aggregate.prototype._optionsForExec = function() {
98
+ const options = this.options || {};
99
+
100
+ const asyncLocalStorage = this.model()?.db?.base.transactionAsyncLocalStorage?.getStore();
101
+ if (!options.hasOwnProperty('session') && asyncLocalStorage?.session != null) {
102
+ options.session = asyncLocalStorage.session;
103
+ }
104
+
105
+ return options;
106
+ };
107
+
90
108
  /**
91
109
  * Get/set the model that this aggregation will execute on.
92
110
  *
@@ -914,6 +932,7 @@ Aggregate.prototype.option = function(value) {
914
932
  */
915
933
 
916
934
  Aggregate.prototype.cursor = function(options) {
935
+ this._optionsForExec();
917
936
  this.options.cursor = options || {};
918
937
  return new AggregationCursor(this); // return this;
919
938
  };
@@ -1022,10 +1041,7 @@ Aggregate.prototype.exec = async function exec() {
1022
1041
  applyGlobalMaxTimeMS(this.options, model.db.options, model.base.options);
1023
1042
  applyGlobalDiskUse(this.options, model.db.options, model.base.options);
1024
1043
 
1025
- const asyncLocalStorage = this.model()?.db?.base.transactionAsyncLocalStorage?.getStore();
1026
- if (!this.options.hasOwnProperty('session') && asyncLocalStorage?.session != null) {
1027
- this.options.session = asyncLocalStorage.session;
1028
- }
1044
+ this._optionsForExec();
1029
1045
 
1030
1046
  if (this.options && this.options.cursor) {
1031
1047
  return new AggregationCursor(this);
@@ -1052,6 +1068,7 @@ Aggregate.prototype.exec = async function exec() {
1052
1068
  }
1053
1069
 
1054
1070
  const options = clone(this.options || {});
1071
+
1055
1072
  let result;
1056
1073
  try {
1057
1074
  const cursor = await collection.aggregate(this._pipeline, options);
package/lib/connection.js CHANGED
@@ -663,7 +663,7 @@ Connection.prototype.withSession = async function withSession(executor) {
663
663
  *
664
664
  * const session = await conn.startSession();
665
665
  * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session });
666
- * await doc.remove();
666
+ * await doc.deleteOne();
667
667
  * // `doc` will always be null, even if reading from a replica set
668
668
  * // secondary. Without causal consistency, it is possible to
669
669
  * // get a doc back from the below query if the query reads from a
@@ -557,17 +557,11 @@ function _onNext(error, doc) {
557
557
 
558
558
  if (this.ctx._batchDocs.length < this.ctx.options._populateBatchSize) {
559
559
  // If both `batchSize` and `_populateBatchSize` are huge, calling `next()` repeatedly may
560
- // cause a stack overflow. So make sure we clear the stack regularly.
561
- if (this.ctx._batchDocs.length > 0 && this.ctx._batchDocs.length % 1000 === 0) {
562
- return immediate(() => this.ctx.cursor.next().then(
563
- res => { _onNext.call(this, null, res); },
564
- err => { _onNext.call(this, err); }
565
- ));
566
- }
567
- this.ctx.cursor.next().then(
560
+ // cause a stack overflow. So make sure we clear the stack.
561
+ immediate(() => this.ctx.cursor.next().then(
568
562
  res => { _onNext.call(this, null, res); },
569
563
  err => { _onNext.call(this, err); }
570
- );
564
+ ));
571
565
  } else {
572
566
  _populateBatch.call(this);
573
567
  }
package/lib/document.js CHANGED
@@ -2357,17 +2357,17 @@ Document.prototype.$isDefault = function(path) {
2357
2357
  };
2358
2358
 
2359
2359
  /**
2360
- * Getter/setter, determines whether the document was removed or not.
2360
+ * Getter/setter, determines whether the document was deleted. The `Model.prototype.deleteOne()` method sets `$isDeleted` if the delete operation succeeded.
2361
2361
  *
2362
2362
  * #### Example:
2363
2363
  *
2364
- * const product = await product.remove();
2364
+ * const product = await product.deleteOne();
2365
2365
  * product.$isDeleted(); // true
2366
- * product.remove(); // no-op, doesn't send anything to the db
2366
+ * product.deleteOne(); // no-op, doesn't send anything to the db
2367
2367
  *
2368
2368
  * product.$isDeleted(false);
2369
2369
  * product.$isDeleted(); // false
2370
- * product.remove(); // will execute a remove against the db
2370
+ * product.deleteOne(); // will execute a remove against the db
2371
2371
  *
2372
2372
  *
2373
2373
  * @param {Boolean} [val] optional, overrides whether mongoose thinks the doc is deleted
@@ -3703,8 +3703,10 @@ Document.prototype.$getAllSubdocs = function(options) {
3703
3703
  const subDocs = [];
3704
3704
  function getSubdocs(doc) {
3705
3705
  const newSubdocs = [];
3706
- for (const { path } of doc.$__schema.childSchemas) {
3707
- const val = doc.$__getValue(path);
3706
+
3707
+ for (const { model } of doc.$__schema.childSchemas) {
3708
+ // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose
3709
+ const val = doc.$__getValue(model.path);
3708
3710
  if (val == null) {
3709
3711
  continue;
3710
3712
  }
@@ -3726,6 +3728,7 @@ Document.prototype.$getAllSubdocs = function(options) {
3726
3728
  }
3727
3729
  }
3728
3730
  }
3731
+
3729
3732
  for (const subdoc of newSubdocs) {
3730
3733
  getSubdocs(subdoc);
3731
3734
  }
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Compares two index specifications to determine if they are equal.
5
+ *
6
+ * #### Example:
7
+ * isIndexSpecEqual({ a: 1, b: 1 }, { a: 1, b: 1 }); // true
8
+ * isIndexSpecEqual({ a: 1, b: 1 }, { b: 1, a: 1 }); // false
9
+ * isIndexSpecEqual({ a: 1, b: -1 }, { a: 1, b: 1 }); // false
10
+ *
11
+ * @param {Object} spec1 The first index specification to compare.
12
+ * @param {Object} spec2 The second index specification to compare.
13
+ * @returns {Boolean} Returns true if the index specifications are equal, otherwise returns false.
14
+ */
15
+
16
+ module.exports = function isIndexSpecEqual(spec1, spec2) {
17
+ const spec1Keys = Object.keys(spec1);
18
+ const spec2Keys = Object.keys(spec2);
19
+
20
+ if (spec1Keys.length !== spec2Keys.length) {
21
+ return false;
22
+ }
23
+
24
+ for (let i = 0; i < spec1Keys.length; i++) {
25
+ const key = spec1Keys[i];
26
+ if (key !== spec2Keys[i] || spec1[key] !== spec2[key]) {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ return true;
32
+ };
package/lib/model.js CHANGED
@@ -69,6 +69,7 @@ const util = require('util');
69
69
  const utils = require('./utils');
70
70
  const minimize = require('./helpers/minimize');
71
71
  const MongooseBulkSaveIncompleteError = require('./error/bulkSaveIncompleteError');
72
+ const ObjectExpectedError = require('./error/objectExpected');
72
73
 
73
74
  const modelCollectionSymbol = Symbol('mongoose#Model#collection');
74
75
  const modelDbSymbol = Symbol('mongoose#Model#db');
@@ -387,7 +388,11 @@ Model.prototype.$__handleSave = function(options, callback) {
387
388
 
388
389
  this[modelCollectionSymbol].updateOne(where, update, saveOptions).then(
389
390
  ret => {
390
- ret.$where = where;
391
+ if (ret == null) {
392
+ ret = { $where: where };
393
+ } else {
394
+ ret.$where = where;
395
+ }
391
396
  callback(null, ret);
392
397
  },
393
398
  err => {
@@ -694,13 +699,20 @@ Model.prototype.$__where = function _where(where) {
694
699
  };
695
700
 
696
701
  /**
697
- * Delete this document from the db.
702
+ * Delete this document from the db. Returns a Query instance containing a `deleteOne` operation by this document's `_id`.
698
703
  *
699
704
  * #### Example:
700
705
  *
701
706
  * await product.deleteOne();
702
707
  * await Product.findById(product._id); // null
703
708
  *
709
+ * Since `deleteOne()` returns a Query, the `deleteOne()` will **not** execute unless you use either `await`, `.then()`, `.catch()`, or [`.exec()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.exec())
710
+ *
711
+ * #### Example:
712
+ *
713
+ * product.deleteOne(); // Doesn't do anything
714
+ * product.deleteOne().exec(); // Deletes the document, returns a promise
715
+ *
704
716
  * @return {Query} Query
705
717
  * @api public
706
718
  */
@@ -1879,8 +1891,6 @@ Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
1879
1891
  /**
1880
1892
  * Deletes the first document that matches `conditions` from the collection.
1881
1893
  * It returns an object with the property `deletedCount` indicating how many documents were deleted.
1882
- * Behaves like `remove()`, but deletes at most one document regardless of the
1883
- * `single` option.
1884
1894
  *
1885
1895
  * #### Example:
1886
1896
  *
@@ -1914,8 +1924,6 @@ Model.deleteOne = function deleteOne(conditions, options) {
1914
1924
  /**
1915
1925
  * Deletes all of the documents that match `conditions` from the collection.
1916
1926
  * It returns an object with the property `deletedCount` containing the number of documents deleted.
1917
- * Behaves like `remove()`, but deletes all documents that match `conditions`
1918
- * regardless of the `single` option.
1919
1927
  *
1920
1928
  * #### Example:
1921
1929
  *
@@ -2729,7 +2737,7 @@ Model.create = async function create(doc, options) {
2729
2737
  * // operationType: 'delete',
2730
2738
  * // ns: { db: 'mydb', coll: 'Person' },
2731
2739
  * // documentKey: { _id: 5a51b125c5500f5aa094c7bd } }
2732
- * await doc.remove();
2740
+ * await doc.deleteOne();
2733
2741
  *
2734
2742
  * @param {Array} [pipeline]
2735
2743
  * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#watch)
@@ -2777,7 +2785,7 @@ Model.watch = function(pipeline, options) {
2777
2785
  *
2778
2786
  * const session = await Person.startSession();
2779
2787
  * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session });
2780
- * await doc.remove();
2788
+ * await doc.deleteOne();
2781
2789
  * // `doc` will always be null, even if reading from a replica set
2782
2790
  * // secondary. Without causal consistency, it is possible to
2783
2791
  * // get a doc back from the below query if the query reads from a
@@ -3684,6 +3692,19 @@ Model.castObject = function castObject(obj, options) {
3684
3692
  }
3685
3693
 
3686
3694
  if (schemaType.$isMongooseDocumentArray) {
3695
+ const castNonArraysOption = schemaType.options?.castNonArrays ?? schemaType.constructor.options.castNonArrays;
3696
+ if (!Array.isArray(val)) {
3697
+ if (!castNonArraysOption) {
3698
+ if (!options.ignoreCastErrors) {
3699
+ error = error || new ValidationError();
3700
+ error.addError(path, new ObjectExpectedError(path, val));
3701
+ }
3702
+ } else {
3703
+ cur[pieces[pieces.length - 1]] = [
3704
+ Model.castObject.call(schemaType.caster, val)
3705
+ ];
3706
+ }
3707
+ }
3687
3708
  continue;
3688
3709
  }
3689
3710
  if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
package/lib/query.js CHANGED
@@ -2247,10 +2247,7 @@ Query.prototype.error = function error(err) {
2247
2247
  */
2248
2248
 
2249
2249
  Query.prototype._unsetCastError = function _unsetCastError() {
2250
- if (this._error == null) {
2251
- return;
2252
- }
2253
- if (this._error != null && !(this._error instanceof CastError)) {
2250
+ if (this._error == null || !(this._error instanceof CastError)) {
2254
2251
  return;
2255
2252
  }
2256
2253
  return this.error(null);
package/lib/schema.js CHANGED
@@ -18,13 +18,13 @@ const getConstructorName = require('./helpers/getConstructorName');
18
18
  const getIndexes = require('./helpers/schema/getIndexes');
19
19
  const handleReadPreferenceAliases = require('./helpers/query/handleReadPreferenceAliases');
20
20
  const idGetter = require('./helpers/schema/idGetter');
21
+ const isIndexSpecEqual = require('./helpers/indexes/isIndexSpecEqual');
21
22
  const merge = require('./helpers/schema/merge');
22
23
  const mpath = require('mpath');
23
24
  const setPopulatedVirtualValue = require('./helpers/populate/setPopulatedVirtualValue');
24
25
  const setupTimestamps = require('./helpers/timestamps/setupTimestamps');
25
26
  const utils = require('./utils');
26
27
  const validateRef = require('./helpers/populate/validateRef');
27
- const util = require('util');
28
28
 
29
29
  const hasNumericSubpathRegex = /\.\d+(\.|$)/;
30
30
 
@@ -899,7 +899,7 @@ Schema.prototype.removeIndex = function removeIndex(index) {
899
899
 
900
900
  if (typeof index === 'object') {
901
901
  for (let i = this._indexes.length - 1; i >= 0; --i) {
902
- if (util.isDeepStrictEqual(this._indexes[i][0], index)) {
902
+ if (isIndexSpecEqual(this._indexes[i][0], index)) {
903
903
  this._indexes.splice(i, 1);
904
904
  }
905
905
  }
@@ -2147,8 +2147,8 @@ Schema.prototype.index = function(fields, options) {
2147
2147
  }
2148
2148
 
2149
2149
  for (const existingIndex of this.indexes()) {
2150
- if (util.isDeepStrictEqual(existingIndex[0], fields)) {
2151
- throw new MongooseError(`Schema already has an index on ${JSON.stringify(fields)}`);
2150
+ if (options.name == null && existingIndex[1].name == null && isIndexSpecEqual(existingIndex[0], fields)) {
2151
+ utils.warn(`Duplicate schema index on ${JSON.stringify(fields)} found. This is often due to declaring an index using both "index: true" and "schema.index()". Please remove the duplicate index definition.`);
2152
2152
  }
2153
2153
  }
2154
2154
 
package/lib/types/map.js CHANGED
@@ -9,6 +9,7 @@ const handleSpreadDoc = require('../helpers/document/handleSpreadDoc');
9
9
  const util = require('util');
10
10
  const specialProperties = require('../helpers/specialProperties');
11
11
  const isBsonType = require('../helpers/isBsonType');
12
+ const cleanModifiedSubpaths = require('../helpers/document/cleanModifiedSubpaths');
12
13
 
13
14
  const populateModelSymbol = require('../helpers/symbols').populateModelSymbol;
14
15
 
@@ -157,7 +158,13 @@ class MongooseMap extends Map {
157
158
  super.set(key, value);
158
159
 
159
160
  if (parent != null && parent.$__ != null && !deepEqual(value, priorVal)) {
160
- parent.markModified(fullPath.call(this));
161
+ const path = fullPath.call(this);
162
+ parent.markModified(path);
163
+ // If overwriting the full document array or subdoc, make sure to clean up any paths that were modified
164
+ // before re: #15108
165
+ if (this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested) {
166
+ cleanModifiedSubpaths(parent, path);
167
+ }
161
168
  }
162
169
 
163
170
  // Delay calculating full path unless absolutely necessary, because string
package/lib/utils.js CHANGED
@@ -631,6 +631,9 @@ exports.getValue = function(path, obj, map) {
631
631
  const mapGetterOptions = Object.freeze({ getters: false });
632
632
 
633
633
  function getValueLookup(obj, part) {
634
+ if (part === '$*' && obj instanceof Map) {
635
+ return obj;
636
+ }
634
637
  let _from = obj?._doc || obj;
635
638
  if (_from != null && _from.isMongooseArrayProxy) {
636
639
  _from = _from.__array;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.9.1",
4
+ "version": "8.9.3",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
package/types/index.d.ts CHANGED
@@ -491,7 +491,7 @@ declare module 'mongoose' {
491
491
  remove(paths: string | Array<string>): this;
492
492
 
493
493
  /** Removes index by name or index spec */
494
- remove(index: string | AnyObject): this;
494
+ removeIndex(index: string | AnyObject): this;
495
495
 
496
496
  /** Returns an Array of path strings that are required by this schema. */
497
497
  requiredPaths(invalidate?: boolean): string[];
@@ -572,7 +572,8 @@ declare module 'mongoose' {
572
572
  | typeof Schema.Types.Number
573
573
  | typeof Schema.Types.String
574
574
  | typeof Schema.Types.Buffer
575
- | typeof Schema.Types.ObjectId;
575
+ | typeof Schema.Types.ObjectId
576
+ | typeof Schema.Types.UUID;
576
577
 
577
578
 
578
579
  export type InferId<T> = T extends { _id?: any } ? T['_id'] : Types.ObjectId;
@@ -712,77 +713,95 @@ declare module 'mongoose' {
712
713
  /**
713
714
  * Converts any Buffer properties into mongodb.Binary instances, which is what `lean()` returns
714
715
  */
715
- export type BufferToBinary<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
716
- [K in keyof T]: T[K] extends Buffer
717
- ? mongodb.Binary
718
- : T[K] extends (Buffer | null | undefined)
719
- ? mongodb.Binary | null | undefined
720
- : T[K] extends Types.DocumentArray<infer ItemType>
721
- ? Types.DocumentArray<BufferToBinary<ItemType>>
722
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
723
- ? HydratedSingleSubdocument<SubdocType>
724
- : BufferToBinary<T[K]>;
725
- } : T;
716
+ export type BufferToBinary<T> = T extends Buffer
717
+ ? mongodb.Binary
718
+ : T extends Document
719
+ ? T
720
+ : T extends TreatAsPrimitives
721
+ ? T
722
+ : T extends Record<string, any> ? {
723
+ [K in keyof T]: T[K] extends Buffer
724
+ ? mongodb.Binary
725
+ : T[K] extends Types.DocumentArray<infer ItemType>
726
+ ? Types.DocumentArray<BufferToBinary<ItemType>>
727
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
728
+ ? HydratedSingleSubdocument<BufferToBinary<SubdocType>>
729
+ : BufferToBinary<T[K]>;
730
+ } : T;
726
731
 
727
732
  /**
728
733
  * Converts any Buffer properties into { type: 'buffer', data: [1, 2, 3] } format for JSON serialization
729
734
  */
730
- export type BufferToJSON<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
731
- [K in keyof T]: T[K] extends Buffer
732
- ? { type: 'buffer', data: number[] }
733
- : T[K] extends (Buffer | null | undefined)
734
- ? { type: 'buffer', data: number[] } | null | undefined
735
- : T[K] extends Types.DocumentArray<infer ItemType>
736
- ? Types.DocumentArray<BufferToBinary<ItemType>>
737
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
738
- ? HydratedSingleSubdocument<SubdocType>
739
- : BufferToBinary<T[K]>;
740
- } : T;
735
+ export type BufferToJSON<T> = T extends Buffer
736
+ ? { type: 'buffer', data: number[] }
737
+ : T extends Document
738
+ ? T
739
+ : T extends TreatAsPrimitives
740
+ ? T
741
+ : T extends Record<string, any> ? {
742
+ [K in keyof T]: T[K] extends Buffer
743
+ ? { type: 'buffer', data: number[] }
744
+ : T[K] extends Types.DocumentArray<infer ItemType>
745
+ ? Types.DocumentArray<BufferToBinary<ItemType>>
746
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
747
+ ? HydratedSingleSubdocument<SubdocType>
748
+ : BufferToBinary<T[K]>;
749
+ } : T;
741
750
 
742
751
  /**
743
752
  * Converts any ObjectId properties into strings for JSON serialization
744
753
  */
745
- export type ObjectIdToString<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
746
- [K in keyof T]: T[K] extends mongodb.ObjectId
747
- ? string
748
- : T[K] extends (mongodb.ObjectId | null | undefined)
749
- ? string | null | undefined
750
- : T[K] extends Types.DocumentArray<infer ItemType>
751
- ? Types.DocumentArray<ObjectIdToString<ItemType>>
752
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
753
- ? HydratedSingleSubdocument<ObjectIdToString<SubdocType>>
754
- : ObjectIdToString<T[K]>;
755
- } : T;
754
+ export type ObjectIdToString<T> = T extends mongodb.ObjectId
755
+ ? string
756
+ : T extends Document
757
+ ? T
758
+ : T extends TreatAsPrimitives
759
+ ? T
760
+ : T extends Record<string, any> ? {
761
+ [K in keyof T]: T[K] extends mongodb.ObjectId
762
+ ? string
763
+ : T[K] extends Types.DocumentArray<infer ItemType>
764
+ ? Types.DocumentArray<ObjectIdToString<ItemType>>
765
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
766
+ ? HydratedSingleSubdocument<ObjectIdToString<SubdocType>>
767
+ : ObjectIdToString<T[K]>;
768
+ } : T;
756
769
 
757
770
  /**
758
771
  * Converts any Date properties into strings for JSON serialization
759
772
  */
760
- export type DateToString<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
761
- [K in keyof T]: T[K] extends NativeDate
762
- ? string
763
- : T[K] extends (NativeDate | null | undefined)
764
- ? string | null | undefined
765
- : T[K] extends Types.DocumentArray<infer ItemType>
766
- ? Types.DocumentArray<DateToString<ItemType>>
767
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
768
- ? HydratedSingleSubdocument<DateToString<SubdocType>>
769
- : DateToString<T[K]>;
770
- } : T;
773
+ export type DateToString<T> = T extends NativeDate
774
+ ? string
775
+ : T extends Document
776
+ ? T
777
+ : T extends TreatAsPrimitives
778
+ ? T
779
+ : T extends Record<string, any> ? {
780
+ [K in keyof T]: T[K] extends NativeDate
781
+ ? string
782
+ : T[K] extends (NativeDate | null | undefined)
783
+ ? string | null | undefined
784
+ : T[K] extends Types.DocumentArray<infer ItemType>
785
+ ? Types.DocumentArray<DateToString<ItemType>>
786
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
787
+ ? HydratedSingleSubdocument<DateToString<SubdocType>>
788
+ : DateToString<T[K]>;
789
+ } : T;
771
790
 
772
791
  /**
773
792
  * Converts any Mongoose subdocuments (single nested or doc arrays) into POJO equivalents
774
793
  */
775
- export type SubdocsToPOJOs<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
776
- [K in keyof T]: T[K] extends NativeDate
777
- ? string
778
- : T[K] extends (NativeDate | null | undefined)
779
- ? string | null | undefined
780
- : T[K] extends Types.DocumentArray<infer ItemType>
781
- ? ItemType[]
782
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
783
- ? SubdocType
784
- : SubdocsToPOJOs<T[K]>;
785
- } : T;
794
+ export type SubdocsToPOJOs<T> = T extends Document
795
+ ? T
796
+ : T extends TreatAsPrimitives
797
+ ? T
798
+ : T extends Record<string, any> ? {
799
+ [K in keyof T]: T[K] extends Types.DocumentArray<infer ItemType>
800
+ ? ItemType[]
801
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
802
+ ? SubdocType
803
+ : SubdocsToPOJOs<T[K]>;
804
+ } : T;
786
805
 
787
806
  export type JSONSerialized<T> = SubdocsToPOJOs<
788
807
  FlattenMaps<
@@ -10,6 +10,9 @@ declare module 'mongoose' {
10
10
  function syncIndexes(options?: SyncIndexesOptions): Promise<ConnectionSyncIndexesResult>;
11
11
 
12
12
  interface IndexManager {
13
+ /* Deletes all indexes that aren't defined in this model's schema. Used by `syncIndexes()`. Returns list of dropped index names. */
14
+ cleanIndexes(options?: { toDrop?: string[], hideIndexes?: boolean }): Promise<string[]>;
15
+
13
16
  /**
14
17
  * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#createIndex)
15
18
  * function.