mongoose 8.9.2 → 8.9.4

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.
@@ -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
@@ -55,6 +55,7 @@ const documentIsModified = require('./helpers/symbols').documentIsModified;
55
55
  const documentModifiedPaths = require('./helpers/symbols').documentModifiedPaths;
56
56
  const documentSchemaSymbol = require('./helpers/symbols').documentSchemaSymbol;
57
57
  const getSymbol = require('./helpers/symbols').getSymbol;
58
+ const modelSymbol = require('./helpers/symbols').modelSymbol;
58
59
  const populateModelSymbol = require('./helpers/symbols').populateModelSymbol;
59
60
  const scopeSymbol = require('./helpers/symbols').scopeSymbol;
60
61
  const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
@@ -1386,7 +1387,9 @@ Document.prototype.$set = function $set(path, val, type, options) {
1386
1387
  const model = val.constructor;
1387
1388
 
1388
1389
  // Check ref
1389
- const ref = schema.options.ref;
1390
+ const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref;
1391
+
1392
+ const ref = refOpt?.modelName || refOpt;
1390
1393
  if (ref != null && (ref === model.modelName || ref === model.baseModelName)) {
1391
1394
  return true;
1392
1395
  }
@@ -3703,8 +3706,10 @@ Document.prototype.$getAllSubdocs = function(options) {
3703
3706
  const subDocs = [];
3704
3707
  function getSubdocs(doc) {
3705
3708
  const newSubdocs = [];
3706
- for (const { path } of doc.$__schema.childSchemas) {
3707
- const val = doc.$__getValue(path);
3709
+
3710
+ for (const { model } of doc.$__schema.childSchemas) {
3711
+ // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose
3712
+ const val = doc.$__getValue(model.path);
3708
3713
  if (val == null) {
3709
3714
  continue;
3710
3715
  }
@@ -3726,6 +3731,7 @@ Document.prototype.$getAllSubdocs = function(options) {
3726
3731
  }
3727
3732
  }
3728
3733
  }
3734
+
3729
3735
  for (const subdoc of newSubdocs) {
3730
3736
  getSubdocs(subdoc);
3731
3737
  }
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 => {
@@ -3431,6 +3436,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3431
3436
  * @param {String|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
3432
3437
  * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout).
3433
3438
  * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
3439
+ * @param {Boolean} [options.validateBeforeSave=true] set to `false` to skip Mongoose validation on all documents
3434
3440
  * @return {BulkWriteResult} the return value from `bulkWrite()`
3435
3441
  */
3436
3442
  Model.bulkSave = async function bulkSave(documents, options) {
@@ -3450,15 +3456,13 @@ Model.bulkSave = async function bulkSave(documents, options) {
3450
3456
  }
3451
3457
  }
3452
3458
 
3453
- await Promise.all(documents.map(buildPreSavePromise));
3459
+ await Promise.all(documents.map(doc => buildPreSavePromise(doc, options)));
3454
3460
 
3455
3461
  const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true, timestamps: options.timestamps });
3456
-
3457
- const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, options).then(
3462
+ const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, { skipValidation: true, ...options }).then(
3458
3463
  (res) => ({ bulkWriteResult: res, bulkWriteError: null }),
3459
3464
  (err) => ({ bulkWriteResult: null, bulkWriteError: err })
3460
3465
  );
3461
-
3462
3466
  // If not a MongoBulkWriteError, treat this as all documents failed to save.
3463
3467
  if (bulkWriteError != null && !(bulkWriteError instanceof MongoBulkWriteError)) {
3464
3468
  throw bulkWriteError;
@@ -3486,7 +3490,6 @@ Model.bulkSave = async function bulkSave(documents, options) {
3486
3490
  successfulDocuments.push(document);
3487
3491
  }
3488
3492
  }
3489
-
3490
3493
  await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document)));
3491
3494
 
3492
3495
  if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) {
@@ -3496,9 +3499,9 @@ Model.bulkSave = async function bulkSave(documents, options) {
3496
3499
  return bulkWriteResult;
3497
3500
  };
3498
3501
 
3499
- function buildPreSavePromise(document) {
3502
+ function buildPreSavePromise(document, options) {
3500
3503
  return new Promise((resolve, reject) => {
3501
- document.schema.s.hooks.execPre('save', document, (err) => {
3504
+ document.schema.s.hooks.execPre('save', document, [options], (err) => {
3502
3505
  if (err) {
3503
3506
  reject(err);
3504
3507
  return;
@@ -3687,7 +3690,21 @@ Model.castObject = function castObject(obj, options) {
3687
3690
  }
3688
3691
 
3689
3692
  if (schemaType.$isMongooseDocumentArray) {
3690
- continue;
3693
+ const castNonArraysOption = schemaType.options?.castNonArrays ?? schemaType.constructor.options.castNonArrays;
3694
+ if (!Array.isArray(val)) {
3695
+ if (!castNonArraysOption) {
3696
+ if (!options.ignoreCastErrors) {
3697
+ error = error || new ValidationError();
3698
+ error.addError(path, new ObjectExpectedError(path, val));
3699
+ }
3700
+ } else {
3701
+ cur[pieces[pieces.length - 1]] = [
3702
+ Model.castObject.call(schemaType.caster, val)
3703
+ ];
3704
+ }
3705
+
3706
+ continue;
3707
+ }
3691
3708
  }
3692
3709
  if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
3693
3710
  try {
@@ -522,6 +522,7 @@ SchemaDocumentArray.prototype.clone = function() {
522
522
  }
523
523
  schematype.Constructor.discriminators = Object.assign({},
524
524
  this.Constructor.discriminators);
525
+ schematype._appliedDiscriminators = this._appliedDiscriminators;
525
526
  return schematype;
526
527
  };
527
528
 
@@ -393,5 +393,6 @@ SchemaSubdocument.prototype.clone = function() {
393
393
  schematype.requiredValidator = this.requiredValidator;
394
394
  }
395
395
  schematype.caster.discriminators = Object.assign({}, this.caster.discriminators);
396
+ schematype._appliedDiscriminators = this._appliedDiscriminators;
396
397
  return schematype;
397
398
  };
package/lib/schema.js CHANGED
@@ -2148,7 +2148,7 @@ Schema.prototype.index = function(fields, options) {
2148
2148
 
2149
2149
  for (const existingIndex of this.indexes()) {
2150
2150
  if (options.name == null && existingIndex[1].name == null && isIndexSpecEqual(existingIndex[0], fields)) {
2151
- throw new MongooseError(`Schema already has an index on ${JSON.stringify(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/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.2",
4
+ "version": "8.9.4",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -30,34 +30,32 @@
30
30
  "devDependencies": {
31
31
  "@babel/core": "7.26.0",
32
32
  "@babel/preset-env": "7.26.0",
33
- "@typescript-eslint/eslint-plugin": "^8.18.0",
34
- "@typescript-eslint/parser": "^8.18.0",
33
+ "@typescript-eslint/eslint-plugin": "^8.19.1",
34
+ "@typescript-eslint/parser": "^8.19.1",
35
35
  "acquit": "1.3.0",
36
36
  "acquit-ignore": "0.2.1",
37
37
  "acquit-require": "0.1.1",
38
38
  "assert-browserify": "2.0.0",
39
- "axios": "1.1.3",
40
39
  "babel-loader": "8.2.5",
41
40
  "broken-link-checker": "^0.7.8",
42
41
  "buffer": "^5.6.0",
43
42
  "cheerio": "1.0.0",
44
43
  "crypto-browserify": "3.12.1",
45
- "dotenv": "16.4.5",
46
44
  "dox": "1.0.0",
47
- "eslint": "8.57.0",
48
- "eslint-plugin-markdown": "^5.0.0",
45
+ "eslint": "8.57.1",
46
+ "eslint-plugin-markdown": "^5.1.0",
49
47
  "eslint-plugin-mocha-no-only": "1.2.0",
50
48
  "express": "^4.19.2",
51
49
  "fs-extra": "~11.2.0",
52
- "highlight.js": "11.10.0",
50
+ "highlight.js": "11.11.1",
53
51
  "lodash.isequal": "4.5.0",
54
52
  "lodash.isequalwith": "4.4.0",
55
- "markdownlint-cli2": "^0.15.0",
56
- "marked": "15.0.3",
53
+ "markdownlint-cli2": "^0.17.1",
54
+ "marked": "15.0.4",
57
55
  "mkdirp": "^3.0.1",
58
- "mocha": "10.8.2",
56
+ "mocha": "11.0.1",
59
57
  "moment": "2.30.1",
60
- "mongodb-memory-server": "10.1.2",
58
+ "mongodb-memory-server": "10.1.3",
61
59
  "ncp": "^2.0.0",
62
60
  "nyc": "15.1.0",
63
61
  "pug": "3.0.3",
@@ -67,7 +65,7 @@
67
65
  "tsd": "0.31.2",
68
66
  "typescript": "5.7.2",
69
67
  "uuid": "11.0.3",
70
- "webpack": "5.96.1"
68
+ "webpack": "5.97.1"
71
69
  },
72
70
  "directories": {
73
71
  "lib": "./lib/mongoose"
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[];
@@ -710,86 +710,102 @@ declare module 'mongoose' {
710
710
  [K in keyof T]: FlattenProperty<T[K]>;
711
711
  };
712
712
 
713
+ export type BufferToBinaryProperty<T> = T extends Buffer
714
+ ? mongodb.Binary
715
+ : T extends Types.DocumentArray<infer ItemType>
716
+ ? Types.DocumentArray<BufferToBinary<ItemType>>
717
+ : T extends Types.Subdocument<unknown, unknown, infer SubdocType>
718
+ ? HydratedSingleSubdocument<BufferToBinary<SubdocType>>
719
+ : BufferToBinary<T>;
720
+
713
721
  /**
714
722
  * Converts any Buffer properties into mongodb.Binary instances, which is what `lean()` returns
715
723
  */
716
724
  export type BufferToBinary<T> = T extends Buffer
717
725
  ? mongodb.Binary
718
- : T extends TreatAsPrimitives
726
+ : T extends Document
719
727
  ? T
720
- : T extends Record<string, any> ? {
721
- [K in keyof T]: T[K] extends Buffer
722
- ? mongodb.Binary
723
- : T[K] extends Types.DocumentArray<infer ItemType>
724
- ? Types.DocumentArray<BufferToBinary<ItemType>>
725
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
726
- ? HydratedSingleSubdocument<SubdocType>
727
- : BufferToBinary<T[K]>;
728
- } : T;
728
+ : T extends TreatAsPrimitives
729
+ ? T
730
+ : T extends Record<string, any>
731
+ ? {
732
+ [K in keyof T]: BufferToBinaryProperty<T[K]>
733
+ }
734
+ : T;
729
735
 
730
736
  /**
731
737
  * Converts any Buffer properties into { type: 'buffer', data: [1, 2, 3] } format for JSON serialization
732
738
  */
733
739
  export type BufferToJSON<T> = T extends Buffer
734
740
  ? { type: 'buffer', data: number[] }
735
- : T extends TreatAsPrimitives
741
+ : T extends Document
736
742
  ? T
737
- : T extends Record<string, any> ? {
738
- [K in keyof T]: T[K] extends Buffer
739
- ? { type: 'buffer', data: number[] }
740
- : T[K] extends Types.DocumentArray<infer ItemType>
741
- ? Types.DocumentArray<BufferToBinary<ItemType>>
742
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
743
- ? HydratedSingleSubdocument<SubdocType>
744
- : BufferToBinary<T[K]>;
745
- } : T;
743
+ : T extends TreatAsPrimitives
744
+ ? T
745
+ : T extends Record<string, any> ? {
746
+ [K in keyof T]: T[K] extends Buffer
747
+ ? { type: 'buffer', data: number[] }
748
+ : T[K] extends Types.DocumentArray<infer ItemType>
749
+ ? Types.DocumentArray<BufferToBinary<ItemType>>
750
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
751
+ ? HydratedSingleSubdocument<SubdocType>
752
+ : BufferToBinary<T[K]>;
753
+ } : T;
746
754
 
747
755
  /**
748
756
  * Converts any ObjectId properties into strings for JSON serialization
749
757
  */
750
758
  export type ObjectIdToString<T> = T extends mongodb.ObjectId
751
759
  ? string
752
- : T extends TreatAsPrimitives
760
+ : T extends Document
753
761
  ? T
754
- : T extends Record<string, any> ? {
755
- [K in keyof T]: T[K] extends mongodb.ObjectId
756
- ? string
757
- : T[K] extends Types.DocumentArray<infer ItemType>
758
- ? Types.DocumentArray<ObjectIdToString<ItemType>>
759
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
760
- ? HydratedSingleSubdocument<ObjectIdToString<SubdocType>>
761
- : ObjectIdToString<T[K]>;
762
- } : T;
762
+ : T extends TreatAsPrimitives
763
+ ? T
764
+ : T extends Record<string, any> ? {
765
+ [K in keyof T]: T[K] extends mongodb.ObjectId
766
+ ? string
767
+ : T[K] extends Types.DocumentArray<infer ItemType>
768
+ ? Types.DocumentArray<ObjectIdToString<ItemType>>
769
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
770
+ ? HydratedSingleSubdocument<ObjectIdToString<SubdocType>>
771
+ : ObjectIdToString<T[K]>;
772
+ } : T;
763
773
 
764
774
  /**
765
775
  * Converts any Date properties into strings for JSON serialization
766
776
  */
767
777
  export type DateToString<T> = T extends NativeDate
768
778
  ? string
769
- : T extends TreatAsPrimitives
779
+ : T extends Document
770
780
  ? T
771
- : T extends Record<string, any> ? {
772
- [K in keyof T]: T[K] extends NativeDate
773
- ? string
774
- : T[K] extends (NativeDate | null | undefined)
775
- ? string | null | undefined
776
- : T[K] extends Types.DocumentArray<infer ItemType>
777
- ? Types.DocumentArray<DateToString<ItemType>>
778
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
779
- ? HydratedSingleSubdocument<DateToString<SubdocType>>
780
- : DateToString<T[K]>;
781
- } : T;
781
+ : T extends TreatAsPrimitives
782
+ ? T
783
+ : T extends Record<string, any> ? {
784
+ [K in keyof T]: T[K] extends NativeDate
785
+ ? string
786
+ : T[K] extends (NativeDate | null | undefined)
787
+ ? string | null | undefined
788
+ : T[K] extends Types.DocumentArray<infer ItemType>
789
+ ? Types.DocumentArray<DateToString<ItemType>>
790
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
791
+ ? HydratedSingleSubdocument<DateToString<SubdocType>>
792
+ : DateToString<T[K]>;
793
+ } : T;
782
794
 
783
795
  /**
784
796
  * Converts any Mongoose subdocuments (single nested or doc arrays) into POJO equivalents
785
797
  */
786
- export type SubdocsToPOJOs<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
787
- [K in keyof T]: T[K] extends Types.DocumentArray<infer ItemType>
788
- ? ItemType[]
789
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
790
- ? SubdocType
791
- : SubdocsToPOJOs<T[K]>;
792
- } : T;
798
+ export type SubdocsToPOJOs<T> = T extends Document
799
+ ? T
800
+ : T extends TreatAsPrimitives
801
+ ? T
802
+ : T extends Record<string, any> ? {
803
+ [K in keyof T]: T[K] extends Types.DocumentArray<infer ItemType>
804
+ ? ItemType[]
805
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
806
+ ? SubdocType
807
+ : SubdocsToPOJOs<T[K]>;
808
+ } : T;
793
809
 
794
810
  export type JSONSerialized<T> = SubdocsToPOJOs<
795
811
  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.