mongoose 6.1.10 → 6.2.3

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 (81) hide show
  1. package/.eslintrc.json +154 -0
  2. package/CHANGELOG.md +73 -0
  3. package/dist/browser.umd.js +233 -222
  4. package/index.js +5 -1
  5. package/lib/aggregate.js +23 -28
  6. package/lib/browserDocument.js +1 -1
  7. package/lib/cast/number.js +2 -3
  8. package/lib/cast.js +9 -7
  9. package/lib/connection.js +76 -24
  10. package/lib/cursor/AggregationCursor.js +12 -7
  11. package/lib/cursor/QueryCursor.js +11 -6
  12. package/lib/document.js +131 -122
  13. package/lib/drivers/node-mongodb-native/collection.js +12 -4
  14. package/lib/drivers/node-mongodb-native/connection.js +11 -0
  15. package/lib/error/cast.js +3 -2
  16. package/lib/error/index.js +11 -0
  17. package/lib/error/syncIndexes.js +30 -0
  18. package/lib/helpers/clone.js +51 -29
  19. package/lib/helpers/common.js +2 -2
  20. package/lib/helpers/cursor/eachAsync.js +18 -15
  21. package/lib/helpers/document/compile.js +7 -4
  22. package/lib/helpers/getFunctionName.js +6 -4
  23. package/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js +14 -0
  24. package/lib/helpers/indexes/getRelatedIndexes.js +59 -0
  25. package/lib/helpers/isMongooseObject.js +9 -8
  26. package/lib/helpers/isObject.js +4 -4
  27. package/lib/helpers/model/discriminator.js +2 -1
  28. package/lib/helpers/path/parentPaths.js +10 -5
  29. package/lib/helpers/populate/assignRawDocsToIdStructure.js +4 -2
  30. package/lib/helpers/populate/assignVals.js +12 -4
  31. package/lib/helpers/populate/getModelsMapForPopulate.js +4 -4
  32. package/lib/helpers/populate/markArraySubdocsPopulated.js +3 -1
  33. package/lib/helpers/populate/modelNamesFromRefPath.js +4 -3
  34. package/lib/helpers/printJestWarning.js +2 -2
  35. package/lib/helpers/projection/applyProjection.js +77 -0
  36. package/lib/helpers/projection/hasIncludedChildren.js +36 -0
  37. package/lib/helpers/projection/isExclusive.js +5 -2
  38. package/lib/helpers/projection/isInclusive.js +5 -1
  39. package/lib/helpers/query/cast$expr.js +279 -0
  40. package/lib/helpers/query/castUpdate.js +6 -2
  41. package/lib/helpers/query/hasDollarKeys.js +7 -3
  42. package/lib/helpers/query/isOperator.js +5 -2
  43. package/lib/helpers/schema/applyPlugins.js +11 -0
  44. package/lib/helpers/schema/getIndexes.js +6 -2
  45. package/lib/helpers/schema/getPath.js +4 -2
  46. package/lib/helpers/timestamps/setupTimestamps.js +3 -8
  47. package/lib/index.js +26 -19
  48. package/lib/internal.js +10 -2
  49. package/lib/model.js +196 -171
  50. package/lib/options/SchemaTypeOptions.js +1 -1
  51. package/lib/plugins/trackTransaction.js +5 -4
  52. package/lib/query.js +159 -146
  53. package/lib/queryhelpers.js +10 -10
  54. package/lib/schema/SubdocumentPath.js +4 -3
  55. package/lib/schema/array.js +30 -21
  56. package/lib/schema/buffer.js +1 -1
  57. package/lib/schema/date.js +1 -1
  58. package/lib/schema/decimal128.js +1 -1
  59. package/lib/schema/documentarray.js +9 -11
  60. package/lib/schema/number.js +1 -1
  61. package/lib/schema/objectid.js +2 -2
  62. package/lib/schema/string.js +4 -4
  63. package/lib/schema.js +13 -8
  64. package/lib/schematype.js +86 -40
  65. package/lib/types/ArraySubdocument.js +2 -1
  66. package/lib/types/DocumentArray/index.js +10 -27
  67. package/lib/types/DocumentArray/isMongooseDocumentArray.js +5 -0
  68. package/lib/types/DocumentArray/methods/index.js +15 -3
  69. package/lib/types/array/index.js +22 -21
  70. package/lib/types/array/isMongooseArray.js +5 -0
  71. package/lib/types/array/methods/index.js +22 -23
  72. package/lib/types/buffer.js +3 -3
  73. package/lib/types/map.js +3 -4
  74. package/lib/utils.js +19 -10
  75. package/package.json +34 -168
  76. package/tools/repl.js +1 -1
  77. package/tsconfig.json +8 -0
  78. package/types/Error.d.ts +129 -0
  79. package/types/PipelineStage.d.ts +272 -0
  80. package/{index.d.ts → types/index.d.ts} +169 -481
  81. package/lib/types/array/ArrayWrapper.js +0 -981
package/lib/document.js CHANGED
@@ -16,6 +16,7 @@ const StrictModeError = require('./error/strict');
16
16
  const ValidationError = require('./error/validation');
17
17
  const ValidatorError = require('./error/validator');
18
18
  const VirtualType = require('./virtualtype');
19
+ const $__hasIncludedChildren = require('./helpers/projection/hasIncludedChildren');
19
20
  const promiseOrCallback = require('./helpers/promiseOrCallback');
20
21
  const cleanModifiedSubpaths = require('./helpers/document/cleanModifiedSubpaths');
21
22
  const compile = require('./helpers/document/compile').compile;
@@ -66,7 +67,7 @@ const specialProperties = utils.specialProperties;
66
67
  * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
67
68
  * @param {Object} [options] various configuration options for the document
68
69
  * @param {Boolean} [options.defaults=true] if `false`, skip applying default values to this document.
69
- * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
70
+ * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#events_class_events_eventemitter
70
71
  * @event `init`: Emitted on a document after it has been retrieved from the db and fully hydrated by Mongoose.
71
72
  * @event `save`: Emitted when the document is successfully saved
72
73
  * @api private
@@ -90,7 +91,7 @@ function Document(obj, fields, skipId, options) {
90
91
  options = arguments[4] || {};
91
92
  }
92
93
 
93
- this.$__ = new InternalCache;
94
+ this.$__ = new InternalCache();
94
95
  this.$isNew = 'isNew' in options ? options.isNew : true;
95
96
 
96
97
  if ('priorDoc' in options) {
@@ -417,33 +418,6 @@ Object.defineProperty(Document.prototype, '$op', {
417
418
  }
418
419
  });
419
420
 
420
- /*!
421
- * ignore
422
- */
423
-
424
- function $__hasIncludedChildren(fields) {
425
- const hasIncludedChildren = {};
426
- const keys = Object.keys(fields);
427
-
428
- for (const key of keys) {
429
- if (key.indexOf('.') === -1) {
430
- hasIncludedChildren[key] = 1;
431
- continue;
432
- }
433
- const parts = key.split('.');
434
- let c = parts[0];
435
-
436
- for (let i = 0; i < parts.length; ++i) {
437
- hasIncludedChildren[c] = 1;
438
- if (i + 1 < parts.length) {
439
- c = c + '.' + parts[i + 1];
440
- }
441
- }
442
- }
443
-
444
- return hasIncludedChildren;
445
- }
446
-
447
421
  /*!
448
422
  * ignore
449
423
  */
@@ -479,7 +453,8 @@ function $__applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSet
479
453
  break;
480
454
  }
481
455
  } else if (exclude === false && fields && !included) {
482
- if (curPath in fields) {
456
+ const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
457
+ if (curPath in fields || (hasSubpaths && hasIncludedChildren[curPath])) {
483
458
  included = true;
484
459
  } else if (!hasIncludedChildren[curPath]) {
485
460
  break;
@@ -795,11 +770,12 @@ function init(self, obj, doc, opts, prefix) {
795
770
 
796
771
  const keys = Object.keys(obj);
797
772
  const len = keys.length;
798
- let schema;
773
+ let schemaType;
799
774
  let path;
800
775
  let i;
801
776
  let index = 0;
802
777
  const strict = self.$__.strictMode;
778
+ const docSchema = self.$__schema;
803
779
 
804
780
  while (index < len) {
805
781
  _init(index++);
@@ -808,25 +784,27 @@ function init(self, obj, doc, opts, prefix) {
808
784
  function _init(index) {
809
785
  i = keys[index];
810
786
  path = prefix + i;
811
- schema = self.$__schema.path(path);
787
+ schemaType = docSchema.path(path);
812
788
 
813
789
  // Should still work if not a model-level discriminator, but should not be
814
790
  // necessary. This is *only* to catch the case where we queried using the
815
791
  // base model and the discriminated model has a projection
816
- if (self.$__schema.$isRootDiscriminator && !self.$__isSelected(path)) {
792
+ if (docSchema.$isRootDiscriminator && !self.$__isSelected(path)) {
817
793
  return;
818
794
  }
819
795
 
820
- if (!schema && utils.isPOJO(obj[i])) {
796
+ if (!schemaType && utils.isPOJO(obj[i])) {
821
797
  // assume nested object
822
798
  if (!doc[i]) {
823
799
  doc[i] = {};
800
+ if (!strict && !(i in docSchema.tree) && !(i in docSchema.methods) && !(i in docSchema.virtuals)) {
801
+ self[i] = doc[i];
802
+ }
824
803
  }
825
804
  init(self, obj[i], doc[i], opts, path + '.');
826
- } else if (!schema) {
805
+ } else if (!schemaType) {
827
806
  doc[i] = obj[i];
828
- if (!strict && !prefix) {
829
- // Set top-level properties that aren't in the schema if strict is false
807
+ if (!strict) {
830
808
  self[i] = obj[i];
831
809
  }
832
810
  } else {
@@ -835,13 +813,13 @@ function init(self, obj, doc, opts, prefix) {
835
813
  delete doc[i];
836
814
  }
837
815
  if (obj[i] === null) {
838
- doc[i] = schema._castNullish(null);
816
+ doc[i] = schemaType._castNullish(null);
839
817
  } else if (obj[i] !== undefined) {
840
818
  const wasPopulated = obj[i].$__ == null ? null : obj[i].$__.wasPopulated;
841
819
 
842
- if (schema && !wasPopulated) {
820
+ if (schemaType && !wasPopulated) {
843
821
  try {
844
- doc[i] = schema.cast(obj[i], self, true);
822
+ doc[i] = schemaType.cast(obj[i], self, true);
845
823
  } catch (e) {
846
824
  self.invalidate(e.path, new ValidatorError({
847
825
  path: e.path,
@@ -911,9 +889,9 @@ Document.prototype.update = function update() {
911
889
  *
912
890
  * @see Model.updateOne #model_Model.updateOne
913
891
  * @param {Object} doc
914
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
892
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
915
893
  * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](/docs/api.html#query_Query-lean) and the [Mongoose lean tutorial](/docs/tutorials/lean.html).
916
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
894
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
917
895
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/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.
918
896
  * @param {Function} callback
919
897
  * @return {Query}
@@ -1116,7 +1094,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1116
1094
 
1117
1095
  // `_skipMinimizeTopLevel` is because we may have deleted the top-level
1118
1096
  // nested key to ensure key order.
1119
- const _skipMinimizeTopLevel = get(options, '_skipMinimizeTopLevel', false);
1097
+ const _skipMinimizeTopLevel = options._skipMinimizeTopLevel || false;
1120
1098
  if (len === 0 && _skipMinimizeTopLevel) {
1121
1099
  delete options._skipMinimizeTopLevel;
1122
1100
  if (val) {
@@ -1135,7 +1113,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1135
1113
  // them to ensure we keep the user's key order.
1136
1114
  if (type === true &&
1137
1115
  !prefix &&
1138
- path[key] != null &&
1116
+ valForKey != null &&
1139
1117
  pathtype === 'nested' &&
1140
1118
  this._doc[key] != null) {
1141
1119
  delete this._doc[key];
@@ -1260,7 +1238,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1260
1238
  const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
1261
1239
 
1262
1240
  // Might need to change path for top-level alias
1263
- if (typeof this.$__schema.aliases[parts[0]] == 'string') {
1241
+ if (typeof this.$__schema.aliases[parts[0]] === 'string') {
1264
1242
  parts[0] = this.$__schema.aliases[parts[0]];
1265
1243
  }
1266
1244
 
@@ -1313,7 +1291,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1313
1291
  let curPath = '';
1314
1292
  for (i = 0; i < parts.length - 1; ++i) {
1315
1293
  cur = cur[parts[i]];
1316
- curPath += (curPath.length > 0 ? '.' : '') + parts[i];
1294
+ curPath += (curPath.length !== 0 ? '.' : '') + parts[i];
1317
1295
  if (!cur) {
1318
1296
  this.$set(curPath, {});
1319
1297
  // Hack re: gh-5800. If nested field is not selected, it probably exists
@@ -1336,7 +1314,8 @@ Document.prototype.$set = function $set(path, val, type, options) {
1336
1314
  if (parts.length <= 1) {
1337
1315
  pathToMark = path;
1338
1316
  } else {
1339
- for (i = 0; i < parts.length; ++i) {
1317
+ const len = parts.length;
1318
+ for (i = 0; i < len; ++i) {
1340
1319
  const subpath = parts.slice(0, i + 1).join('.');
1341
1320
  if (this.$get(subpath, null, { getters: false }) === null) {
1342
1321
  pathToMark = subpath;
@@ -1361,7 +1340,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1361
1340
  _markValidSubpaths(this, path);
1362
1341
  }
1363
1342
 
1364
- if (schema.$isSingleNested && val != null && merge) {
1343
+ if (val != null && merge && schema.$isSingleNested) {
1365
1344
  if (val instanceof Document) {
1366
1345
  val = val.toObject({ virtuals: false, transform: false });
1367
1346
  }
@@ -1403,8 +1382,9 @@ Document.prototype.$set = function $set(path, val, type, options) {
1403
1382
 
1404
1383
  let didPopulate = false;
1405
1384
  if (refMatches && val instanceof Document) {
1406
- this.$populated(path, val._id, { [populateModelSymbol]: val.constructor });
1407
- val.$__.wasPopulated = true;
1385
+ const unpopulatedValue = (schema && schema.$isSingleNested) ? schema.cast(val, this) : val._id;
1386
+ this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor });
1387
+ val.$__.wasPopulated = { value: unpopulatedValue };
1408
1388
  didPopulate = true;
1409
1389
  }
1410
1390
 
@@ -1418,7 +1398,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1418
1398
  this.$populated(path, val.map(function(v) { return v._id; }), popOpts);
1419
1399
 
1420
1400
  for (const doc of val) {
1421
- doc.$__.wasPopulated = true;
1401
+ doc.$__.wasPopulated = { value: doc._id };
1422
1402
  }
1423
1403
  didPopulate = true;
1424
1404
  }
@@ -1431,9 +1411,10 @@ Document.prototype.$set = function $set(path, val, type, options) {
1431
1411
  val = schema.applySetters(val, this, false, priorVal);
1432
1412
  }
1433
1413
 
1434
- if (schema.$isMongooseDocumentArray &&
1435
- Array.isArray(val) &&
1436
- val.length > 0 &&
1414
+ if (Array.isArray(val) &&
1415
+ !Array.isArray(schema) &&
1416
+ schema.$isMongooseDocumentArray &&
1417
+ val.length !== 0 &&
1437
1418
  val[0] != null &&
1438
1419
  val[0].$__ != null &&
1439
1420
  val[0].$__.populated != null) {
@@ -1459,7 +1440,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1459
1440
  delete this.$__.populated[path];
1460
1441
  }
1461
1442
 
1462
- if (schema.$isSingleNested && val != null) {
1443
+ if (val != null && schema.$isSingleNested) {
1463
1444
  _checkImmutableSubpaths(val, schema, priorVal);
1464
1445
  }
1465
1446
 
@@ -1652,11 +1633,11 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1652
1633
 
1653
1634
  // handle directly setting arrays (gh-1126)
1654
1635
  MongooseArray || (MongooseArray = require('./types/array'));
1655
- if (val && val.isMongooseArray) {
1636
+ if (val && utils.isMongooseArray(val)) {
1656
1637
  val._registerAtomic('$set', val);
1657
1638
 
1658
1639
  // Update embedded document parent references (gh-5189)
1659
- if (val.isMongooseDocumentArray) {
1640
+ if (utils.isMongooseDocumentArray(val)) {
1660
1641
  val.forEach(function(item) {
1661
1642
  item && item.__parentArray && (item.__parentArray = val);
1662
1643
  });
@@ -1670,10 +1651,10 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1670
1651
  }
1671
1652
  });
1672
1653
  }
1673
- } else if (Array.isArray(val) && val.isMongooseArray && Array.isArray(priorVal) && priorVal.isMongooseArray) {
1654
+ } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) {
1674
1655
  val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol];
1675
1656
  val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol];
1676
- if (val.isMongooseDocumentArray) {
1657
+ if (utils.isMongooseDocumentArray(val)) {
1677
1658
  val.forEach(doc => { doc.isNew = false; });
1678
1659
  }
1679
1660
  }
@@ -1702,7 +1683,7 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1702
1683
  obj = obj[parts[i]];
1703
1684
  } else if (obj[parts[i]] && obj[parts[i]] instanceof Embedded) {
1704
1685
  obj = obj[parts[i]];
1705
- } else if (obj[parts[i]] && obj[parts[i]].$isSingleNested) {
1686
+ } else if (obj[parts[i]] && !Array.isArray(obj[parts[i]]) && obj[parts[i]].$isSingleNested) {
1706
1687
  obj = obj[parts[i]];
1707
1688
  } else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
1708
1689
  obj = obj[parts[i]];
@@ -1782,7 +1763,7 @@ Document.prototype.get = function(path, type, options) {
1782
1763
  }
1783
1764
 
1784
1765
  // Might need to change path for top-level alias
1785
- if (typeof this.$__schema.aliases[pieces[0]] == 'string') {
1766
+ if (typeof this.$__schema.aliases[pieces[0]] === 'string') {
1786
1767
  pieces[0] = this.$__schema.aliases[pieces[0]];
1787
1768
  }
1788
1769
 
@@ -1963,7 +1944,7 @@ Document.prototype.$isEmpty = function(path) {
1963
1944
  transform: false
1964
1945
  };
1965
1946
 
1966
- if (arguments.length > 0) {
1947
+ if (arguments.length !== 0) {
1967
1948
  const v = this.$get(path);
1968
1949
  if (v == null) {
1969
1950
  return true;
@@ -2006,51 +1987,60 @@ function _isEmpty(v) {
2006
1987
 
2007
1988
  Document.prototype.modifiedPaths = function(options) {
2008
1989
  options = options || {};
1990
+
2009
1991
  const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
2010
- const _this = this;
2011
- return directModifiedPaths.reduce(function(list, path) {
2012
- const parts = path.split('.');
2013
- list = list.concat(parts.reduce(function(chains, part, i) {
2014
- return chains.concat(parts.slice(0, i).concat(part).join('.'));
2015
- }, []).filter(function(chain) {
2016
- return (list.indexOf(chain) === -1);
2017
- }));
1992
+ const result = new Set();
1993
+
1994
+ let i = 0;
1995
+ let j = 0;
1996
+ const len = directModifiedPaths.length;
1997
+
1998
+ for (i = 0; i < len; ++i) {
1999
+ const path = directModifiedPaths[i];
2000
+ const parts = parentPaths(path);
2001
+ const pLen = parts.length;
2002
+
2003
+ for (j = 0; j < pLen; ++j) {
2004
+ result.add(parts[j]);
2005
+ }
2018
2006
 
2019
2007
  if (!options.includeChildren) {
2020
- return list;
2008
+ continue;
2021
2009
  }
2022
2010
 
2023
- let cur = _this.$get(path);
2024
- if (cur != null && typeof cur === 'object') {
2011
+ let ii = 0;
2012
+ let cur = this.$get(path);
2013
+ if (typeof cur === 'object' && cur !== null) {
2025
2014
  if (cur._doc) {
2026
2015
  cur = cur._doc;
2027
2016
  }
2017
+ const len = cur.length;
2028
2018
  if (Array.isArray(cur)) {
2029
- const len = cur.length;
2030
- for (let i = 0; i < len; ++i) {
2031
- if (list.indexOf(path + '.' + i) === -1) {
2032
- list.push(path + '.' + i);
2033
- if (cur[i] != null && cur[i].$__) {
2034
- const modified = cur[i].modifiedPaths();
2035
- for (const childPath of modified) {
2036
- list.push(path + '.' + i + '.' + childPath);
2019
+ for (ii = 0; ii < len; ++ii) {
2020
+ const subPath = path + '.' + ii;
2021
+ if (!result.has(subPath)) {
2022
+ result.add(subPath);
2023
+ if (cur[ii] != null && cur[ii].$__) {
2024
+ const modified = cur[ii].modifiedPaths();
2025
+ let iii = 0;
2026
+ const iiiLen = modified.length;
2027
+ for (iii = 0; iii < iiiLen; ++iii) {
2028
+ result.add(subPath + '.' + modified[iii]);
2037
2029
  }
2038
2030
  }
2039
2031
  }
2040
2032
  }
2041
2033
  } else {
2042
- Object.keys(cur).
2043
- filter(function(key) {
2044
- return list.indexOf(path + '.' + key) === -1;
2045
- }).
2046
- forEach(function(key) {
2047
- list.push(path + '.' + key);
2048
- });
2034
+ const keys = Object.keys(cur);
2035
+ let ii = 0;
2036
+ const len = keys.length;
2037
+ for (ii = 0; ii < len; ++ii) {
2038
+ result.add(path + '.' + keys[ii]);
2039
+ }
2049
2040
  }
2050
2041
  }
2051
-
2052
- return list;
2053
- }, []);
2042
+ }
2043
+ return Array.from(result);
2054
2044
  };
2055
2045
 
2056
2046
  Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
@@ -2448,7 +2438,12 @@ Document.prototype.$validate = Document.prototype.validate;
2448
2438
  */
2449
2439
 
2450
2440
  function _evaluateRequiredFunctions(doc) {
2451
- Object.keys(doc.$__.activePaths.states.require).forEach(path => {
2441
+ const requiredFields = Object.keys(doc.$__.activePaths.states.require);
2442
+ let i = 0;
2443
+ const len = requiredFields.length;
2444
+ for (i = 0; i < len; ++i) {
2445
+ const path = requiredFields[i];
2446
+
2452
2447
  const p = doc.$__schema.path(path);
2453
2448
 
2454
2449
  if (p != null && typeof p.originalRequiredValue === 'function') {
@@ -2459,7 +2454,7 @@ function _evaluateRequiredFunctions(doc) {
2459
2454
  doc.invalidate(path, err);
2460
2455
  }
2461
2456
  }
2462
- });
2457
+ }
2463
2458
  }
2464
2459
 
2465
2460
  /*!
@@ -2520,7 +2515,9 @@ function _getPathsToValidate(doc) {
2520
2515
  // To avoid potential performance issues, skip doc arrays whose children
2521
2516
  // are not required. `getPositionalPathType()` may be slow, so avoid
2522
2517
  // it unless we have a case of #6364
2523
- (_pathType.$isMongooseDocumentArray && !get(_pathType, 'schemaOptions.required'))) {
2518
+ (!Array.isArray(_pathType) &&
2519
+ _pathType.$isMongooseDocumentArray &&
2520
+ !(_pathType && _pathType.schemaOptions && _pathType.schemaOptions.required))) {
2524
2521
  continue;
2525
2522
  }
2526
2523
 
@@ -2597,7 +2594,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2597
2594
  (typeof options === 'object') &&
2598
2595
  ('validateModifiedOnly' in options);
2599
2596
 
2600
- const pathsToSkip = get(options, 'pathsToSkip', null);
2597
+ const pathsToSkip = (options && options.pathsToSkip) || null;
2601
2598
 
2602
2599
  let shouldValidateModifiedOnly;
2603
2600
  if (hasValidateModifiedOnlyOption) {
@@ -2858,19 +2855,21 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
2858
2855
  }
2859
2856
  const validating = {};
2860
2857
 
2861
- paths.forEach(function(path) {
2858
+ for (let i = 0, len = paths.length; i < len; ++i) {
2859
+ const path = paths[i];
2860
+
2862
2861
  if (validating[path]) {
2863
- return;
2862
+ continue;
2864
2863
  }
2865
2864
 
2866
2865
  validating[path] = true;
2867
2866
 
2868
2867
  const p = _this.$__schema.path(path);
2869
2868
  if (!p) {
2870
- return;
2869
+ continue;
2871
2870
  }
2872
2871
  if (!_this.$isValid(path)) {
2873
- return;
2872
+ continue;
2874
2873
  }
2875
2874
 
2876
2875
  const val = _this.$__getValue(path);
@@ -2884,11 +2883,11 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
2884
2883
  p.$isArraySubdocument ||
2885
2884
  p.$isMongooseDocumentArray;
2886
2885
  if (isSubdoc && err instanceof ValidationError) {
2887
- return;
2886
+ continue;
2888
2887
  }
2889
2888
  _this.invalidate(path, err, undefined, true);
2890
2889
  }
2891
- });
2890
+ }
2892
2891
 
2893
2892
  const err = _this.$__.validationError;
2894
2893
  _this.$__.validationError = undefined;
@@ -3047,7 +3046,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3047
3046
  *
3048
3047
  * @param {Object} [options] options optional options
3049
3048
  * @param {Session} [options.session=null] the [session](https://docs.mongodb.com/manual/reference/server-sessions/) associated with this save operation. If not specified, defaults to the [document's associated session](api.html#document_Document-$session).
3050
- * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
3049
+ * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
3051
3050
  * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
3052
3051
  * @param {Boolean} [options.validateModifiedOnly=false] If `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
3053
3052
  * @param {Number|String} [options.w] set the [write concern](https://docs.mongodb.com/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern)
@@ -3062,7 +3061,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3062
3061
  * @throws {DocumentNotFoundError} if this [save updates an existing document](api.html#document_Document-isNew) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating).
3063
3062
  * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
3064
3063
  * @api public
3065
- * @see middleware http://mongoosejs.com/docs/middleware.html
3064
+ * @see middleware https://mongoosejs.com/docs/middleware.html
3066
3065
  */
3067
3066
 
3068
3067
  /**
@@ -3112,7 +3111,7 @@ Document.prototype.$__reset = function reset() {
3112
3111
  return _this.$__getValue(i);
3113
3112
  })
3114
3113
  .filter(function(val) {
3115
- return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
3114
+ return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length;
3116
3115
  })
3117
3116
  .forEach(function(array) {
3118
3117
  let i = array.length;
@@ -3135,7 +3134,7 @@ Document.prototype.$__reset = function reset() {
3135
3134
  return _this.$__getValue(i);
3136
3135
  }).
3137
3136
  filter(function(val) {
3138
- return val && val.$isSingleNested;
3137
+ return val && !Array.isArray(val) && val.$isSingleNested;
3139
3138
  }).
3140
3139
  forEach(function(doc) {
3141
3140
  doc.$__reset();
@@ -3315,7 +3314,7 @@ Document.prototype.$__getArrayPathsToValidate = function() {
3315
3314
  return this.$__getValue(i);
3316
3315
  }.bind(this))
3317
3316
  .filter(function(val) {
3318
- return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
3317
+ return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length;
3319
3318
  }).reduce(function(seed, array) {
3320
3319
  return seed.concat(array);
3321
3320
  }, [])
@@ -3357,12 +3356,12 @@ Document.prototype.$getAllSubdocs = function() {
3357
3356
  seed = Array.from(val.keys()).reduce(function(seed, path) {
3358
3357
  return docReducer(val.get(path), seed, null);
3359
3358
  }, seed);
3360
- } else if (val && val.$isSingleNested) {
3359
+ } else if (val && !Array.isArray(val) && val.$isSingleNested) {
3361
3360
  seed = Object.keys(val._doc).reduce(function(seed, path) {
3362
3361
  return docReducer(val._doc, seed, path);
3363
3362
  }, seed);
3364
3363
  seed.push(val);
3365
- } else if (val && val.isMongooseDocumentArray) {
3364
+ } else if (val && utils.isMongooseDocumentArray(val)) {
3366
3365
  val.forEach(function _docReduce(doc) {
3367
3366
  if (!doc || !doc._doc) {
3368
3367
  return;
@@ -3436,8 +3435,11 @@ Document.prototype.$toObject = function(options, json) {
3436
3435
  };
3437
3436
 
3438
3437
  const path = json ? 'toJSON' : 'toObject';
3439
- const baseOptions = get(this, 'constructor.base.options.' + path, {});
3440
- const schemaOptions = get(this, '$__schema.options', {});
3438
+ const baseOptions = this.constructor &&
3439
+ this.constructor.base &&
3440
+ this.constructor.base.options &&
3441
+ get(this.constructor.base.options, path) || {};
3442
+ const schemaOptions = this.$__schema && this.$__schema.options || {};
3441
3443
  // merge base default options with Schema's set default options if available.
3442
3444
  // `clone` is necessary here because `utils.options` directly modifies the second input.
3443
3445
  defaultOptions = utils.options(defaultOptions, clone(baseOptions));
@@ -3484,12 +3486,11 @@ Document.prototype.$toObject = function(options, json) {
3484
3486
  }
3485
3487
 
3486
3488
  const depopulate = options.depopulate ||
3487
- get(options, '_parentOptions.depopulate', false);
3489
+ (options._parentOptions && options._parentOptions.depopulate || false);
3488
3490
  // _isNested will only be true if this is not the top level document, we
3489
- // should never depopulate
3491
+ // should never depopulate the top-level document
3490
3492
  if (depopulate && options._isNested && this.$__.wasPopulated) {
3491
- // populated paths that we set to a document
3492
- return clone(this._id, cloneOptions);
3493
+ return clone(this.$__.wasPopulated.value || this._id, cloneOptions);
3493
3494
  }
3494
3495
 
3495
3496
  // merge default options with input options.
@@ -3564,7 +3565,7 @@ Document.prototype.$toObject = function(options, json) {
3564
3565
  /**
3565
3566
  * Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)).
3566
3567
  *
3567
- * Buffers are converted to instances of [mongodb.Binary](http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
3568
+ * Buffers are converted to instances of [mongodb.Binary](https://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
3568
3569
  *
3569
3570
  * ####Options:
3570
3571
  *
@@ -3703,7 +3704,7 @@ Document.prototype.$toObject = function(options, json) {
3703
3704
  * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
3704
3705
  * @param {Boolean} [options.useProjection=false] - If true, omits 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.
3705
3706
  * @return {Object} js object
3706
- * @see mongodb.Binary http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
3707
+ * @see mongodb.Binary https://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
3707
3708
  * @api public
3708
3709
  * @memberOf Document
3709
3710
  * @instance
@@ -3761,7 +3762,9 @@ function applyVirtuals(self, json, options, toObjectOptions) {
3761
3762
  let assignPath;
3762
3763
  let cur = self._doc;
3763
3764
  let v;
3764
- const aliases = get(toObjectOptions, 'aliases', true);
3765
+ const aliases = typeof (toObjectOptions && toObjectOptions.aliases) === 'boolean'
3766
+ ? toObjectOptions.aliases
3767
+ : true;
3765
3768
 
3766
3769
  let virtualsToApply = null;
3767
3770
  if (Array.isArray(options.virtuals)) {
@@ -3955,15 +3958,20 @@ function omitDeselectedFields(self, json) {
3955
3958
  }
3956
3959
 
3957
3960
  /**
3958
- * The return value of this method is used in calls to JSON.stringify(doc).
3961
+ * The return value of this method is used in calls to [`JSON.stringify(doc)`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript#the-tojson-function).
3959
3962
  *
3960
3963
  * This method accepts the same options as [Document#toObject](#document_Document-toObject). To apply the options to every document of your schema by default, set your [schemas](#schema_Schema) `toJSON` option to the same argument.
3961
3964
  *
3962
- * schema.set('toJSON', { virtuals: true })
3965
+ * schema.set('toJSON', { virtuals: true });
3966
+ *
3967
+ * There is one difference between `toJSON()` and `toObject()` options.
3968
+ * When you call `toJSON()`, the [`flattenMaps` option](./document.html#document_Document-toObject) defaults to `true`, because `JSON.stringify()` doesn't convert maps to objects by default.
3969
+ * When you call `toObject()`, the `flattenMaps` option is `false` by default.
3963
3970
  *
3964
- * See [schema options](/docs/guide.html#toJSON) for details.
3971
+ * See [schema options](/docs/guide.html#toJSON) for more information on setting `toJSON` option defaults.
3965
3972
  *
3966
3973
  * @param {Object} options
3974
+ * @param {Boolean} [options.flattenMaps=true] if true, convert Maps to [POJOs](https://masteringjs.io/tutorials/fundamentals/pojo). Useful if you want to `JSON.stringify()` the result.
3967
3975
  * @return {Object}
3968
3976
  * @see Document#toObject #document_Document-toObject
3969
3977
  * @see JSON.stringify() in JavaScript https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html
@@ -4118,6 +4126,7 @@ Document.prototype.equals = function(doc) {
4118
4126
  * @param {Object} [match] Conditions for the population query
4119
4127
  * @param {Object} [options] Options for the population query (sort, etc)
4120
4128
  * @param {String} [options.path=null] The path to populate.
4129
+ * @param {string|PopulateOptions} [options.populate=null] Recursively populate paths in the populated documents. See [deep populate docs](/docs/populate.html#deep-populate).
4121
4130
  * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
4122
4131
  * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
4123
4132
  * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
@@ -4139,7 +4148,7 @@ Document.prototype.populate = function populate() {
4139
4148
  const args = [...arguments];
4140
4149
  let fn;
4141
4150
 
4142
- if (args.length > 0) {
4151
+ if (args.length !== 0) {
4143
4152
  if (typeof args[args.length - 1] === 'function') {
4144
4153
  fn = args.pop();
4145
4154
  }
@@ -4298,7 +4307,7 @@ Document.prototype.depopulate = function(path) {
4298
4307
 
4299
4308
  let populatedIds;
4300
4309
  const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : [];
4301
- const populated = get(this, '$__.populated', {});
4310
+ const populated = this.$__ && this.$__.populated || {};
4302
4311
 
4303
4312
  if (arguments.length === 0) {
4304
4313
  // Depopulate all