mongoose 8.2.0 → 8.2.2

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/connection.js CHANGED
@@ -829,6 +829,30 @@ Connection.prototype.openUri = async function openUri(uri, options) {
829
829
  return this;
830
830
  };
831
831
 
832
+ /*!
833
+ * Treat `on('error')` handlers as handling the initialConnection promise
834
+ * to avoid uncaught exceptions when using `on('error')`. See gh-14377.
835
+ */
836
+
837
+ Connection.prototype.on = function on(event, callback) {
838
+ if (event === 'error' && this.$initialConnection) {
839
+ this.$initialConnection.catch(() => {});
840
+ }
841
+ return EventEmitter.prototype.on.call(this, event, callback);
842
+ };
843
+
844
+ /*!
845
+ * Treat `once('error')` handlers as handling the initialConnection promise
846
+ * to avoid uncaught exceptions when using `on('error')`. See gh-14377.
847
+ */
848
+
849
+ Connection.prototype.once = function on(event, callback) {
850
+ if (event === 'error' && this.$initialConnection) {
851
+ this.$initialConnection.catch(() => {});
852
+ }
853
+ return EventEmitter.prototype.once.call(this, event, callback);
854
+ };
855
+
832
856
  /*!
833
857
  * ignore
834
858
  */
package/lib/document.js CHANGED
@@ -742,7 +742,7 @@ function init(self, obj, doc, opts, prefix) {
742
742
  if (i === '__proto__' || i === 'constructor') {
743
743
  return;
744
744
  }
745
- path = prefix + i;
745
+ path = prefix ? prefix + i : i;
746
746
  schemaType = docSchema.path(path);
747
747
  // Should still work if not a model-level discriminator, but should not be
748
748
  // necessary. This is *only* to catch the case where we queried using the
@@ -751,7 +751,8 @@ function init(self, obj, doc, opts, prefix) {
751
751
  return;
752
752
  }
753
753
 
754
- if (!schemaType && utils.isPOJO(obj[i])) {
754
+ const value = obj[i];
755
+ if (!schemaType && utils.isPOJO(value)) {
755
756
  // assume nested object
756
757
  if (!doc[i]) {
757
758
  doc[i] = {};
@@ -759,29 +760,30 @@ function init(self, obj, doc, opts, prefix) {
759
760
  self[i] = doc[i];
760
761
  }
761
762
  }
762
- init(self, obj[i], doc[i], opts, path + '.');
763
+ init(self, value, doc[i], opts, path + '.');
763
764
  } else if (!schemaType) {
764
- doc[i] = obj[i];
765
+ doc[i] = value;
765
766
  if (!strict && !prefix) {
766
- self[i] = obj[i];
767
+ self[i] = value;
767
768
  }
768
769
  } else {
769
770
  // Retain order when overwriting defaults
770
- if (doc.hasOwnProperty(i) && obj[i] !== void 0 && !opts.hydratedPopulatedDocs) {
771
+ if (doc.hasOwnProperty(i) && value !== void 0 && !opts.hydratedPopulatedDocs) {
771
772
  delete doc[i];
772
773
  }
773
- if (obj[i] === null) {
774
+ if (value === null) {
774
775
  doc[i] = schemaType._castNullish(null);
775
- } else if (obj[i] !== undefined) {
776
- const wasPopulated = obj[i].$__ == null ? null : obj[i].$__.wasPopulated;
777
- if ((schemaType && !wasPopulated) && !opts.hydratedPopulatedDocs) {
776
+ } else if (value !== undefined) {
777
+ const wasPopulated = value.$__ == null ? null : value.$__.wasPopulated;
778
+
779
+ if (schemaType && !wasPopulated && !opts.hydratedPopulatedDocs) {
778
780
  try {
779
781
  if (opts && opts.setters) {
780
782
  // Call applySetters with `init = false` because otherwise setters are a noop
781
783
  const overrideInit = false;
782
- doc[i] = schemaType.applySetters(obj[i], self, overrideInit);
784
+ doc[i] = schemaType.applySetters(value, self, overrideInit);
783
785
  } else {
784
- doc[i] = schemaType.cast(obj[i], self, true);
786
+ doc[i] = schemaType.cast(value, self, true);
785
787
  }
786
788
  } catch (e) {
787
789
  self.invalidate(e.path, new ValidatorError({
@@ -793,7 +795,7 @@ function init(self, obj, doc, opts, prefix) {
793
795
  }));
794
796
  }
795
797
  } else {
796
- doc[i] = obj[i];
798
+ doc[i] = value;
797
799
  }
798
800
  }
799
801
  // mark as hydrated
@@ -1388,6 +1390,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1388
1390
  if (schema.options &&
1389
1391
  Array.isArray(schema.options[typeKey]) &&
1390
1392
  schema.options[typeKey].length &&
1393
+ schema.options[typeKey][0] &&
1391
1394
  schema.options[typeKey][0].ref &&
1392
1395
  _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) {
1393
1396
  popOpts = { [populateModelSymbol]: val[0].constructor };
@@ -4735,7 +4738,7 @@ Document.prototype.$clone = function() {
4735
4738
  const clonedDoc = new Model();
4736
4739
  clonedDoc.$isNew = this.$isNew;
4737
4740
  if (this._doc) {
4738
- clonedDoc._doc = clone(this._doc);
4741
+ clonedDoc._doc = clone(this._doc, { retainDocuments: true });
4739
4742
  }
4740
4743
  if (this.$__) {
4741
4744
  const Cache = this.$__.constructor;
@@ -136,11 +136,10 @@ function iter(i) {
136
136
  let promise = null;
137
137
  let timeout = null;
138
138
  if (syncCollectionMethods[i] && typeof lastArg === 'function') {
139
- this.addQueue(() => {
140
- lastArg.call(this, null, this[i].apply(this, _args.slice(0, _args.length - 1)));
141
- }, []);
139
+ this.addQueue(i, _args);
140
+ callback = lastArg;
142
141
  } else if (syncCollectionMethods[i]) {
143
- promise = new Promise((resolve, reject) => {
142
+ promise = new this.Promise((resolve, reject) => {
144
143
  callback = function collectionOperationCallback(err, res) {
145
144
  if (timeout != null) {
146
145
  clearTimeout(timeout);
@@ -36,10 +36,23 @@ function clone(obj, options, isArrayChild) {
36
36
  }
37
37
 
38
38
  if (isMongooseObject(obj)) {
39
- // Single nested subdocs should apply getters later in `applyGetters()`
40
- // when calling `toObject()`. See gh-7442, gh-8295
41
- if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
42
- options = Object.assign({}, options, { getters: false });
39
+ if (options) {
40
+ // Single nested subdocs should apply getters later in `applyGetters()`
41
+ // when calling `toObject()`. See gh-7442, gh-8295
42
+ if (options._skipSingleNestedGetters && obj.$isSingleNested) {
43
+ options = Object.assign({}, options, { getters: false });
44
+ }
45
+ if (options.retainDocuments && obj.$__ != null) {
46
+ const clonedDoc = obj.$clone();
47
+ if (obj.__index != null) {
48
+ clonedDoc.__index = obj.__index;
49
+ }
50
+ if (obj.__parentArray != null) {
51
+ clonedDoc.__parentArray = obj.__parentArray;
52
+ }
53
+ clonedDoc.$__parent = obj.$__parent;
54
+ return clonedDoc;
55
+ }
43
56
  }
44
57
  const isSingleNested = obj.$isSingleNested;
45
58
 
@@ -19,11 +19,10 @@ function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
19
19
  if (schemaType._appliedDiscriminators) {
20
20
  continue;
21
21
  }
22
- for (const disc of schemaType.schema._applyDiscriminators.keys()) {
23
- schemaType.discriminator(
24
- disc,
25
- schemaType.schema._applyDiscriminators.get(disc)
26
- );
22
+ for (const discriminatorKey of schemaType.schema._applyDiscriminators.keys()) {
23
+ const discriminatorSchema = schemaType.schema._applyDiscriminators.get(discriminatorKey);
24
+ applyEmbeddedDiscriminators(discriminatorSchema, seen);
25
+ schemaType.discriminator(discriminatorKey, discriminatorSchema);
27
26
  }
28
27
  schemaType._appliedDiscriminators = true;
29
28
  }
@@ -6,6 +6,12 @@ module.exports = function applyWriteConcern(schema, options) {
6
6
  if (options.writeConcern != null) {
7
7
  return;
8
8
  }
9
+ // Don't apply default write concern to operations in transactions,
10
+ // because setting write concern on an operation in a transaction is an error
11
+ // See: https://www.mongodb.com/docs/manual/reference/write-concern/
12
+ if (options && options.session && options.session.transaction) {
13
+ return;
14
+ }
9
15
  const writeConcern = get(schema, 'options.writeConcern', {});
10
16
  if (Object.keys(writeConcern).length != 0) {
11
17
  options.writeConcern = {};
package/lib/model.js CHANGED
@@ -351,12 +351,22 @@ Model.prototype.$__handleSave = function(options, callback) {
351
351
 
352
352
  const update = delta[1];
353
353
  if (this.$__schema.options.minimize) {
354
- minimize(update);
355
- // minimize might leave us with an empty object, which would
356
- // lead to MongoDB throwing a "Update document requires atomic operators" error
357
- if (Object.keys(update).length === 0) {
358
- handleEmptyUpdate.call(this);
359
- return;
354
+ for (const updateOp of Object.values(update)) {
355
+ if (updateOp == null) {
356
+ continue;
357
+ }
358
+ for (const key of Object.keys(updateOp)) {
359
+ if (updateOp[key] == null || typeof updateOp[key] !== 'object') {
360
+ continue;
361
+ }
362
+ if (!utils.isPOJO(updateOp[key])) {
363
+ continue;
364
+ }
365
+ minimize(updateOp[key]);
366
+ if (Object.keys(updateOp[key]).length === 0) {
367
+ updateOp[key] = null;
368
+ }
369
+ }
360
370
  }
361
371
  }
362
372
 
@@ -2532,6 +2542,7 @@ Model.$where = function $where() {
2532
2542
  * @param {Boolean} [options.setDefaultsOnInsert=true] If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created
2533
2543
  * @param {Boolean} [options.includeResultMetadata] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html)
2534
2544
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
2545
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
2535
2546
  * @return {Query}
2536
2547
  * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
2537
2548
  * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/
@@ -2624,6 +2635,7 @@ Model.findOneAndUpdate = function(conditions, update, options) {
2624
2635
  * @param {Boolean} [options.new=false] if true, return the modified document rather than the original
2625
2636
  * @param {Object|String} [options.select] sets the document fields to return.
2626
2637
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
2638
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
2627
2639
  * @return {Query}
2628
2640
  * @see Model.findOneAndUpdate https://mongoosejs.com/docs/api/model.html#Model.findOneAndUpdate()
2629
2641
  * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/
@@ -3073,7 +3085,7 @@ Model.startSession = function() {
3073
3085
  * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#insertMany)
3074
3086
  * @param {Boolean} [options.ordered=true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
3075
3087
  * @param {Boolean} [options.rawResult=false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/InsertManyResult.html) with a `mongoose` property that contains `validationErrors` and `results` if this is an unordered `insertMany`.
3076
- * @param {Boolean} [options.lean=false] if `true`, skips hydrating and validating the documents. This option is useful if you need the extra performance, but Mongoose won't validate the documents before inserting.
3088
+ * @param {Boolean} [options.lean=false] if `true`, skips hydrating the documents. This means Mongoose will **not** cast or validate any of the documents passed to `insertMany()`. This option is useful if you need the extra performance, but comes with data integrity risk. Consider using with [`castObject()`](https://mongoosejs.com/docs/api/model.html#Model.castObject()).
3077
3089
  * @param {Number} [options.limit=null] this limits the number of documents being processed (validation/casting) by mongoose in parallel, this does **NOT** send the documents in batches to MongoDB. Use this option if you're processing a large number of documents and your app is running out of memory.
3078
3090
  * @param {String|Object|Array} [options.populate=null] populates the result documents. This option is a no-op if `rawResult` is set.
3079
3091
  * @param {Boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully.
@@ -3134,6 +3146,13 @@ Model.$__insertMany = function(arr, options, callback) {
3134
3146
  const results = ordered ? null : new Array(arr.length);
3135
3147
  const toExecute = arr.map((doc, index) =>
3136
3148
  callback => {
3149
+ // If option `lean` is set to true bypass validation and hydration
3150
+ if (lean) {
3151
+ // we have to execute callback at the nextTick to be compatible
3152
+ // with parallelLimit, as `results` variable has TDZ issue if we
3153
+ // execute the callback synchronously
3154
+ return immediate(() => callback(null, doc));
3155
+ }
3137
3156
  if (!(doc instanceof _this)) {
3138
3157
  if (doc != null && typeof doc !== 'object') {
3139
3158
  return callback(new ObjectParameterError(doc, 'arr.' + index, 'insertMany'));
@@ -3214,7 +3233,7 @@ Model.$__insertMany = function(arr, options, callback) {
3214
3233
  callback(null, []);
3215
3234
  return;
3216
3235
  }
3217
- const docObjects = docAttributes.map(function(doc) {
3236
+ const docObjects = lean ? docAttributes : docAttributes.map(function(doc) {
3218
3237
  if (doc.$__schema.options.versionKey) {
3219
3238
  doc[doc.$__schema.options.versionKey] = 0;
3220
3239
  }
@@ -3227,9 +3246,11 @@ Model.$__insertMany = function(arr, options, callback) {
3227
3246
 
3228
3247
  _this.$__collection.insertMany(docObjects, options).then(
3229
3248
  res => {
3230
- for (const attribute of docAttributes) {
3231
- attribute.$__reset();
3232
- _setIsNew(attribute, false);
3249
+ if (!lean) {
3250
+ for (const attribute of docAttributes) {
3251
+ attribute.$__reset();
3252
+ _setIsNew(attribute, false);
3253
+ }
3233
3254
  }
3234
3255
 
3235
3256
  if (ordered === false && throwOnValidationError && validationErrors.length > 0) {
@@ -3331,6 +3352,9 @@ Model.$__insertMany = function(arr, options, callback) {
3331
3352
  return !isErrored;
3332
3353
  }).
3333
3354
  map(function setIsNewForInsertedDoc(doc) {
3355
+ if (lean) {
3356
+ return doc;
3357
+ }
3334
3358
  doc.$__reset();
3335
3359
  _setIsNew(doc, false);
3336
3360
  return doc;
@@ -3373,6 +3397,7 @@ function _setIsNew(doc, val) {
3373
3397
  * trip to MongoDB.
3374
3398
  *
3375
3399
  * Mongoose will perform casting on all operations you provide.
3400
+ * The only exception is [setting the `update` operator for `updateOne` or `updateMany` to a pipeline](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#updateone-and-updatemany): Mongoose does **not** cast update pipelines.
3376
3401
  *
3377
3402
  * This function does **not** trigger any middleware, neither `save()`, nor `update()`.
3378
3403
  * If you need to trigger
@@ -3408,6 +3433,15 @@ function _setIsNew(doc, val) {
3408
3433
  * console.log(res.insertedCount, res.modifiedCount, res.deletedCount);
3409
3434
  * });
3410
3435
  *
3436
+ * // Mongoose does **not** cast update pipelines, so no casting for the `update` option below.
3437
+ * // Mongoose does still cast `filter`
3438
+ * await Character.bulkWrite([{
3439
+ * updateOne: {
3440
+ * filter: { name: 'Annika Hansen' },
3441
+ * update: [{ $set: { name: 7 } }] // Array means update pipeline, so Mongoose skips casting
3442
+ * }
3443
+ * }]);
3444
+ *
3411
3445
  * The [supported operations](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite) are:
3412
3446
  *
3413
3447
  * - `insertOne`
@@ -3937,13 +3971,14 @@ Model.hydrate = function(obj, projection, options) {
3937
3971
  * - `updateMany()`
3938
3972
  *
3939
3973
  * @param {Object} filter
3940
- * @param {Object|Array} update
3974
+ * @param {Object|Array} update. If array, this update will be treated as an update pipeline and not casted.
3941
3975
  * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3942
3976
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3943
3977
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3944
3978
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
3945
3979
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3946
3980
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3981
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3947
3982
  * @return {Query}
3948
3983
  * @see Query docs https://mongoosejs.com/docs/queries.html
3949
3984
  * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
@@ -3976,13 +4011,14 @@ Model.updateMany = function updateMany(conditions, doc, options) {
3976
4011
  * - `updateOne()`
3977
4012
  *
3978
4013
  * @param {Object} filter
3979
- * @param {Object|Array} update
4014
+ * @param {Object|Array} update. If array, this update will be treated as an update pipeline and not casted.
3980
4015
  * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3981
4016
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3982
4017
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3983
4018
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
3984
4019
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
3985
4020
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
4021
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3986
4022
  * @return {Query}
3987
4023
  * @see Query docs https://mongoosejs.com/docs/queries.html
3988
4024
  * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
package/lib/mongoose.js CHANGED
@@ -506,12 +506,14 @@ Mongoose.prototype.pluralize = function(fn) {
506
506
  *
507
507
  * // or
508
508
  *
509
- * const collectionName = 'actor'
510
- * const M = mongoose.model('Actor', schema, collectionName)
509
+ * const collectionName = 'actor';
510
+ * const M = mongoose.model('Actor', schema, collectionName);
511
511
  *
512
512
  * @param {String|Function} name model name or class extending Model
513
513
  * @param {Schema} [schema] the schema to use.
514
514
  * @param {String} [collection] name (optional, inferred from model name)
515
+ * @param {Object} [options]
516
+ * @param {Boolean} [options.overwriteModels=false] If true, overwrite existing models with the same name to avoid `OverwriteModelError`
515
517
  * @return {Model} The model associated with `name`. Mongoose will create the model if it doesn't already exist.
516
518
  * @api public
517
519
  */
package/lib/query.js CHANGED
@@ -3191,6 +3191,7 @@ function prepareDiscriminatorCriteria(query) {
3191
3191
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
3192
3192
  * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
3193
3193
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3194
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3194
3195
  * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
3195
3196
  * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
3196
3197
  * @see ModifyResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html
@@ -3879,7 +3880,7 @@ Query.prototype._replaceOne = async function _replaceOne() {
3879
3880
  * - `updateMany()`
3880
3881
  *
3881
3882
  * @param {Object} [filter]
3882
- * @param {Object|Array} [update] the update command
3883
+ * @param {Object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted.
3883
3884
  * @param {Object} [options]
3884
3885
  * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
3885
3886
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
@@ -3887,6 +3888,7 @@ Query.prototype._replaceOne = async function _replaceOne() {
3887
3888
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
3888
3889
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3889
3890
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3891
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3890
3892
  * @param {Function} [callback] params are (error, writeOpResult)
3891
3893
  * @return {Query} this
3892
3894
  * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
@@ -3948,14 +3950,15 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
3948
3950
  * - `updateOne()`
3949
3951
  *
3950
3952
  * @param {Object} [filter]
3951
- * @param {Object|Array} [update] the update command
3953
+ * @param {Object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted.
3952
3954
  * @param {Object} [options]
3953
3955
  * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
3954
3956
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3955
3957
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3956
3958
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
3957
3959
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
3958
- @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3960
+ * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3961
+ * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3959
3962
  * @param {Function} [callback] params are (error, writeOpResult)
3960
3963
  * @return {Query} this
3961
3964
  * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
package/lib/schema.js CHANGED
@@ -1014,6 +1014,9 @@ reserved.collection = 1;
1014
1014
 
1015
1015
  Schema.prototype.path = function(path, obj) {
1016
1016
  if (obj === undefined) {
1017
+ if (this.paths[path] != null) {
1018
+ return this.paths[path];
1019
+ }
1017
1020
  // Convert to '.$' to check subpaths re: gh-6405
1018
1021
  const cleanPath = _pathToPositionalSyntax(path);
1019
1022
  let schematype = _getPath(this, path, cleanPath);
@@ -698,22 +698,21 @@ const methods = {
698
698
 
699
699
  if ((atomics.$push && atomics.$push.$each && atomics.$push.$each.length || 0) !== 0 &&
700
700
  atomics.$push.$position != atomic.$position) {
701
- throw new MongooseError('Cannot call `Array#push()` multiple times ' +
702
- 'with different `$position`');
703
- }
701
+ if (atomic.$position != null) {
702
+ [].splice.apply(arr, [atomic.$position, 0].concat(values));
703
+ ret = arr.length;
704
+ } else {
705
+ ret = [].push.apply(arr, values);
706
+ }
704
707
 
705
- if (atomic.$position != null) {
708
+ this._registerAtomic('$set', this);
709
+ } else if (atomic.$position != null) {
706
710
  [].splice.apply(arr, [atomic.$position, 0].concat(values));
707
711
  ret = this.length;
708
712
  } else {
709
713
  ret = [].push.apply(arr, values);
710
714
  }
711
715
  } else {
712
- if ((atomics.$push && atomics.$push.$each && atomics.$push.$each.length || 0) !== 0 &&
713
- atomics.$push.$position != null) {
714
- throw new MongooseError('Cannot call `Array#push()` multiple times ' +
715
- 'with different `$position`');
716
- }
717
716
  atomic = values;
718
717
  ret = _basePush.apply(arr, values);
719
718
  }
@@ -145,7 +145,7 @@ ArraySubdocument.prototype.$__fullPath = function(path, skipIndex) {
145
145
  */
146
146
 
147
147
  ArraySubdocument.prototype.$__pathRelativeToParent = function(path, skipIndex) {
148
- if (this.__index == null) {
148
+ if (this.__index == null || (!this.__parentArray || !this.__parentArray.$path)) {
149
149
  return null;
150
150
  }
151
151
  if (skipIndex) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.2.0",
4
+ "version": "8.2.2",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -28,8 +28,8 @@
28
28
  "sift": "16.0.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@babel/core": "7.23.9",
32
- "@babel/preset-env": "7.23.9",
31
+ "@babel/core": "7.24.0",
32
+ "@babel/preset-env": "7.24.0",
33
33
  "@typescript-eslint/eslint-plugin": "^6.2.1",
34
34
  "@typescript-eslint/parser": "^6.2.1",
35
35
  "acquit": "1.3.0",
@@ -43,9 +43,9 @@
43
43
  "buffer": "^5.6.0",
44
44
  "cheerio": "1.0.0-rc.12",
45
45
  "crypto-browserify": "3.12.0",
46
- "dotenv": "16.4.1",
46
+ "dotenv": "16.4.5",
47
47
  "dox": "1.0.0",
48
- "eslint": "8.56.0",
48
+ "eslint": "8.57.0",
49
49
  "eslint-plugin-markdown": "^3.0.1",
50
50
  "eslint-plugin-mocha-no-only": "1.1.1",
51
51
  "express": "^4.18.1",
@@ -56,7 +56,7 @@
56
56
  "markdownlint-cli2": "^0.12.1",
57
57
  "marked": "4.3.0",
58
58
  "mkdirp": "^3.0.1",
59
- "mocha": "10.2.0",
59
+ "mocha": "10.3.0",
60
60
  "moment": "2.x",
61
61
  "mongodb-memory-server": "8.15.1",
62
62
  "ncp": "^2.0.0",
@@ -65,10 +65,10 @@
65
65
  "q": "1.5.1",
66
66
  "sinon": "17.0.1",
67
67
  "stream-browserify": "3.0.0",
68
- "tsd": "0.30.4",
68
+ "tsd": "0.30.7",
69
69
  "typescript": "5.3.3",
70
70
  "uuid": "9.0.1",
71
- "webpack": "5.90.1"
71
+ "webpack": "5.90.3"
72
72
  },
73
73
  "directories": {
74
74
  "lib": "./lib/mongoose"
@@ -229,7 +229,7 @@ type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueT
229
229
  ObtainDocumentPathType<Item, TypeKey>[] :
230
230
  // If the type key isn't callable, then this is an array of objects, in which case
231
231
  // we need to call ObtainDocumentType to correctly infer its type.
232
- ObtainDocumentType<Item, any, { typeKey: TypeKey }>[] :
232
+ Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
233
233
  IsSchemaTypeFromBuiltinClass<Item> extends true ?
234
234
  ObtainDocumentPathType<Item, TypeKey>[] :
235
235
  IsItRecordAndNotAny<Item> extends true ?