mongoose 8.20.0 → 9.0.0-rc1

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.
Files changed (90) hide show
  1. package/eslint.config.mjs +198 -0
  2. package/lib/aggregate.js +17 -73
  3. package/lib/cast/bigint.js +1 -1
  4. package/lib/cast/double.js +1 -1
  5. package/lib/cast/uuid.js +5 -48
  6. package/lib/cast.js +2 -2
  7. package/lib/connection.js +0 -1
  8. package/lib/cursor/aggregationCursor.js +14 -24
  9. package/lib/cursor/queryCursor.js +7 -14
  10. package/lib/document.js +125 -121
  11. package/lib/drivers/node-mongodb-native/connection.js +3 -10
  12. package/lib/error/objectParameter.js +1 -2
  13. package/lib/error/validation.js +0 -8
  14. package/lib/helpers/clone.js +1 -1
  15. package/lib/helpers/common.js +1 -1
  16. package/lib/helpers/indexes/isIndexEqual.js +0 -1
  17. package/lib/helpers/model/applyDefaultsToPOJO.js +2 -2
  18. package/lib/helpers/model/applyHooks.js +43 -53
  19. package/lib/helpers/model/applyMethods.js +2 -2
  20. package/lib/helpers/model/applyStaticHooks.js +1 -48
  21. package/lib/helpers/model/castBulkWrite.js +1 -1
  22. package/lib/helpers/parallelLimit.js +18 -36
  23. package/lib/helpers/pluralize.js +3 -3
  24. package/lib/helpers/populate/assignRawDocsToIdStructure.js +1 -8
  25. package/lib/helpers/populate/createPopulateQueryFilter.js +1 -1
  26. package/lib/helpers/populate/getModelsMapForPopulate.js +17 -9
  27. package/lib/helpers/populate/getSchemaTypes.js +5 -5
  28. package/lib/helpers/query/cast$expr.js +8 -10
  29. package/lib/helpers/query/castFilterPath.js +1 -1
  30. package/lib/helpers/query/castUpdate.js +14 -12
  31. package/lib/helpers/query/getEmbeddedDiscriminatorPath.js +1 -1
  32. package/lib/helpers/schema/applyPlugins.js +1 -1
  33. package/lib/helpers/schema/getIndexes.js +1 -7
  34. package/lib/helpers/timestamps/setupTimestamps.js +3 -6
  35. package/lib/helpers/updateValidators.js +57 -111
  36. package/lib/model.js +419 -607
  37. package/lib/mongoose.js +41 -13
  38. package/lib/plugins/saveSubdocs.js +24 -51
  39. package/lib/plugins/sharding.js +5 -4
  40. package/lib/plugins/validateBeforeSave.js +3 -13
  41. package/lib/query.js +101 -145
  42. package/lib/queryHelpers.js +2 -2
  43. package/lib/schema/array.js +41 -84
  44. package/lib/schema/documentArray.js +57 -94
  45. package/lib/schema/documentArrayElement.js +16 -11
  46. package/lib/schema/string.js +1 -1
  47. package/lib/schema/subdocument.js +22 -28
  48. package/lib/schema/uuid.js +0 -21
  49. package/lib/schema.js +81 -39
  50. package/lib/schemaType.js +39 -57
  51. package/lib/types/array/index.js +2 -2
  52. package/lib/types/array/methods/index.js +4 -4
  53. package/lib/types/arraySubdocument.js +1 -1
  54. package/lib/types/buffer.js +10 -10
  55. package/lib/types/decimal128.js +1 -1
  56. package/lib/types/documentArray/index.js +1 -1
  57. package/lib/types/documentArray/methods/index.js +5 -3
  58. package/lib/types/double.js +1 -1
  59. package/lib/types/objectid.js +1 -1
  60. package/lib/types/subdocument.js +15 -43
  61. package/lib/types/uuid.js +1 -1
  62. package/lib/utils.js +1 -8
  63. package/lib/validOptions.js +3 -3
  64. package/package.json +11 -24
  65. package/types/connection.d.ts +20 -11
  66. package/types/document.d.ts +95 -26
  67. package/types/index.d.ts +143 -39
  68. package/types/inferhydrateddoctype.d.ts +115 -0
  69. package/types/inferrawdoctype.d.ts +99 -75
  70. package/types/inferschematype.d.ts +17 -3
  71. package/types/middlewares.d.ts +0 -2
  72. package/types/models.d.ts +131 -199
  73. package/types/mongooseoptions.d.ts +6 -5
  74. package/types/pipelinestage.d.ts +1 -1
  75. package/types/query.d.ts +71 -139
  76. package/types/schemaoptions.d.ts +1 -1
  77. package/types/schematypes.d.ts +14 -10
  78. package/types/types.d.ts +3 -4
  79. package/types/utility.d.ts +68 -48
  80. package/types/validation.d.ts +18 -14
  81. package/browser.js +0 -8
  82. package/dist/browser.umd.js +0 -2
  83. package/lib/browser.js +0 -141
  84. package/lib/browserDocument.js +0 -101
  85. package/lib/documentProvider.js +0 -30
  86. package/lib/drivers/browser/binary.js +0 -14
  87. package/lib/drivers/browser/decimal128.js +0 -7
  88. package/lib/drivers/browser/index.js +0 -13
  89. package/lib/drivers/browser/objectid.js +0 -29
  90. package/lib/helpers/promiseOrCallback.js +0 -54
package/lib/query.js CHANGED
@@ -116,7 +116,7 @@ function Query(conditions, options, model, collection) {
116
116
 
117
117
  this._transforms = [];
118
118
  this._hooks = new Kareem();
119
- this._executionStack = null;
119
+ this._execCount = 0;
120
120
 
121
121
  // this is the case where we have a CustomQuery, we need to check if we got
122
122
  // options passed in, and if we did, merge them in
@@ -300,7 +300,6 @@ Query.prototype.toConstructor = function toConstructor() {
300
300
  p.setOptions(options);
301
301
 
302
302
  p.op = this.op;
303
- p._validateOp();
304
303
  p._conditions = clone(this._conditions);
305
304
  p._fields = clone(this._fields);
306
305
  p._update = clone(this._update, {
@@ -349,7 +348,6 @@ Query.prototype.clone = function() {
349
348
  q.setOptions(options);
350
349
 
351
350
  q.op = this.op;
352
- q._validateOp();
353
351
  q._conditions = clone(this._conditions);
354
352
  q._fields = clone(this._fields);
355
353
  q._update = clone(this._update, {
@@ -513,6 +511,10 @@ Query.prototype._validateOp = function() {
513
511
  if (this.op != null && !validOpsSet.has(this.op)) {
514
512
  this.error(new Error('Query has invalid `op`: "' + this.op + '"'));
515
513
  }
514
+
515
+ if (this.op !== 'estimatedDocumentCount' && this._conditions == null) {
516
+ throw new ObjectParameterError(this._conditions, 'filter', this.op);
517
+ }
516
518
  };
517
519
 
518
520
  /**
@@ -918,7 +920,7 @@ Query.prototype.limit = function limit(v) {
918
920
  if (typeof v === 'string') {
919
921
  try {
920
922
  v = castNumber(v);
921
- } catch (err) {
923
+ } catch {
922
924
  throw new CastError('Number', v, 'limit');
923
925
  }
924
926
  }
@@ -952,7 +954,7 @@ Query.prototype.skip = function skip(v) {
952
954
  if (typeof v === 'string') {
953
955
  try {
954
956
  v = castNumber(v);
955
- } catch (err) {
957
+ } catch {
956
958
  throw new CastError('Number', v, 'skip');
957
959
  }
958
960
  }
@@ -1746,6 +1748,10 @@ Query.prototype.setOptions = function(options, overwrite) {
1746
1748
  this._mongooseOptions.overwriteImmutable = options.overwriteImmutable;
1747
1749
  delete options.overwriteImmutable;
1748
1750
  }
1751
+ if ('updatePipeline' in options) {
1752
+ this._mongooseOptions.updatePipeline = options.updatePipeline;
1753
+ delete options.updatePipeline;
1754
+ }
1749
1755
  if ('sanitizeProjection' in options) {
1750
1756
  if (options.sanitizeProjection && !this._mongooseOptions.sanitizeProjection) {
1751
1757
  sanitizeProjection(this._fields);
@@ -1782,14 +1788,14 @@ Query.prototype.setOptions = function(options, overwrite) {
1782
1788
  if (typeof options.limit === 'string') {
1783
1789
  try {
1784
1790
  options.limit = castNumber(options.limit);
1785
- } catch (err) {
1791
+ } catch {
1786
1792
  throw new CastError('Number', options.limit, 'limit');
1787
1793
  }
1788
1794
  }
1789
1795
  if (typeof options.skip === 'string') {
1790
1796
  try {
1791
1797
  options.skip = castNumber(options.skip);
1792
- } catch (err) {
1798
+ } catch {
1793
1799
  throw new CastError('Number', options.skip, 'skip');
1794
1800
  }
1795
1801
  }
@@ -2481,7 +2487,7 @@ Query.prototype.find = function(conditions) {
2481
2487
 
2482
2488
  this.op = 'find';
2483
2489
 
2484
- if (mquery.canMerge(conditions)) {
2490
+ if (canMerge(conditions)) {
2485
2491
  this.merge(conditions);
2486
2492
 
2487
2493
  prepareDiscriminatorCriteria(this);
@@ -2503,9 +2509,14 @@ Query.prototype.find = function(conditions) {
2503
2509
 
2504
2510
  Query.prototype.merge = function(source) {
2505
2511
  if (!source) {
2512
+ if (source === null) {
2513
+ this._conditions = null;
2514
+ }
2506
2515
  return this;
2507
2516
  }
2508
2517
 
2518
+ this._conditions = this._conditions ?? {};
2519
+
2509
2520
  const opts = { overwrite: true };
2510
2521
 
2511
2522
  if (source instanceof Query) {
@@ -2756,7 +2767,6 @@ Query.prototype.findOne = function(conditions, projection, options) {
2756
2767
  }
2757
2768
 
2758
2769
  this.op = 'findOne';
2759
- this._validateOp();
2760
2770
 
2761
2771
  if (options) {
2762
2772
  this.setOptions(options);
@@ -2766,7 +2776,7 @@ Query.prototype.findOne = function(conditions, projection, options) {
2766
2776
  this.select(projection);
2767
2777
  }
2768
2778
 
2769
- if (mquery.canMerge(conditions)) {
2779
+ if (canMerge(conditions)) {
2770
2780
  this.merge(conditions);
2771
2781
 
2772
2782
  prepareDiscriminatorCriteria(this);
@@ -2883,7 +2893,6 @@ Query.prototype.estimatedDocumentCount = function(options) {
2883
2893
  }
2884
2894
 
2885
2895
  this.op = 'estimatedDocumentCount';
2886
- this._validateOp();
2887
2896
 
2888
2897
  if (options != null) {
2889
2898
  this.setOptions(options);
@@ -2938,9 +2947,8 @@ Query.prototype.countDocuments = function(conditions, options) {
2938
2947
  }
2939
2948
 
2940
2949
  this.op = 'countDocuments';
2941
- this._validateOp();
2942
2950
 
2943
- if (mquery.canMerge(conditions)) {
2951
+ if (canMerge(conditions)) {
2944
2952
  this.merge(conditions);
2945
2953
  }
2946
2954
 
@@ -3004,9 +3012,8 @@ Query.prototype.distinct = function(field, conditions, options) {
3004
3012
  }
3005
3013
 
3006
3014
  this.op = 'distinct';
3007
- this._validateOp();
3008
3015
 
3009
- if (mquery.canMerge(conditions)) {
3016
+ if (canMerge(conditions)) {
3010
3017
  this.merge(conditions);
3011
3018
 
3012
3019
  prepareDiscriminatorCriteria(this);
@@ -3174,7 +3181,7 @@ Query.prototype.deleteOne = function deleteOne(filter, options) {
3174
3181
  this.op = 'deleteOne';
3175
3182
  this.setOptions(options);
3176
3183
 
3177
- if (mquery.canMerge(filter)) {
3184
+ if (canMerge(filter)) {
3178
3185
  this.merge(filter);
3179
3186
 
3180
3187
  prepareDiscriminatorCriteria(this);
@@ -3250,7 +3257,7 @@ Query.prototype.deleteMany = function(filter, options) {
3250
3257
  this.setOptions(options);
3251
3258
  this.op = 'deleteMany';
3252
3259
 
3253
- if (mquery.canMerge(filter)) {
3260
+ if (canMerge(filter)) {
3254
3261
  this.merge(filter);
3255
3262
 
3256
3263
  prepareDiscriminatorCriteria(this);
@@ -3382,7 +3389,7 @@ function prepareDiscriminatorCriteria(query) {
3382
3389
  * @memberOf Query
3383
3390
  * @instance
3384
3391
  * @param {Object|Query} [filter]
3385
- * @param {Object} [doc]
3392
+ * @param {Object} [update]
3386
3393
  * @param {Object} [options]
3387
3394
  * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
3388
3395
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
@@ -3404,16 +3411,15 @@ function prepareDiscriminatorCriteria(query) {
3404
3411
  * @api public
3405
3412
  */
3406
3413
 
3407
- Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3414
+ Query.prototype.findOneAndUpdate = function(filter, update, options) {
3408
3415
  if (typeof filter === 'function' ||
3409
- typeof doc === 'function' ||
3416
+ typeof update === 'function' ||
3410
3417
  typeof options === 'function' ||
3411
3418
  typeof arguments[3] === 'function') {
3412
3419
  throw new MongooseError('Query.prototype.findOneAndUpdate() no longer accepts a callback');
3413
3420
  }
3414
3421
 
3415
3422
  this.op = 'findOneAndUpdate';
3416
- this._validateOp();
3417
3423
  this._validate();
3418
3424
 
3419
3425
  switch (arguments.length) {
@@ -3421,12 +3427,12 @@ Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3421
3427
  options = undefined;
3422
3428
  break;
3423
3429
  case 1:
3424
- doc = filter;
3430
+ update = filter;
3425
3431
  filter = options = undefined;
3426
3432
  break;
3427
3433
  }
3428
3434
 
3429
- if (mquery.canMerge(filter)) {
3435
+ if (canMerge(filter)) {
3430
3436
  this.merge(filter);
3431
3437
  } else if (filter != null) {
3432
3438
  this.error(
@@ -3434,11 +3440,6 @@ Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3434
3440
  );
3435
3441
  }
3436
3442
 
3437
- // apply doc
3438
- if (doc) {
3439
- this._mergeUpdate(doc);
3440
- }
3441
-
3442
3443
  options = options ? clone(options) : {};
3443
3444
 
3444
3445
  if (options.projection) {
@@ -3450,17 +3451,23 @@ Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3450
3451
  delete options.fields;
3451
3452
  }
3452
3453
 
3453
- const returnOriginal = this &&
3454
- this.model &&
3455
- this.model.base &&
3456
- this.model.base.options &&
3457
- this.model.base.options.returnOriginal;
3454
+ const returnOriginal = this?.model?.base?.options?.returnOriginal;
3458
3455
  if (options.new == null && options.returnDocument == null && options.returnOriginal == null && returnOriginal != null) {
3459
3456
  options.returnOriginal = returnOriginal;
3460
3457
  }
3461
3458
 
3459
+ const updatePipeline = this?.model?.base?.options?.updatePipeline;
3460
+ if (options.updatePipeline == null && updatePipeline != null) {
3461
+ options.updatePipeline = updatePipeline;
3462
+ }
3463
+
3462
3464
  this.setOptions(options);
3463
3465
 
3466
+ // apply doc
3467
+ if (update) {
3468
+ this._mergeUpdate(update);
3469
+ }
3470
+
3464
3471
  return this;
3465
3472
  };
3466
3473
 
@@ -3509,7 +3516,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
3509
3516
  delete $set._id;
3510
3517
  this._update = { $set };
3511
3518
  } else {
3512
- this._executionStack = null;
3519
+ this._execCount = 0;
3513
3520
  const res = await this._findOne();
3514
3521
  return res;
3515
3522
  }
@@ -3591,10 +3598,9 @@ Query.prototype.findOneAndDelete = function(filter, options) {
3591
3598
  }
3592
3599
 
3593
3600
  this.op = 'findOneAndDelete';
3594
- this._validateOp();
3595
3601
  this._validate();
3596
3602
 
3597
- if (mquery.canMerge(filter)) {
3603
+ if (canMerge(filter)) {
3598
3604
  this.merge(filter);
3599
3605
  }
3600
3606
 
@@ -3694,10 +3700,9 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
3694
3700
  }
3695
3701
 
3696
3702
  this.op = 'findOneAndReplace';
3697
- this._validateOp();
3698
3703
  this._validate();
3699
3704
 
3700
- if (mquery.canMerge(filter)) {
3705
+ if (canMerge(filter)) {
3701
3706
  this.merge(filter);
3702
3707
  } else if (filter != null) {
3703
3708
  this.error(
@@ -3711,14 +3716,11 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
3711
3716
 
3712
3717
  options = options || {};
3713
3718
 
3714
- const returnOriginal = this &&
3715
- this.model &&
3716
- this.model.base &&
3717
- this.model.base.options &&
3718
- this.model.base.options.returnOriginal;
3719
+ const returnOriginal = this?.model?.base?.options?.returnOriginal;
3719
3720
  if (options.new == null && options.returnDocument == null && options.returnOriginal == null && returnOriginal != null) {
3720
3721
  options.returnOriginal = returnOriginal;
3721
3722
  }
3723
+
3722
3724
  this.setOptions(options);
3723
3725
 
3724
3726
  return this;
@@ -3997,39 +3999,43 @@ function _completeManyLean(schema, docs, path, opts) {
3997
3999
  * Override mquery.prototype._mergeUpdate to handle mongoose objects in
3998
4000
  * updates.
3999
4001
  *
4000
- * @param {Object} doc
4002
+ * @param {Object} update
4001
4003
  * @method _mergeUpdate
4002
4004
  * @memberOf Query
4003
4005
  * @instance
4004
4006
  * @api private
4005
4007
  */
4006
4008
 
4007
- Query.prototype._mergeUpdate = function(doc) {
4009
+ Query.prototype._mergeUpdate = function(update) {
4010
+ const updatePipeline = this._mongooseOptions.updatePipeline;
4011
+ if (!updatePipeline && Array.isArray(update)) {
4012
+ throw new MongooseError('Cannot pass an array to query updates unless the `updatePipeline` option is set.');
4013
+ }
4008
4014
  if (!this._update) {
4009
- this._update = Array.isArray(doc) ? [] : {};
4015
+ this._update = Array.isArray(update) ? [] : {};
4010
4016
  }
4011
4017
 
4012
- if (doc == null || (typeof doc === 'object' && Object.keys(doc).length === 0)) {
4018
+ if (update == null || (typeof update === 'object' && Object.keys(update).length === 0)) {
4013
4019
  return;
4014
4020
  }
4015
4021
 
4016
- if (doc instanceof Query) {
4022
+ if (update instanceof Query) {
4017
4023
  if (Array.isArray(this._update)) {
4018
- throw new Error('Cannot mix array and object updates');
4024
+ throw new MongooseError('Cannot mix array and object updates');
4019
4025
  }
4020
- if (doc._update) {
4021
- utils.mergeClone(this._update, doc._update);
4026
+ if (update._update) {
4027
+ utils.mergeClone(this._update, update._update);
4022
4028
  }
4023
- } else if (Array.isArray(doc)) {
4029
+ } else if (Array.isArray(update)) {
4024
4030
  if (!Array.isArray(this._update)) {
4025
- throw new Error('Cannot mix array and object updates');
4031
+ throw new MongooseError('Cannot mix array and object updates');
4026
4032
  }
4027
- this._update = this._update.concat(doc);
4033
+ this._update = this._update.concat(update);
4028
4034
  } else {
4029
4035
  if (Array.isArray(this._update)) {
4030
- throw new Error('Cannot mix array and object updates');
4036
+ throw new MongooseError('Cannot mix array and object updates');
4031
4037
  }
4032
- utils.mergeClone(this._update, doc);
4038
+ utils.mergeClone(this._update, update);
4033
4039
  }
4034
4040
  };
4035
4041
 
@@ -4057,7 +4063,7 @@ async function _updateThunk(op) {
4057
4063
  this._update = clone(this._update, options);
4058
4064
  const isOverwriting = op === 'replaceOne';
4059
4065
  if (isOverwriting) {
4060
- this._update = new this.model(this._update, null, true);
4066
+ this._update = new this.model(this._update, null, { skipId: true });
4061
4067
  } else {
4062
4068
  this._update = this._castUpdate(this._update);
4063
4069
 
@@ -4111,14 +4117,7 @@ Query.prototype.validate = async function validate(castedDoc, options, isOverwri
4111
4117
  if (isOverwriting) {
4112
4118
  await castedDoc.$validate();
4113
4119
  } else {
4114
- await new Promise((resolve, reject) => {
4115
- updateValidators(this, this.model.schema, castedDoc, options, (err) => {
4116
- if (err != null) {
4117
- return reject(err);
4118
- }
4119
- resolve();
4120
- });
4121
- });
4120
+ await updateValidators(this, this.model.schema, castedDoc, options);
4122
4121
  }
4123
4122
 
4124
4123
  await _executePostHooks(this, null, null, 'validate');
@@ -4209,13 +4208,13 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
4209
4208
  if (typeof options === 'function') {
4210
4209
  // .update(conditions, doc, callback)
4211
4210
  callback = options;
4212
- options = null;
4211
+ options = undefined;
4213
4212
  } else if (typeof doc === 'function') {
4214
4213
  // .update(doc, callback);
4215
4214
  callback = doc;
4216
4215
  doc = conditions;
4217
4216
  conditions = {};
4218
- options = null;
4217
+ options = undefined;
4219
4218
  } else if (typeof conditions === 'function') {
4220
4219
  // .update(callback)
4221
4220
  callback = conditions;
@@ -4284,13 +4283,13 @@ Query.prototype.updateOne = function(conditions, doc, options, callback) {
4284
4283
  if (typeof options === 'function') {
4285
4284
  // .update(conditions, doc, callback)
4286
4285
  callback = options;
4287
- options = null;
4286
+ options = undefined;
4288
4287
  } else if (typeof doc === 'function') {
4289
4288
  // .update(doc, callback);
4290
4289
  callback = doc;
4291
4290
  doc = conditions;
4292
4291
  conditions = {};
4293
- options = null;
4292
+ options = undefined;
4294
4293
  } else if (typeof conditions === 'function') {
4295
4294
  // .update(callback)
4296
4295
  callback = conditions;
@@ -4351,13 +4350,13 @@ Query.prototype.replaceOne = function(conditions, doc, options, callback) {
4351
4350
  if (typeof options === 'function') {
4352
4351
  // .update(conditions, doc, callback)
4353
4352
  callback = options;
4354
- options = null;
4353
+ options = undefined;
4355
4354
  } else if (typeof doc === 'function') {
4356
4355
  // .update(doc, callback);
4357
4356
  callback = doc;
4358
4357
  doc = conditions;
4359
4358
  conditions = {};
4360
- options = null;
4359
+ options = undefined;
4361
4360
  } else if (typeof conditions === 'function') {
4362
4361
  // .update(callback)
4363
4362
  callback = conditions;
@@ -4389,7 +4388,6 @@ Query.prototype.replaceOne = function(conditions, doc, options, callback) {
4389
4388
  function _update(query, op, filter, doc, options, callback) {
4390
4389
  // make sure we don't send in the whole Document to merge()
4391
4390
  query.op = op;
4392
- query._validateOp();
4393
4391
  doc = doc || {};
4394
4392
 
4395
4393
  // strict is an option used in the update checking, make sure it gets set
@@ -4407,6 +4405,12 @@ function _update(query, op, filter, doc, options, callback) {
4407
4405
  query.merge(filter);
4408
4406
  }
4409
4407
 
4408
+ const updatePipeline = query?.model?.base?.options?.updatePipeline;
4409
+ if (updatePipeline != null && (options == null || options.updatePipeline == null)) {
4410
+ options = options || {};
4411
+ options.updatePipeline = updatePipeline;
4412
+ }
4413
+
4410
4414
  if (utils.isObject(options)) {
4411
4415
  query.setOptions(options);
4412
4416
  }
@@ -4574,6 +4578,7 @@ Query.prototype.exec = async function exec(op) {
4574
4578
  throw new MongooseError('Query.prototype.exec() no longer accepts a callback');
4575
4579
  }
4576
4580
 
4581
+ this._validateOp();
4577
4582
  if (typeof op === 'string') {
4578
4583
  this.op = op;
4579
4584
  }
@@ -4594,23 +4599,18 @@ Query.prototype.exec = async function exec(op) {
4594
4599
  throw new Error('Invalid field "" passed to sort()');
4595
4600
  }
4596
4601
 
4597
- if (this._executionStack != null) {
4602
+ if (this._execCount > 0) {
4598
4603
  let str = this.toString();
4599
4604
  if (str.length > 60) {
4600
4605
  str = str.slice(0, 60) + '...';
4601
4606
  }
4602
- const err = new MongooseError('Query was already executed: ' + str);
4603
- if (!this.model.base.options.skipOriginalStackTraces) {
4604
- err.originalStack = this._executionStack;
4605
- }
4606
- throw err;
4607
- } else {
4608
- this._executionStack = this.model.base.options.skipOriginalStackTraces ? true : new Error().stack;
4607
+ throw new MongooseError('Query was already executed: ' + str);
4609
4608
  }
4609
+ this._execCount++;
4610
4610
 
4611
4611
  let skipWrappedFunction = null;
4612
4612
  try {
4613
- await _executePreExecHooks(this);
4613
+ await this._hooks.execPre('exec', this, []);
4614
4614
  } catch (err) {
4615
4615
  if (err instanceof Kareem.skipWrappedFunction) {
4616
4616
  skipWrappedFunction = err;
@@ -4641,27 +4641,11 @@ Query.prototype.exec = async function exec(op) {
4641
4641
 
4642
4642
  res = await _executePostHooks(this, res, error);
4643
4643
 
4644
- await _executePostExecHooks(this);
4644
+ await this._hooks.execPost('exec', this, []);
4645
4645
 
4646
4646
  return res;
4647
4647
  };
4648
4648
 
4649
- /*!
4650
- * ignore
4651
- */
4652
-
4653
- function _executePostExecHooks(query) {
4654
- return new Promise((resolve, reject) => {
4655
- query._hooks.execPost('exec', query, [], {}, (error) => {
4656
- if (error) {
4657
- return reject(error);
4658
- }
4659
-
4660
- resolve();
4661
- });
4662
- });
4663
- }
4664
-
4665
4649
  /*!
4666
4650
  * ignore
4667
4651
  */
@@ -4674,31 +4658,10 @@ function _executePostHooks(query, res, error, op) {
4674
4658
  return res;
4675
4659
  }
4676
4660
 
4677
- return new Promise((resolve, reject) => {
4678
- const opts = error ? { error } : {};
4679
-
4680
- query._queryMiddleware.execPost(op || query.op, query, [res], opts, (error, res) => {
4681
- if (error) {
4682
- return reject(error);
4683
- }
4684
-
4685
- resolve(res);
4686
- });
4687
- });
4688
- }
4689
-
4690
- /*!
4691
- * ignore
4692
- */
4693
-
4694
- function _executePreExecHooks(query) {
4695
- return new Promise((resolve, reject) => {
4696
- query._hooks.execPre('exec', query, [], (error) => {
4697
- if (error != null) {
4698
- return reject(error);
4699
- }
4700
- resolve();
4701
- });
4661
+ const opts = error ? { error } : {};
4662
+ return query._queryMiddleware.execPost(op || query.op, query, [res], opts).then((res) => {
4663
+ // `res` is array of return args, but queries only return one result.
4664
+ return res[0];
4702
4665
  });
4703
4666
  }
4704
4667
 
@@ -4711,14 +4674,7 @@ function _executePreHooks(query, op) {
4711
4674
  return;
4712
4675
  }
4713
4676
 
4714
- return new Promise((resolve, reject) => {
4715
- query._queryMiddleware.execPre(op || query.op, query, [], (error) => {
4716
- if (error != null) {
4717
- return reject(error);
4718
- }
4719
- resolve();
4720
- });
4721
- });
4677
+ return query._queryMiddleware.execPre(op || query.op, query, []);
4722
4678
  }
4723
4679
 
4724
4680
  /**
@@ -5467,26 +5423,17 @@ Query.prototype.nearSphere = function() {
5467
5423
  * console.log(doc.name);
5468
5424
  * }
5469
5425
  *
5470
- * Node.js 10.x supports async iterators natively without any flags. You can
5471
- * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
5472
- *
5473
- * **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
5474
- * `Symbol.asyncIterator` is undefined, that means your Node.js version does not
5475
- * support async iterators.
5476
- *
5477
5426
  * @method [Symbol.asyncIterator]
5478
5427
  * @memberOf Query
5479
5428
  * @instance
5480
5429
  * @api public
5481
5430
  */
5482
5431
 
5483
- if (Symbol.asyncIterator != null) {
5484
- Query.prototype[Symbol.asyncIterator] = function queryAsyncIterator() {
5485
- // Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax
5486
- this._mongooseOptions._asyncIterator = true;
5487
- return this.cursor();
5488
- };
5489
- }
5432
+ Query.prototype[Symbol.asyncIterator] = function queryAsyncIterator() {
5433
+ // Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax
5434
+ this._mongooseOptions._asyncIterator = true;
5435
+ return this.cursor();
5436
+ };
5490
5437
 
5491
5438
  /**
5492
5439
  * Specifies a `$polygon` condition
@@ -5686,6 +5633,15 @@ Query.prototype.selectedExclusively = function selectedExclusively() {
5686
5633
 
5687
5634
  Query.prototype.model;
5688
5635
 
5636
+ /**
5637
+ * Determine if we can merge the given value as a query filter. Override for mquery.canMerge() to allow null
5638
+ */
5639
+
5640
+ function canMerge(value) {
5641
+ return value instanceof Query || utils.isObject(value) || value === null;
5642
+
5643
+ }
5644
+
5689
5645
  /*!
5690
5646
  * Export
5691
5647
  */
@@ -90,7 +90,7 @@ exports.createModel = function createModel(model, doc, fields, userProvidedField
90
90
  if (discriminator) {
91
91
  const _fields = clone(userProvidedFields);
92
92
  exports.applyPaths(_fields, discriminator.schema);
93
- return new discriminator(undefined, _fields, true);
93
+ return new discriminator(undefined, _fields, { skipId: true });
94
94
  }
95
95
  }
96
96
 
@@ -268,7 +268,7 @@ exports.applyPaths = function applyPaths(fields, schema, sanitizeProjection) {
268
268
  let addedPath = analyzePath(path, type);
269
269
  // arrays
270
270
  if (addedPath == null && !Array.isArray(type) && type.$isMongooseArray && !type.$isMongooseDocumentArray) {
271
- addedPath = analyzePath(path, type.caster);
271
+ addedPath = analyzePath(path, type.embeddedSchemaType);
272
272
  }
273
273
  if (addedPath != null) {
274
274
  addedPaths.push(addedPath);