mongoose 6.2.7 → 6.2.10

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 (59) hide show
  1. package/.eslintrc.json +35 -26
  2. package/CHANGELOG.md +27 -0
  3. package/dist/browser.umd.js +71 -71
  4. package/lib/aggregate.js +36 -36
  5. package/lib/browser.js +4 -4
  6. package/lib/browserDocument.js +1 -0
  7. package/lib/connection.js +21 -21
  8. package/lib/cursor/AggregationCursor.js +2 -3
  9. package/lib/cursor/QueryCursor.js +3 -3
  10. package/lib/document.js +75 -47
  11. package/lib/error/index.js +2 -2
  12. package/lib/helpers/document/handleSpreadDoc.js +19 -1
  13. package/lib/helpers/populate/markArraySubdocsPopulated.js +1 -1
  14. package/lib/helpers/projection/hasIncludedChildren.js +1 -1
  15. package/lib/helpers/query/castFilterPath.js +0 -1
  16. package/lib/index.js +24 -26
  17. package/lib/model.js +141 -136
  18. package/lib/options/SchemaArrayOptions.js +2 -2
  19. package/lib/options/SchemaBufferOptions.js +1 -1
  20. package/lib/options/SchemaDateOptions.js +9 -2
  21. package/lib/options/SchemaDocumentArrayOptions.js +3 -3
  22. package/lib/options/SchemaMapOptions.js +2 -2
  23. package/lib/options/SchemaNumberOptions.js +3 -3
  24. package/lib/options/SchemaObjectIdOptions.js +2 -2
  25. package/lib/options/SchemaStringOptions.js +1 -1
  26. package/lib/options/SchemaSubdocumentOptions.js +2 -2
  27. package/lib/options/SchemaTypeOptions.js +3 -3
  28. package/lib/query.js +253 -225
  29. package/lib/schema/SubdocumentPath.js +6 -3
  30. package/lib/schema/array.js +3 -2
  31. package/lib/schema/boolean.js +4 -4
  32. package/lib/schema/buffer.js +3 -3
  33. package/lib/schema/date.js +7 -7
  34. package/lib/schema/decimal128.js +2 -2
  35. package/lib/schema/documentarray.js +17 -10
  36. package/lib/schema/mixed.js +2 -2
  37. package/lib/schema/number.js +6 -6
  38. package/lib/schema/objectid.js +4 -4
  39. package/lib/schema/string.js +14 -14
  40. package/lib/schema.js +28 -28
  41. package/lib/schematype.js +78 -68
  42. package/lib/types/ArraySubdocument.js +1 -1
  43. package/lib/types/DocumentArray/methods/index.js +2 -2
  44. package/lib/types/array/index.js +1 -1
  45. package/lib/types/array/methods/index.js +14 -12
  46. package/lib/types/buffer.js +1 -1
  47. package/lib/types/decimal128.js +1 -1
  48. package/lib/types/objectid.js +1 -1
  49. package/lib/types/subdocument.js +2 -2
  50. package/lib/virtualtype.js +4 -3
  51. package/package.json +18 -17
  52. package/tools/repl.js +8 -8
  53. package/tools/sharded.js +3 -3
  54. package/types/connection.d.ts +116 -116
  55. package/types/document.d.ts +3 -0
  56. package/types/error.d.ts +2 -2
  57. package/types/index.d.ts +75 -67
  58. package/types/pipelinestage.d.ts +194 -194
  59. package/types/schemaoptions.d.ts +2 -2
package/lib/document.js CHANGED
@@ -156,9 +156,9 @@ function Document(obj, fields, skipId, options) {
156
156
  if (obj) {
157
157
  // Skip set hooks
158
158
  if (this.$__original_set) {
159
- this.$__original_set(obj, undefined, true);
159
+ this.$__original_set(obj, undefined, true, options);
160
160
  } else {
161
- this.$set(obj, undefined, true);
161
+ this.$set(obj, undefined, true, options);
162
162
  }
163
163
 
164
164
  if (obj instanceof Document) {
@@ -268,7 +268,7 @@ Document.prototype.schema;
268
268
  * is handy for passing data to middleware without conflicting with Mongoose
269
269
  * internals.
270
270
  *
271
- * ####Example:
271
+ * #### Example:
272
272
  *
273
273
  * schema.pre('save', function() {
274
274
  * // Mongoose will set `isNew` to `false` if `save()` succeeds
@@ -326,7 +326,7 @@ Document.prototype.isNew;
326
326
  /**
327
327
  * Set this property to add additional query filters when Mongoose saves this document and `isNew` is false.
328
328
  *
329
- * ####Example:
329
+ * #### Example:
330
330
  *
331
331
  * // Make sure `save()` never updates a soft deleted document.
332
332
  * schema.pre('save', function() {
@@ -348,7 +348,7 @@ Object.defineProperty(Document.prototype, '$where', {
348
348
  /**
349
349
  * The string version of this documents _id.
350
350
  *
351
- * ####Note:
351
+ * #### Note:
352
352
  *
353
353
  * This getter exists on all documents by default. The getter can be disabled by setting the `id` [option](/docs/guide.html#id) of its `Schema` to false at construction time.
354
354
  *
@@ -389,7 +389,7 @@ Document.prototype.errors;
389
389
  * A string containing the current operation that Mongoose is executing
390
390
  * on this document. May be `null`, `'save'`, `'validate'`, or `'remove'`.
391
391
  *
392
- * ####Example:
392
+ * #### Example:
393
393
  *
394
394
  * const doc = new Model({ name: 'test' });
395
395
  * doc.$op; // null
@@ -801,7 +801,7 @@ function init(self, obj, doc, opts, prefix) {
801
801
  init(self, obj[i], doc[i], opts, path + '.');
802
802
  } else if (!schemaType) {
803
803
  doc[i] = obj[i];
804
- if (!strict) {
804
+ if (!strict && !prefix) {
805
805
  self[i] = obj[i];
806
806
  }
807
807
  } else {
@@ -841,11 +841,11 @@ function init(self, obj, doc, opts, prefix) {
841
841
  /**
842
842
  * Sends an update command with this document `_id` as the query selector.
843
843
  *
844
- * ####Example:
844
+ * #### Example:
845
845
  *
846
846
  * weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
847
847
  *
848
- * ####Valid options:
848
+ * #### Valid options:
849
849
  *
850
850
  * - same as in [Model.update](#model_Model.update)
851
851
  *
@@ -876,11 +876,11 @@ Document.prototype.update = function update() {
876
876
  /**
877
877
  * Sends an updateOne command with this document `_id` as the query selector.
878
878
  *
879
- * ####Example:
879
+ * #### Example:
880
880
  *
881
881
  * weirdCar.updateOne({$inc: {wheels:1}}, { w: 1 }, callback);
882
882
  *
883
- * ####Valid options:
883
+ * #### Valid options:
884
884
  *
885
885
  * - same as in [Model.updateOne](#model_Model.updateOne)
886
886
  *
@@ -922,7 +922,7 @@ Document.prototype.updateOne = function updateOne(doc, options, callback) {
922
922
  /**
923
923
  * Sends a replaceOne command with this document `_id` as the query selector.
924
924
  *
925
- * ####Valid options:
925
+ * #### Valid options:
926
926
  *
927
927
  * - same as in [Model.replaceOne](https://mongoosejs.com/docs/api/model.html#model_Model.replaceOne)
928
928
  *
@@ -947,7 +947,7 @@ Document.prototype.replaceOne = function replaceOne() {
947
947
  * automatically set `session` if you `save()` a doc that you got from a
948
948
  * query with an associated session.
949
949
  *
950
- * ####Example:
950
+ * #### Example:
951
951
  *
952
952
  * const session = MyModel.startSession();
953
953
  * const doc = await MyModel.findOne().session(session);
@@ -1514,7 +1514,7 @@ function _isManuallyPopulatedArray(val, ref) {
1514
1514
  /**
1515
1515
  * Sets the value of a path, or many paths.
1516
1516
  *
1517
- * ####Example:
1517
+ * #### Example:
1518
1518
  *
1519
1519
  * // path, value
1520
1520
  * doc.set(path, value)
@@ -1718,7 +1718,7 @@ Document.prototype.$__setValue = function(path, val) {
1718
1718
  /**
1719
1719
  * Returns the value of a path.
1720
1720
  *
1721
- * ####Example
1721
+ * #### Example
1722
1722
  *
1723
1723
  * // path
1724
1724
  * doc.get('age') // 47
@@ -1824,7 +1824,7 @@ Document.prototype.$__path = function(path) {
1824
1824
  *
1825
1825
  * _Very helpful when using [Mixed](https://mongoosejs.com/docs/schematypes.html#mixed) types._
1826
1826
  *
1827
- * ####Example:
1827
+ * #### Example:
1828
1828
  *
1829
1829
  * doc.mixed.type = 'changed';
1830
1830
  * doc.markModified('mixed.type');
@@ -1836,6 +1836,7 @@ Document.prototype.$__path = function(path) {
1836
1836
  */
1837
1837
 
1838
1838
  Document.prototype.markModified = function(path, scope) {
1839
+ // console.log('MarkModified', path, new Error().stack);
1839
1840
  this.$__.activePaths.modify(path);
1840
1841
  if (scope != null && !this.$isSubdocument) {
1841
1842
  this.$__.pathsToScopes = this.$__pathsToScopes || {};
@@ -1846,7 +1847,7 @@ Document.prototype.markModified = function(path, scope) {
1846
1847
  /**
1847
1848
  * Clears the modified state on the specified path.
1848
1849
  *
1849
- * ####Example:
1850
+ * #### Example:
1850
1851
  *
1851
1852
  * doc.foo = 'bar';
1852
1853
  * doc.unmarkModified('foo');
@@ -1866,7 +1867,7 @@ Document.prototype.unmarkModified = function(path) {
1866
1867
  /**
1867
1868
  * Don't run validation on this path or persist changes to this path.
1868
1869
  *
1869
- * ####Example:
1870
+ * #### Example:
1870
1871
  *
1871
1872
  * doc.foo = null;
1872
1873
  * doc.$ignore('foo');
@@ -1891,7 +1892,7 @@ Document.prototype.$ignore = function(path) {
1891
1892
  * A path `a` may be in `modifiedPaths()` but not in `directModifiedPaths()`
1892
1893
  * because a child of `a` was directly modified.
1893
1894
  *
1894
- * ####Example
1895
+ * #### Example
1895
1896
  * const schema = new Schema({ foo: String, nested: { bar: String } });
1896
1897
  * const Model = mongoose.model('Test', schema);
1897
1898
  * await Model.create({ foo: 'original', nested: { bar: 'original' } });
@@ -1914,7 +1915,7 @@ Document.prototype.directModifiedPaths = function() {
1914
1915
  * Useful for determining whether this subdoc will get stripped out by the
1915
1916
  * [minimize option](/docs/guide.html#minimize).
1916
1917
  *
1917
- * ####Example:
1918
+ * #### Example:
1918
1919
  * const schema = new Schema({ nested: { foo: String } });
1919
1920
  * const Model = mongoose.model('Test', schema);
1920
1921
  * const doc = new Model({});
@@ -2047,7 +2048,7 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
2047
2048
  *
2048
2049
  * If `path` is given, checks if a path or any full path containing `path` as part of its path chain has been modified.
2049
2050
  *
2050
- * ####Example
2051
+ * #### Example
2051
2052
  *
2052
2053
  * doc.set('documents.0.title', 'changed');
2053
2054
  * doc.isModified() // true
@@ -2093,7 +2094,7 @@ Document.prototype[documentIsModified] = Document.prototype.isModified;
2093
2094
  /**
2094
2095
  * Checks if a path is set to its default.
2095
2096
  *
2096
- * ####Example
2097
+ * #### Example
2097
2098
  *
2098
2099
  * MyModel = mongoose.model('test', { name: { type: String, default: 'Val '} });
2099
2100
  * const m = new MyModel();
@@ -2127,7 +2128,7 @@ Document.prototype.$isDefault = function(path) {
2127
2128
  /**
2128
2129
  * Getter/setter, determines whether the document was removed or not.
2129
2130
  *
2130
- * ####Example:
2131
+ * #### Example:
2131
2132
  * const product = await product.remove();
2132
2133
  * product.$isDeleted(); // true
2133
2134
  * product.remove(); // no-op, doesn't send anything to the db
@@ -2157,7 +2158,7 @@ Document.prototype.$isDeleted = function(val) {
2157
2158
  /**
2158
2159
  * Returns true if `path` was directly set and modified, else false.
2159
2160
  *
2160
- * ####Example
2161
+ * #### Example
2161
2162
  *
2162
2163
  * doc.set('documents.0.title', 'changed');
2163
2164
  * doc.isDirectModified('documents.0.title') // true
@@ -2213,7 +2214,7 @@ Document.prototype.isInit = function(path) {
2213
2214
  /**
2214
2215
  * Checks if `path` was selected in the source query which initialized this document.
2215
2216
  *
2216
- * ####Example
2217
+ * #### Example
2217
2218
  *
2218
2219
  * const doc = await Thing.findOne().select('name');
2219
2220
  * doc.isSelected('name') // true
@@ -2294,7 +2295,7 @@ Document.prototype.$__isSelected = Document.prototype.isSelected;
2294
2295
  * Checks if `path` was explicitly selected. If no projection, always returns
2295
2296
  * true.
2296
2297
  *
2297
- * ####Example
2298
+ * #### Example
2298
2299
  *
2299
2300
  * Thing.findOne().select('nested.name').exec(function (err, doc) {
2300
2301
  * doc.isDirectSelected('nested.name') // true
@@ -2356,11 +2357,11 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
2356
2357
  /**
2357
2358
  * Executes registered validation rules for this document.
2358
2359
  *
2359
- * ####Note:
2360
+ * #### Note:
2360
2361
  *
2361
2362
  * This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned to your `callback`.
2362
2363
  *
2363
- * ####Example:
2364
+ * #### Example:
2364
2365
  *
2365
2366
  * doc.validate(function (err) {
2366
2367
  * if (err) handleError(err);
@@ -2422,6 +2423,7 @@ Document.prototype.validate = function(pathsToValidate, options, callback) {
2422
2423
 
2423
2424
  this.$__validate(pathsToValidate, options, (error) => {
2424
2425
  this.$op = null;
2426
+ this.$__.validating = null;
2425
2427
  cb(error);
2426
2428
  });
2427
2429
  }, this.constructor.events);
@@ -2472,7 +2474,6 @@ function _getPathsToValidate(doc) {
2472
2474
  return true;
2473
2475
  }));
2474
2476
 
2475
-
2476
2477
  Object.keys(doc.$__.activePaths.states.init).forEach(addToPaths);
2477
2478
  Object.keys(doc.$__.activePaths.states.modify).forEach(addToPaths);
2478
2479
  Object.keys(doc.$__.activePaths.states.default).forEach(addToPaths);
@@ -2484,28 +2485,53 @@ function _getPathsToValidate(doc) {
2484
2485
  if (subdoc.$basePath) {
2485
2486
  // Remove child paths for now, because we'll be validating the whole
2486
2487
  // subdoc
2488
+ if (!subdoc.$__.fullPath) {
2489
+ subdoc.ownerDocument();
2490
+ }
2491
+ const fullPathToSubdoc = subdoc.$__.fullPath;
2492
+
2487
2493
  for (const p of paths) {
2488
- if (p === null || p.startsWith(subdoc.$basePath + '.')) {
2494
+ if (p === null || p.startsWith(fullPathToSubdoc + '.')) {
2489
2495
  paths.delete(p);
2490
2496
  }
2491
2497
  }
2492
2498
 
2493
- if (doc.$isModified(subdoc.$basePath, modifiedPaths) &&
2494
- !doc.isDirectModified(subdoc.$basePath) &&
2495
- !doc.$isDefault(subdoc.$basePath)) {
2496
- paths.add(subdoc.$basePath);
2499
+ if (doc.$isModified(fullPathToSubdoc, modifiedPaths) &&
2500
+ !doc.isDirectModified(fullPathToSubdoc) &&
2501
+ !doc.$isDefault(fullPathToSubdoc)) {
2502
+ paths.add(fullPathToSubdoc);
2497
2503
 
2498
- skipSchemaValidators[subdoc.$basePath] = true;
2504
+ skipSchemaValidators[fullPathToSubdoc] = true;
2499
2505
  }
2500
2506
  }
2501
2507
  }
2502
2508
 
2509
+ for (const path of paths) {
2510
+ const _pathType = doc.$__schema.path(path);
2511
+ if (!_pathType) {
2512
+ continue;
2513
+ }
2514
+
2515
+ // Optimization: if primitive path with no validators, or array of primitives
2516
+ // with no validators, skip validating this path entirely.
2517
+ if (!_pathType.caster && _pathType.validators.length === 0) {
2518
+ paths.delete(path);
2519
+ } else if (_pathType.$isMongooseArray &&
2520
+ !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2521
+ !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2522
+ _pathType.validators.length === 0 && // and arrays with top-level validators
2523
+ _pathType.$embeddedSchemaType.validators.length === 0) {
2524
+ paths.delete(path);
2525
+ }
2526
+ }
2527
+
2503
2528
  // from here on we're not removing items from paths
2504
2529
 
2505
2530
  // gh-661: if a whole array is modified, make sure to run validation on all
2506
2531
  // the children as well
2507
2532
  for (const path of paths) {
2508
2533
  const _pathType = doc.$__schema.path(path);
2534
+
2509
2535
  if (!_pathType ||
2510
2536
  !_pathType.$isMongooseArray ||
2511
2537
  // To avoid potential performance issues, skip doc arrays whose children
@@ -2612,7 +2638,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2612
2638
  const _this = this;
2613
2639
  const _complete = () => {
2614
2640
  let validationError = this.$__.validationError;
2615
- this.$__.validationError = undefined;
2641
+ this.$__.validationError = null;
2642
+ this.$__.validating = null;
2616
2643
 
2617
2644
  if (shouldValidateModifiedOnly && validationError != null) {
2618
2645
  // Remove any validation errors that aren't from modified paths
@@ -2658,6 +2685,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2658
2685
  } else if (pathsToSkip) {
2659
2686
  paths = _handlePathsToSkip(paths, pathsToSkip);
2660
2687
  }
2688
+
2661
2689
  if (paths.length === 0) {
2662
2690
  return immediate(function() {
2663
2691
  const error = _complete();
@@ -2798,11 +2826,11 @@ function _handlePathsToSkip(paths, pathsToSkip) {
2798
2826
  /**
2799
2827
  * Executes registered validation rules (skipping asynchronous validators) for this document.
2800
2828
  *
2801
- * ####Note:
2829
+ * #### Note:
2802
2830
  *
2803
2831
  * This method is useful if you need synchronous validation.
2804
2832
  *
2805
- * ####Example:
2833
+ * #### Example:
2806
2834
  *
2807
2835
  * const err = doc.validateSync();
2808
2836
  * if (err) {
@@ -3037,7 +3065,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3037
3065
  * Saves this document by inserting a new document into the database if [document.isNew](/docs/api.html#document_Document-isNew) is `true`,
3038
3066
  * or sends an [updateOne](/docs/api.html#document_Document-updateOne) operation **only** with the modifications to the database, it does not replace the whole document in the latter case.
3039
3067
  *
3040
- * ####Example:
3068
+ * #### Example:
3041
3069
  *
3042
3070
  * product.sold = Date.now();
3043
3071
  * product = await product.save();
@@ -3045,7 +3073,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3045
3073
  * If save is successful, the returned promise will fulfill with the document
3046
3074
  * saved.
3047
3075
  *
3048
- * ####Example:
3076
+ * #### Example:
3049
3077
  *
3050
3078
  * const newProduct = await product.save();
3051
3079
  * newProduct === product; // true
@@ -3573,7 +3601,7 @@ Document.prototype.$toObject = function(options, json) {
3573
3601
  *
3574
3602
  * Buffers are converted to instances of [mongodb.Binary](https://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
3575
3603
  *
3576
- * ####Options:
3604
+ * #### Options:
3577
3605
  *
3578
3606
  * - `getters` apply all getters (path and virtual getters), defaults to false
3579
3607
  * - `aliases` apply all aliases if `virtuals=true`, defaults to true
@@ -3585,7 +3613,7 @@ Document.prototype.$toObject = function(options, json) {
3585
3613
  * - `flattenMaps` convert Maps to POJOs. Useful if you want to JSON.stringify() the result of toObject(), defaults to false
3586
3614
  * - `useProjection` set to `true` to omit fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema.
3587
3615
  *
3588
- * ####Getters/Virtuals
3616
+ * #### Getters/Virtuals
3589
3617
  *
3590
3618
  * Example of only applying path getters
3591
3619
  *
@@ -3603,7 +3631,7 @@ Document.prototype.$toObject = function(options, json) {
3603
3631
  *
3604
3632
  * schema.set('toObject', { virtuals: true })
3605
3633
  *
3606
- * ####Transform
3634
+ * #### Transform
3607
3635
  *
3608
3636
  * We may need to perform a transformation of the resulting object based on some criteria, say to remove some sensitive information or return a custom object. In this case we set the optional `transform` function.
3609
3637
  *
@@ -3615,7 +3643,7 @@ Document.prototype.$toObject = function(options, json) {
3615
3643
  * - `ret` The plain object representation which has been converted
3616
3644
  * - `options` The options in use (either schema options or the options passed inline)
3617
3645
  *
3618
- * ####Example
3646
+ * #### Example
3619
3647
  *
3620
3648
  * // specify the transform schema option
3621
3649
  * if (!schema.options.toObject) schema.options.toObject = {};
@@ -4109,7 +4137,7 @@ Document.prototype.equals = function(doc) {
4109
4137
  /**
4110
4138
  * Populates paths on an existing document.
4111
4139
  *
4112
- * ####Example:
4140
+ * #### Example:
4113
4141
  *
4114
4142
  * await doc.populate([
4115
4143
  * 'stories',
@@ -4226,7 +4254,7 @@ Document.prototype.$getPopulatedDocs = function $getPopulatedDocs() {
4226
4254
  /**
4227
4255
  * Gets _id(s) used during population of the given `path`.
4228
4256
  *
4229
- * ####Example:
4257
+ * #### Example:
4230
4258
  *
4231
4259
  * Model.findOne().populate('author').exec(function (err, doc) {
4232
4260
  * console.log(doc.author.name) // Dr.Seuss
@@ -4288,7 +4316,7 @@ Document.prototype.$populated = Document.prototype.populated;
4288
4316
  /**
4289
4317
  * Takes a populated field and returns it to its unpopulated state.
4290
4318
  *
4291
- * ####Example:
4319
+ * #### Example:
4292
4320
  *
4293
4321
  * Model.findOne().populate('author').exec(function (err, doc) {
4294
4322
  * console.log(doc.author.name); // Dr.Seuss
@@ -4,7 +4,7 @@
4
4
  * MongooseError constructor. MongooseError is the base class for all
5
5
  * Mongoose-specific errors.
6
6
  *
7
- * ####Example:
7
+ * #### Example:
8
8
  * const Model = mongoose.model('Test', new Schema({ answer: Number }));
9
9
  * const doc = new Model({ answer: 'not a number' });
10
10
  * const err = doc.validateSync();
@@ -105,7 +105,7 @@ MongooseError.ValidationError = require('./validation');
105
105
  * A `ValidationError` has a hash of `errors` that contain individual
106
106
  * `ValidatorError` instances.
107
107
  *
108
- * ####Example:
108
+ * #### Example:
109
109
  *
110
110
  * const schema = Schema({ name: { type: String, required: true } });
111
111
  * const Model = mongoose.model('Test', schema);
@@ -2,14 +2,32 @@
2
2
 
3
3
  const utils = require('../../utils');
4
4
 
5
+ const keysToSkip = new Set(['__index', '__parentArray', '_doc']);
6
+
5
7
  /**
6
8
  * Using spread operator on a Mongoose document gives you a
7
9
  * POJO that has a tendency to cause infinite recursion. So
8
10
  * we use this function on `set()` to prevent that.
9
11
  */
10
12
 
11
- module.exports = function handleSpreadDoc(v) {
13
+ module.exports = function handleSpreadDoc(v, includeExtraKeys) {
12
14
  if (utils.isPOJO(v) && v.$__ != null && v._doc != null) {
15
+ if (includeExtraKeys) {
16
+ const extraKeys = {};
17
+ for (const key of Object.keys(v)) {
18
+ if (typeof key === 'symbol') {
19
+ continue;
20
+ }
21
+ if (key[0] === '$') {
22
+ continue;
23
+ }
24
+ if (keysToSkip.has(key)) {
25
+ continue;
26
+ }
27
+ extraKeys[key] = v[key];
28
+ }
29
+ return { ...v._doc, ...extraKeys };
30
+ }
13
31
  return v._doc;
14
32
  }
15
33
 
@@ -6,7 +6,7 @@ const utils = require('../../utils');
6
6
  * If populating a path within a document array, make sure each
7
7
  * subdoc within the array knows its subpaths are populated.
8
8
  *
9
- * ####Example:
9
+ * #### Example:
10
10
  * const doc = await Article.findOne().populate('comments.author');
11
11
  * doc.comments[0].populated('author'); // Should be set
12
12
  */
@@ -4,7 +4,7 @@
4
4
  * Creates an object that precomputes whether a given path has child fields in
5
5
  * the projection.
6
6
  *
7
- * ####Example:
7
+ * #### Example:
8
8
  * const res = hasIncludedChildren({ 'a.b.c': 0 });
9
9
  * res.a; // 1
10
10
  * res['a.b']; // 1
@@ -41,7 +41,6 @@ module.exports = function castFilterPath(query, schematype, val) {
41
41
  }
42
42
  continue;
43
43
  }
44
- // cast(schematype.caster ? schematype.caster.schema : schema, nested, options, context);
45
44
  } else {
46
45
  val[$cond] = schematype.castForQueryWrapper({
47
46
  $conditional: $cond,