mongoose 8.1.2 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/aggregate.js CHANGED
@@ -1019,8 +1019,8 @@ Aggregate.prototype.exec = async function exec() {
1019
1019
  const model = this._model;
1020
1020
  const collection = this._model.collection;
1021
1021
 
1022
- applyGlobalMaxTimeMS(this.options, model);
1023
- applyGlobalDiskUse(this.options, model);
1022
+ applyGlobalMaxTimeMS(this.options, model.db.options, model.base.options);
1023
+ applyGlobalDiskUse(this.options, model.db.options, model.base.options);
1024
1024
 
1025
1025
  if (this.options && this.options.cursor) {
1026
1026
  return new AggregationCursor(this);
package/lib/connection.js CHANGED
@@ -443,6 +443,28 @@ Connection.prototype.createCollections = async function createCollections(option
443
443
  return result;
444
444
  };
445
445
 
446
+ /**
447
+ * A convenience wrapper for `connection.client.withSession()`.
448
+ *
449
+ * #### Example:
450
+ *
451
+ * await conn.withSession(async session => {
452
+ * const doc = await TestModel.findOne().session(session);
453
+ * });
454
+ *
455
+ * @method withSession
456
+ * @param {Function} executor called with 1 argument: a `ClientSession` instance
457
+ * @return {Promise} resolves to the return value of the executor function
458
+ * @api public
459
+ */
460
+
461
+ Connection.prototype.withSession = async function withSession(executor) {
462
+ if (arguments.length === 0) {
463
+ throw new Error('Please provide an executor function');
464
+ }
465
+ return await this.client.withSession(executor);
466
+ };
467
+
446
468
  /**
447
469
  * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions)
448
470
  * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/),
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ /*!
4
+ * ignore
5
+ */
6
+
7
+ const queryOperations = Object.freeze([
8
+ // Read
9
+ 'countDocuments',
10
+ 'distinct',
11
+ 'estimatedDocumentCount',
12
+ 'find',
13
+ 'findOne',
14
+ // Update
15
+ 'findOneAndReplace',
16
+ 'findOneAndUpdate',
17
+ 'replaceOne',
18
+ 'updateMany',
19
+ 'updateOne',
20
+ // Delete
21
+ 'deleteMany',
22
+ 'deleteOne',
23
+ 'findOneAndDelete'
24
+ ]);
25
+
26
+ exports.queryOperations = queryOperations;
27
+
28
+ /*!
29
+ * ignore
30
+ */
31
+
32
+ const queryMiddlewareFunctions = queryOperations.concat([
33
+ 'validate'
34
+ ]);
35
+
36
+ exports.queryMiddlewareFunctions = queryMiddlewareFunctions;
@@ -57,12 +57,20 @@ util.inherits(AggregationCursor, Readable);
57
57
  function _init(model, c, agg) {
58
58
  if (!model.collection.buffer) {
59
59
  model.hooks.execPre('aggregate', agg, function() {
60
+ if (typeof agg.options?.cursor?.transform === 'function') {
61
+ c._transforms.push(agg.options.cursor.transform);
62
+ }
63
+
60
64
  c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
61
65
  c.emit('cursor', c.cursor);
62
66
  });
63
67
  } else {
64
68
  model.collection.emitter.once('queue', function() {
65
69
  model.hooks.execPre('aggregate', agg, function() {
70
+ if (typeof agg.options?.cursor?.transform === 'function') {
71
+ c._transforms.push(agg.options.cursor.transform);
72
+ }
73
+
66
74
  c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
67
75
  c.emit('cursor', c.cursor);
68
76
  });
package/lib/document.js CHANGED
@@ -694,7 +694,6 @@ Document.prototype.$__init = function(doc, opts) {
694
694
  init(this, doc, this._doc, opts);
695
695
 
696
696
  markArraySubdocsPopulated(this, opts.populated);
697
-
698
697
  this.$emit('init', this);
699
698
  this.constructor.emit('init', this);
700
699
 
@@ -703,7 +702,6 @@ Document.prototype.$__init = function(doc, opts) {
703
702
  null;
704
703
 
705
704
  applyDefaults(this, this.$__.selected, this.$__.exclude, hasIncludedChildren, false, this.$__.skipDefaults);
706
-
707
705
  return this;
708
706
  };
709
707
 
@@ -746,7 +744,6 @@ function init(self, obj, doc, opts, prefix) {
746
744
  }
747
745
  path = prefix + i;
748
746
  schemaType = docSchema.path(path);
749
-
750
747
  // Should still work if not a model-level discriminator, but should not be
751
748
  // necessary. This is *only* to catch the case where we queried using the
752
749
  // base model and the discriminated model has a projection
@@ -770,15 +767,14 @@ function init(self, obj, doc, opts, prefix) {
770
767
  }
771
768
  } else {
772
769
  // Retain order when overwriting defaults
773
- if (doc.hasOwnProperty(i) && obj[i] !== void 0) {
770
+ if (doc.hasOwnProperty(i) && obj[i] !== void 0 && !opts.hydratedPopulatedDocs) {
774
771
  delete doc[i];
775
772
  }
776
773
  if (obj[i] === null) {
777
774
  doc[i] = schemaType._castNullish(null);
778
775
  } else if (obj[i] !== undefined) {
779
776
  const wasPopulated = obj[i].$__ == null ? null : obj[i].$__.wasPopulated;
780
-
781
- if (schemaType && !wasPopulated) {
777
+ if ((schemaType && !wasPopulated) && !opts.hydratedPopulatedDocs) {
782
778
  try {
783
779
  if (opts && opts.setters) {
784
780
  // Call applySetters with `init = false` because otherwise setters are a noop
@@ -3901,14 +3897,24 @@ Document.prototype.$toObject = function(options, json) {
3901
3897
  *
3902
3898
  * _Note: if a transform function returns `undefined`, the return value will be ignored._
3903
3899
  *
3904
- * Transformations may also be applied inline, overridding any transform set in the options:
3900
+ * Transformations may also be applied inline, overridding any transform set in the schema options.
3901
+ * Any transform function specified in `toObject` options also propagates to any subdocuments.
3905
3902
  *
3906
- * function xform (doc, ret, options) {
3907
- * return { inline: ret.name, custom: true }
3903
+ * function deleteId(doc, ret, options) {
3904
+ * delete ret._id;
3905
+ * return ret;
3908
3906
  * }
3909
3907
  *
3910
- * // pass the transform as an inline option
3911
- * doc.toObject({ transform: xform }); // { inline: 'Wreck-it Ralph', custom: true }
3908
+ * const schema = mongoose.Schema({ name: String, docArr: [{ name: String }] });
3909
+ * const TestModel = mongoose.model('Test', schema);
3910
+ *
3911
+ * const doc = new TestModel({ name: 'test', docArr: [{ name: 'test' }] });
3912
+ *
3913
+ * // pass the transform as an inline option. Deletes `_id` property
3914
+ * // from both the top-level document and the subdocument.
3915
+ * const obj = doc.toObject({ transform: deleteId });
3916
+ * obj._id; // undefined
3917
+ * obj.docArr[0]._id; // undefined
3912
3918
  *
3913
3919
  * If you want to skip transformations, use `transform: false`:
3914
3920
  *
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const middlewareFunctions = require('../query/applyQueryMiddleware').middlewareFunctions;
3
+ const middlewareFunctions = require('../../constants').queryMiddlewareFunctions;
4
4
  const promiseOrCallback = require('../promiseOrCallback');
5
5
 
6
6
  module.exports = function applyStaticHooks(model, hooks, statics) {
@@ -494,7 +494,7 @@ function addModelNamesToMap(model, map, available, modelNames, options, data, re
494
494
 
495
495
  let k = modelNames.length;
496
496
  while (k--) {
497
- const modelName = modelNames[k];
497
+ let modelName = modelNames[k];
498
498
  if (modelName == null) {
499
499
  continue;
500
500
  }
@@ -504,6 +504,7 @@ function addModelNamesToMap(model, map, available, modelNames, options, data, re
504
504
  Model = options.model;
505
505
  } else if (modelName[modelSymbol]) {
506
506
  Model = modelName;
507
+ modelName = Model.modelName;
507
508
  } else {
508
509
  try {
509
510
  Model = _getModelFromConn(connection, modelName);
@@ -2,12 +2,12 @@
2
2
 
3
3
  const utils = require('../../utils');
4
4
 
5
- function applyGlobalMaxTimeMS(options, model) {
6
- applyGlobalOption(options, model, 'maxTimeMS');
5
+ function applyGlobalMaxTimeMS(options, connectionOptions, baseOptions) {
6
+ applyGlobalOption(options, connectionOptions, baseOptions, 'maxTimeMS');
7
7
  }
8
8
 
9
- function applyGlobalDiskUse(options, model) {
10
- applyGlobalOption(options, model, 'allowDiskUse');
9
+ function applyGlobalDiskUse(options, connectionOptions, baseOptions) {
10
+ applyGlobalOption(options, connectionOptions, baseOptions, 'allowDiskUse');
11
11
  }
12
12
 
13
13
  module.exports = {
@@ -16,14 +16,14 @@ module.exports = {
16
16
  };
17
17
 
18
18
 
19
- function applyGlobalOption(options, model, optionName) {
19
+ function applyGlobalOption(options, connectionOptions, baseOptions, optionName) {
20
20
  if (utils.hasUserDefinedProperty(options, optionName)) {
21
21
  return;
22
22
  }
23
23
 
24
- if (utils.hasUserDefinedProperty(model.db.options, optionName)) {
25
- options[optionName] = model.db.options[optionName];
26
- } else if (utils.hasUserDefinedProperty(model.base.options, optionName)) {
27
- options[optionName] = model.base.options[optionName];
24
+ if (utils.hasUserDefinedProperty(connectionOptions, optionName)) {
25
+ options[optionName] = connectionOptions[optionName];
26
+ } else if (utils.hasUserDefinedProperty(baseOptions, optionName)) {
27
+ options[optionName] = baseOptions[optionName];
28
28
  }
29
29
  }
@@ -2,8 +2,7 @@
2
2
 
3
3
  const isOperator = require('./isOperator');
4
4
 
5
- module.exports = function castFilterPath(query, schematype, val) {
6
- const ctx = query;
5
+ module.exports = function castFilterPath(ctx, schematype, val) {
7
6
  const any$conditionals = Object.keys(val).some(isOperator);
8
7
 
9
8
  if (!any$conditionals) {
@@ -1,20 +1,3 @@
1
1
  'use strict';
2
2
 
3
- module.exports = Object.freeze([
4
- // Read
5
- 'countDocuments',
6
- 'distinct',
7
- 'estimatedDocumentCount',
8
- 'find',
9
- 'findOne',
10
- // Update
11
- 'findOneAndReplace',
12
- 'findOneAndUpdate',
13
- 'replaceOne',
14
- 'updateMany',
15
- 'updateOne',
16
- // Delete
17
- 'deleteMany',
18
- 'deleteOne',
19
- 'findOneAndDelete'
20
- ]);
3
+ module.exports = require('../../constants').queryMiddlewareFunctions;
package/lib/model.js CHANGED
@@ -23,7 +23,6 @@ const VersionError = require('./error/version');
23
23
  const ParallelSaveError = require('./error/parallelSave');
24
24
  const applyDefaultsHelper = require('./helpers/document/applyDefaults');
25
25
  const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO');
26
- const applyQueryMiddleware = require('./helpers/query/applyQueryMiddleware');
27
26
  const applyHooks = require('./helpers/model/applyHooks');
28
27
  const applyMethods = require('./helpers/model/applyMethods');
29
28
  const applyProjection = require('./helpers/projection/applyProjection');
@@ -1415,6 +1414,18 @@ Model.createCollection = async function createCollection(options) {
1415
1414
  throw new MongooseError('Model.createCollection() no longer accepts a callback');
1416
1415
  }
1417
1416
 
1417
+ const shouldSkip = await new Promise((resolve, reject) => {
1418
+ this.hooks.execPre('createCollection', this, [options], (err) => {
1419
+ if (err != null) {
1420
+ if (err instanceof Kareem.skipWrappedFunction) {
1421
+ return resolve(true);
1422
+ }
1423
+ return reject(err);
1424
+ }
1425
+ resolve();
1426
+ });
1427
+ });
1428
+
1418
1429
  const collectionOptions = this &&
1419
1430
  this.schema &&
1420
1431
  this.schema.options &&
@@ -1468,13 +1479,32 @@ Model.createCollection = async function createCollection(options) {
1468
1479
  }
1469
1480
 
1470
1481
  try {
1471
- await this.db.createCollection(this.$__collection.collectionName, options);
1482
+ if (!shouldSkip) {
1483
+ await this.db.createCollection(this.$__collection.collectionName, options);
1484
+ }
1472
1485
  } catch (err) {
1473
-
1474
1486
  if (err != null && (err.name !== 'MongoServerError' || err.code !== 48)) {
1475
- throw err;
1487
+ await new Promise((resolve, reject) => {
1488
+ const _opts = { error: err };
1489
+ this.hooks.execPost('createCollection', this, [null], _opts, (err) => {
1490
+ if (err != null) {
1491
+ return reject(err);
1492
+ }
1493
+ resolve();
1494
+ });
1495
+ });
1476
1496
  }
1477
1497
  }
1498
+
1499
+ await new Promise((resolve, reject) => {
1500
+ this.hooks.execPost('createCollection', this, [this.$__collection], (err) => {
1501
+ if (err != null) {
1502
+ return reject(err);
1503
+ }
1504
+ resolve();
1505
+ });
1506
+ });
1507
+
1478
1508
  return this.$__collection;
1479
1509
  };
1480
1510
 
@@ -3428,44 +3458,62 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3428
3458
  throw new MongooseError('Model.bulkWrite() no longer accepts a callback');
3429
3459
  }
3430
3460
  options = options || {};
3461
+
3462
+ const shouldSkip = await new Promise((resolve, reject) => {
3463
+ this.hooks.execPre('bulkWrite', this, [ops, options], (err) => {
3464
+ if (err != null) {
3465
+ if (err instanceof Kareem.skipWrappedFunction) {
3466
+ return resolve(err);
3467
+ }
3468
+ return reject(err);
3469
+ }
3470
+ resolve();
3471
+ });
3472
+ });
3473
+
3474
+ if (shouldSkip) {
3475
+ return shouldSkip.args[0];
3476
+ }
3477
+
3431
3478
  const ordered = options.ordered == null ? true : options.ordered;
3432
3479
 
3480
+ if (ops.length === 0) {
3481
+ return getDefaultBulkwriteResult();
3482
+ }
3483
+
3433
3484
  const validations = ops.map(op => castBulkWrite(this, op, options));
3434
3485
 
3435
- return new Promise((resolve, reject) => {
3436
- if (ordered) {
3486
+ let res = null;
3487
+ if (ordered) {
3488
+ await new Promise((resolve, reject) => {
3437
3489
  each(validations, (fn, cb) => fn(cb), error => {
3438
3490
  if (error) {
3439
3491
  return reject(error);
3440
3492
  }
3441
3493
 
3442
- if (ops.length === 0) {
3443
- return resolve(getDefaultBulkwriteResult());
3444
- }
3445
-
3446
- try {
3447
- this.$__collection.bulkWrite(ops, options, (error, res) => {
3448
- if (error) {
3449
- return reject(error);
3450
- }
3451
-
3452
- resolve(res);
3453
- });
3454
- } catch (err) {
3455
- return reject(err);
3456
- }
3494
+ resolve();
3457
3495
  });
3496
+ });
3458
3497
 
3459
- return;
3498
+ try {
3499
+ res = await this.$__collection.bulkWrite(ops, options);
3500
+ } catch (error) {
3501
+ await new Promise((resolve, reject) => {
3502
+ const _opts = { error: error };
3503
+ this.hooks.execPost('bulkWrite', this, [null], _opts, (err) => {
3504
+ if (err != null) {
3505
+ return reject(err);
3506
+ }
3507
+ resolve();
3508
+ });
3509
+ });
3460
3510
  }
3461
-
3511
+ } else {
3462
3512
  let remaining = validations.length;
3463
3513
  let validOps = [];
3464
3514
  let validationErrors = [];
3465
3515
  const results = [];
3466
- if (remaining === 0) {
3467
- completeUnorderedValidation.call(this);
3468
- } else {
3516
+ await new Promise((resolve) => {
3469
3517
  for (let i = 0; i < validations.length; ++i) {
3470
3518
  validations[i]((err) => {
3471
3519
  if (err == null) {
@@ -3475,56 +3523,74 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3475
3523
  results[i] = err;
3476
3524
  }
3477
3525
  if (--remaining <= 0) {
3478
- completeUnorderedValidation.call(this);
3526
+ resolve();
3479
3527
  }
3480
3528
  });
3481
3529
  }
3482
- }
3530
+ });
3483
3531
 
3484
3532
  validationErrors = validationErrors.
3485
3533
  sort((v1, v2) => v1.index - v2.index).
3486
3534
  map(v => v.error);
3487
3535
 
3488
- function completeUnorderedValidation() {
3489
- const validOpIndexes = validOps;
3490
- validOps = validOps.sort().map(index => ops[index]);
3536
+ const validOpIndexes = validOps;
3537
+ validOps = validOps.sort().map(index => ops[index]);
3491
3538
 
3492
- if (validOps.length === 0) {
3493
- return resolve(getDefaultBulkwriteResult());
3494
- }
3539
+ if (validOps.length === 0) {
3540
+ return getDefaultBulkwriteResult();
3541
+ }
3495
3542
 
3496
- this.$__collection.bulkWrite(validOps, options, (error, res) => {
3497
- if (error) {
3498
- if (validationErrors.length > 0) {
3499
- error.mongoose = error.mongoose || {};
3500
- error.mongoose.validationErrors = validationErrors;
3501
- }
3543
+ let error;
3544
+ [res, error] = await this.$__collection.bulkWrite(validOps, options).
3545
+ then(res => ([res, null])).
3546
+ catch(err => ([null, err]));
3502
3547
 
3503
- return reject(error);
3504
- }
3548
+ if (error) {
3549
+ if (validationErrors.length > 0) {
3550
+ error.mongoose = error.mongoose || {};
3551
+ error.mongoose.validationErrors = validationErrors;
3552
+ }
3505
3553
 
3506
- for (let i = 0; i < validOpIndexes.length; ++i) {
3507
- results[validOpIndexes[i]] = null;
3508
- }
3509
- if (validationErrors.length > 0) {
3510
- if (options.throwOnValidationError) {
3511
- return reject(new MongooseBulkWriteError(
3512
- validationErrors,
3513
- results,
3514
- res,
3515
- 'bulkWrite'
3516
- ));
3517
- } else {
3518
- res.mongoose = res.mongoose || {};
3519
- res.mongoose.validationErrors = validationErrors;
3520
- res.mongoose.results = results;
3554
+ await new Promise((resolve, reject) => {
3555
+ const _opts = { error: error };
3556
+ this.hooks.execPost('bulkWrite', this, [null], _opts, (err) => {
3557
+ if (err != null) {
3558
+ return reject(err);
3521
3559
  }
3522
- }
3523
-
3524
- resolve(res);
3560
+ resolve();
3561
+ });
3525
3562
  });
3526
3563
  }
3564
+
3565
+ for (let i = 0; i < validOpIndexes.length; ++i) {
3566
+ results[validOpIndexes[i]] = null;
3567
+ }
3568
+ if (validationErrors.length > 0) {
3569
+ if (options.throwOnValidationError) {
3570
+ throw new MongooseBulkWriteError(
3571
+ validationErrors,
3572
+ results,
3573
+ res,
3574
+ 'bulkWrite'
3575
+ );
3576
+ } else {
3577
+ res.mongoose = res.mongoose || {};
3578
+ res.mongoose.validationErrors = validationErrors;
3579
+ res.mongoose.results = results;
3580
+ }
3581
+ }
3582
+ }
3583
+
3584
+ await new Promise((resolve, reject) => {
3585
+ this.hooks.execPost('bulkWrite', this, [res], (err) => {
3586
+ if (err != null) {
3587
+ return reject(err);
3588
+ }
3589
+ resolve();
3590
+ });
3527
3591
  });
3592
+
3593
+ return res;
3528
3594
  };
3529
3595
 
3530
3596
  /**
@@ -3830,6 +3896,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
3830
3896
  * @param {Object|String|String[]} [projection] optional projection containing which fields should be selected for this document
3831
3897
  * @param {Object} [options] optional options
3832
3898
  * @param {Boolean} [options.setters=false] if true, apply schema setters when hydrating
3899
+ * @param {Boolean} [options.hydratedPopulatedDocs=false] if true, populates the docs if passing pre-populated data
3833
3900
  * @return {Document} document instance
3834
3901
  * @api public
3835
3902
  */
@@ -3843,7 +3910,6 @@ Model.hydrate = function(obj, projection, options) {
3843
3910
  }
3844
3911
  obj = applyProjection(obj, projection);
3845
3912
  }
3846
-
3847
3913
  const document = require('./queryHelpers').createModel(this, obj, projection);
3848
3914
  document.$init(obj, options);
3849
3915
  return document;
@@ -4774,7 +4840,7 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4774
4840
  Object.setPrototypeOf(model.Query.prototype, Query.prototype);
4775
4841
  model.Query.base = Query.base;
4776
4842
  model.Query.prototype.constructor = Query;
4777
- applyQueryMiddleware(model.Query, model);
4843
+ model._applyQueryMiddleware();
4778
4844
  applyQueryMethods(model, schema.query);
4779
4845
 
4780
4846
  return model;
@@ -4861,6 +4927,35 @@ Model.__subclass = function subclass(conn, schema, collection) {
4861
4927
  return Model;
4862
4928
  };
4863
4929
 
4930
+ /**
4931
+ * Apply changes made to this model's schema after this model was compiled.
4932
+ * By default, adding virtuals and other properties to a schema after the model is compiled does nothing.
4933
+ * Call this function to apply virtuals and properties that were added later.
4934
+ *
4935
+ * #### Example:
4936
+ *
4937
+ * const schema = new mongoose.Schema({ field: String });
4938
+ * const TestModel = mongoose.model('Test', schema);
4939
+ * TestModel.schema.virtual('myVirtual').get(function() {
4940
+ * return this.field + ' from myVirtual';
4941
+ * });
4942
+ * const doc = new TestModel({ field: 'Hello' });
4943
+ * doc.myVirtual; // undefined
4944
+ *
4945
+ * TestModel.recompileSchema();
4946
+ * doc.myVirtual; // 'Hello from myVirtual'
4947
+ *
4948
+ * @return {undefined}
4949
+ * @api public
4950
+ * @memberOf Model
4951
+ * @static
4952
+ * @method recompileSchema
4953
+ */
4954
+
4955
+ Model.recompileSchema = function recompileSchema() {
4956
+ this.prototype.$__setSchema(this.schema);
4957
+ };
4958
+
4864
4959
  /**
4865
4960
  * Helper for console.log. Given a model named 'MyModel', returns the string
4866
4961
  * `'Model { MyModel }'`.
@@ -4883,6 +4978,41 @@ if (util.inspect.custom) {
4883
4978
  Model[util.inspect.custom] = Model.inspect;
4884
4979
  }
4885
4980
 
4981
+ /*!
4982
+ * Applies query middleware from this model's schema to this model's
4983
+ * Query constructor.
4984
+ */
4985
+
4986
+ Model._applyQueryMiddleware = function _applyQueryMiddleware() {
4987
+ const Query = this.Query;
4988
+ const queryMiddleware = this.schema.s.hooks.filter(hook => {
4989
+ const contexts = _getContexts(hook);
4990
+ if (hook.name === 'validate') {
4991
+ return !!contexts.query;
4992
+ }
4993
+ if (hook.name === 'deleteOne' || hook.name === 'updateOne') {
4994
+ return !!contexts.query || Object.keys(contexts).length === 0;
4995
+ }
4996
+ if (hook.query != null || hook.document != null) {
4997
+ return !!hook.query;
4998
+ }
4999
+ return true;
5000
+ });
5001
+
5002
+ Query.prototype._queryMiddleware = queryMiddleware;
5003
+ };
5004
+
5005
+ function _getContexts(hook) {
5006
+ const ret = {};
5007
+ if (hook.hasOwnProperty('query')) {
5008
+ ret.query = hook.query;
5009
+ }
5010
+ if (hook.hasOwnProperty('document')) {
5011
+ ret.document = hook.document;
5012
+ }
5013
+ return ret;
5014
+ }
5015
+
4886
5016
  /*!
4887
5017
  * Module exports.
4888
5018
  */
package/lib/options.js CHANGED
@@ -11,5 +11,6 @@ exports.internalToObjectOptions = {
11
11
  _skipDepopulateTopLevel: true,
12
12
  depopulate: true,
13
13
  flattenDecimals: false,
14
- useProjection: false
14
+ useProjection: false,
15
+ versionKey: true
15
16
  };
@@ -85,7 +85,7 @@ function mergeAtomics(destination, source) {
85
85
  destination.$addToSet = (destination.$addToSet || []).concat(source.$addToSet);
86
86
  }
87
87
  if (source.$set != null) {
88
- destination.$set = Object.assign(destination.$set || {}, source.$set);
88
+ destination.$set = Array.isArray(source.$set) ? [...source.$set] : Object.assign({}, source.$set);
89
89
  }
90
90
 
91
91
  return destination;