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/model.js CHANGED
@@ -25,6 +25,7 @@ const ParallelSaveError = require('./error/parallelSave');
25
25
  const applyQueryMiddleware = require('./helpers/query/applyQueryMiddleware');
26
26
  const applyHooks = require('./helpers/model/applyHooks');
27
27
  const applyMethods = require('./helpers/model/applyMethods');
28
+ const applyProjection = require('./helpers/projection/applyProjection');
28
29
  const applyStaticHooks = require('./helpers/model/applyStaticHooks');
29
30
  const applyStatics = require('./helpers/model/applyStatics');
30
31
  const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
@@ -42,12 +43,18 @@ const immediate = require('./helpers/immediate');
42
43
  const internalToObjectOptions = require('./options').internalToObjectOptions;
43
44
  const isDefaultIdIndex = require('./helpers/indexes/isDefaultIdIndex');
44
45
  const isIndexEqual = require('./helpers/indexes/isIndexEqual');
46
+ const {
47
+ getRelatedDBIndexes,
48
+ getRelatedSchemaIndexes
49
+ } = require('./helpers/indexes/getRelatedIndexes');
50
+ const decorateDiscriminatorIndexOptions = require('./helpers/indexes/decorateDiscriminatorIndexOptions');
45
51
  const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
46
52
  const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
47
53
  const modifiedPaths = require('./helpers/update/modifiedPaths');
48
54
  const parallelLimit = require('./helpers/parallelLimit');
49
55
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
50
56
  const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
57
+ const setDottedPath = require('./helpers/path/setDottedPath');
51
58
  const util = require('util');
52
59
  const utils = require('./utils');
53
60
 
@@ -90,7 +97,7 @@ const saveToObjectOptions = Object.assign({}, internalToObjectOptions, {
90
97
  * @param {Object} doc values for initial set
91
98
  * @param [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projection](./api.html#query_Query-select).
92
99
  * @param {Boolean} [skipId=false] optional boolean. If true, mongoose doesn't add an `_id` field to the document.
93
- * @inherits Document http://mongoosejs.com/docs/api/document.html
100
+ * @inherits Document https://mongoosejs.com/docs/api/document.html
94
101
  * @event `error`: If listening to this event, 'error' is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
95
102
  * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
96
103
  * @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event.
@@ -242,7 +249,7 @@ Model.prototype.$__handleSave = function(options, callback) {
242
249
  let saveOptions = {};
243
250
 
244
251
  applyWriteConcern(this.$__schema, options);
245
- if (typeof options.writeConcern != 'undefined') {
252
+ if (typeof options.writeConcern !== 'undefined') {
246
253
  saveOptions.writeConcern = {};
247
254
  if ('w' in options.writeConcern) {
248
255
  saveOptions.writeConcern.w = options.writeConcern.w;
@@ -344,17 +351,12 @@ Model.prototype.$__handleSave = function(options, callback) {
344
351
  where[key] = val;
345
352
  }
346
353
  }
347
- this.constructor.exists(where, optionsWithCustomValues).
348
- then((documentExists) => {
349
- if (!documentExists) {
350
- const matchedCount = 0;
351
- return callback(null, { $where: where, matchedCount });
352
- }
353
-
354
- const matchedCount = 1;
354
+ this.constructor.exists(where, optionsWithCustomValues)
355
+ .then(documentExists => {
356
+ const matchedCount = !documentExists ? 0 : 1;
355
357
  callback(null, { $where: where, matchedCount });
356
- }).
357
- catch(callback);
358
+ })
359
+ .catch(callback);
358
360
  return;
359
361
  }
360
362
 
@@ -379,7 +381,12 @@ Model.prototype.$__save = function(options, callback) {
379
381
  });
380
382
  }
381
383
  let numAffected = 0;
382
- if (get(options, 'safe.w') !== 0 && get(options, 'w') !== 0) {
384
+ const writeConcern = options != null ?
385
+ options.writeConcern != null ?
386
+ options.writeConcern.w :
387
+ options.w :
388
+ 0;
389
+ if (writeConcern !== 0) {
383
390
  // Skip checking if write succeeded if writeConcern is set to
384
391
  // unacknowledged writes, because otherwise `numAffected` will always be 0
385
392
  if (result != null) {
@@ -391,8 +398,10 @@ Model.prototype.$__save = function(options, callback) {
391
398
  numAffected = result;
392
399
  }
393
400
  }
401
+
402
+ const versionBump = this.$__.version || this.$__schema.options.optimisticConcurrency;
394
403
  // was this an update that required a version bump?
395
- if (this.$__.version && !this.$__.inserting) {
404
+ if (versionBump && !this.$__.inserting) {
396
405
  const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
397
406
  this.$__.version = undefined;
398
407
  const key = this.$__schema.options.versionKey;
@@ -459,7 +468,7 @@ function generateVersionError(doc, modifiedPaths) {
459
468
  *
460
469
  * @param {Object} [options] options optional options
461
470
  * @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).
462
- * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
471
+ * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
463
472
  * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
464
473
  * @param {Boolean} [options.validateModifiedOnly=false] if `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
465
474
  * @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)
@@ -471,7 +480,7 @@ function generateVersionError(doc, modifiedPaths) {
471
480
  * @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).
472
481
  * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
473
482
  * @api public
474
- * @see middleware http://mongoosejs.com/docs/middleware.html
483
+ * @see middleware https://mongoosejs.com/docs/middleware.html
475
484
  */
476
485
 
477
486
  Model.prototype.save = function(options, fn) {
@@ -702,7 +711,7 @@ Model.prototype.$__delta = function() {
702
711
  where._id = this._doc._id;
703
712
  // If `_id` is an object, need to depopulate, but also need to be careful
704
713
  // because `_id` can technically be null (see gh-6406)
705
- if (get(where, '_id.$__', null) != null) {
714
+ if ((where && where._id && where._id.$__ || null) != null) {
706
715
  where._id = where._id.toObject({ transform: false, depopulate: true });
707
716
  }
708
717
  for (; d < len; ++d) {
@@ -740,7 +749,7 @@ Model.prototype.$__delta = function() {
740
749
  operand(this, where, delta, data, 1, '$unset');
741
750
  } else if (value === null) {
742
751
  operand(this, where, delta, data, null);
743
- } else if (value.isMongooseArray && value.$path() && value[arrayAtomicsSymbol]) {
752
+ } else if (utils.isMongooseArray(value) && value.$path() && value[arrayAtomicsSymbol]) {
744
753
  // arrays and other custom types (support plugins etc)
745
754
  handleAtomics(this, where, delta, data, value);
746
755
  } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) {
@@ -792,7 +801,7 @@ function checkDivergentArray(doc, path, array) {
792
801
  }
793
802
  }
794
803
 
795
- if (!(pop && array && array.isMongooseArray)) return;
804
+ if (!(pop && utils.isMongooseArray(array))) return;
796
805
 
797
806
  // If the array was populated using options that prevented all
798
807
  // documents from being returned (match, skip, limit) or they
@@ -832,7 +841,8 @@ Model.prototype.$__version = function(where, delta) {
832
841
  if (where === true) {
833
842
  // this is an insert
834
843
  if (key) {
835
- this.$__setValue(key, delta[key] = 0);
844
+ setDottedPath(delta, key, 0);
845
+ this.$__setValue(key, 0);
836
846
  }
837
847
  return;
838
848
  }
@@ -881,7 +891,7 @@ Model.prototype.$__version = function(where, delta) {
881
891
  * doc.save(function (err) { .. })
882
892
  * })
883
893
  *
884
- * @see versionKeys http://mongoosejs.com/docs/guide.html#versionKey
894
+ * @see versionKeys https://mongoosejs.com/docs/guide.html#versionKey
885
895
  * @api public
886
896
  */
887
897
 
@@ -1059,8 +1069,8 @@ Model.prototype.model = function model(name) {
1059
1069
  };
1060
1070
 
1061
1071
  /**
1062
- * Returns true if at least one document exists in the database that matches
1063
- * the given `filter`, and false otherwise.
1072
+ * Returns a document with `_id` only if at least one document exists in the database that matches
1073
+ * the given `filter`, and `null` otherwise.
1064
1074
  *
1065
1075
  * Under the hood, `MyModel.exists({ answer: 42 })` is equivalent to
1066
1076
  * `MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean()`
@@ -1077,7 +1087,7 @@ Model.prototype.model = function model(name) {
1077
1087
  * - `findOne()`
1078
1088
  *
1079
1089
  * @param {Object} filter
1080
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
1090
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
1081
1091
  * @param {Function} [callback] callback
1082
1092
  * @return {Query}
1083
1093
  */
@@ -1097,10 +1107,6 @@ Model.exists = function exists(filter, options, callback) {
1097
1107
  if (typeof callback === 'function') {
1098
1108
  return query.exec(callback);
1099
1109
  }
1100
- options = options || {};
1101
- if (!options.explain) {
1102
- return query.then(doc => !!doc);
1103
- }
1104
1110
 
1105
1111
  return query;
1106
1112
  };
@@ -1136,11 +1142,13 @@ Model.exists = function exists(filter, options, callback) {
1136
1142
  * @param {Object|String} [options] If string, same as `options.value`.
1137
1143
  * @param {String} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
1138
1144
  * @param {Boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
1145
+ * @param {Boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
1139
1146
  * @return {Model} The newly created discriminator model
1140
1147
  * @api public
1141
1148
  */
1142
1149
 
1143
1150
  Model.discriminator = function(name, schema, options) {
1151
+
1144
1152
  let model;
1145
1153
  if (typeof name === 'function') {
1146
1154
  model = name;
@@ -1152,7 +1160,7 @@ Model.discriminator = function(name, schema, options) {
1152
1160
 
1153
1161
  options = options || {};
1154
1162
  const value = utils.isPOJO(options) ? options.value : options;
1155
- const clone = get(options, 'clone', true);
1163
+ const clone = typeof options.clone === 'boolean' ? options.clone : true;
1156
1164
 
1157
1165
  _checkContext(this, 'discriminator');
1158
1166
 
@@ -1164,7 +1172,7 @@ Model.discriminator = function(name, schema, options) {
1164
1172
  }
1165
1173
 
1166
1174
  schema = discriminator(this, name, schema, value, true);
1167
- if (this.db.models[name]) {
1175
+ if (this.db.models[name] && !schema.options.overwriteModels) {
1168
1176
  throw new OverwriteModelError(name);
1169
1177
  }
1170
1178
 
@@ -1226,7 +1234,7 @@ for (const i in EventEmitter.prototype) {
1226
1234
 
1227
1235
  /**
1228
1236
  * This function is responsible for building [indexes](https://docs.mongodb.com/manual/indexes/),
1229
- * unless [`autoIndex`](http://mongoosejs.com/docs/guide.html#autoIndex) is turned off.
1237
+ * unless [`autoIndex`](https://mongoosejs.com/docs/guide.html#autoIndex) is turned off.
1230
1238
  *
1231
1239
  * Mongoose calls this function automatically when a model is created using
1232
1240
  * [`mongoose.model()`](/docs/api.html#mongoose_Mongoose-model) or
@@ -1331,7 +1339,7 @@ Model.init = function init(callback) {
1331
1339
  * });
1332
1340
  *
1333
1341
  * @api public
1334
- * @param {Object} [options] see [MongoDB driver docs](http://mongodb.github.io/node-mongodb-native/3.1/api/Db.html#createCollection)
1342
+ * @param {Object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/3.1/api/Db.html#createCollection)
1335
1343
  * @param {Function} [callback]
1336
1344
  * @returns {Promise}
1337
1345
  */
@@ -1347,15 +1355,24 @@ Model.createCollection = function createCollection(options, callback) {
1347
1355
  options = void 0;
1348
1356
  }
1349
1357
 
1350
- const schemaCollation = get(this, ['schema', 'options', 'collation'], null);
1358
+ const schemaCollation = this &&
1359
+ this.schema &&
1360
+ this.schema.options &&
1361
+ this.schema.options.collation || null;
1351
1362
  if (schemaCollation != null) {
1352
1363
  options = Object.assign({ collation: schemaCollation }, options);
1353
1364
  }
1354
- const capped = get(this, ['schema', 'options', 'capped']);
1365
+ const capped = this &&
1366
+ this.schema &&
1367
+ this.schema.options &&
1368
+ this.schema.options.capped;
1355
1369
  if (capped) {
1356
1370
  options = Object.assign({ capped: true }, capped, options);
1357
1371
  }
1358
- const timeseries = get(this, ['schema', 'options', 'timeseries']);
1372
+ const timeseries = this &&
1373
+ this.schema &&
1374
+ this.schema.options &&
1375
+ this.schema.options.timeseries;
1359
1376
  if (timeseries != null) {
1360
1377
  options = Object.assign({ timeseries }, options);
1361
1378
  }
@@ -1381,7 +1398,7 @@ Model.createCollection = function createCollection(options, callback) {
1381
1398
  * the model's schema except the `_id` index, and build any indexes that
1382
1399
  * are in your schema but not in MongoDB.
1383
1400
  *
1384
- * See the [introductory blog post](http://thecodebarbarian.com/whats-new-in-mongoose-5-2-syncindexes)
1401
+ * See the [introductory blog post](https://thecodebarbarian.com/whats-new-in-mongoose-5-2-syncindexes)
1385
1402
  * for more information.
1386
1403
  *
1387
1404
  * ####Example:
@@ -1459,8 +1476,7 @@ Model.diffIndexes = function diffIndexes(options, callback) {
1459
1476
 
1460
1477
  for (const schemaIndex of schemaIndexes) {
1461
1478
  const key = schemaIndex[0];
1462
- const options = _decorateDiscriminatorIndexOptions(this,
1463
- utils.clone(schemaIndex[1]));
1479
+ const options = decorateDiscriminatorIndexOptions(this.schema, utils.clone(schemaIndex[1]));
1464
1480
  if (isIndexEqual(key, options, index)) {
1465
1481
  found = true;
1466
1482
  }
@@ -1473,10 +1489,9 @@ Model.diffIndexes = function diffIndexes(options, callback) {
1473
1489
  // Iterate through the indexes created on the schema and
1474
1490
  // compare against the indexes in mongodb.
1475
1491
  for (const schemaIndex of schemaIndexes) {
1476
- const key = schemaIndex[0];
1477
1492
  let found = false;
1478
- const options = _decorateDiscriminatorIndexOptions(this,
1479
- utils.clone(schemaIndex[1]));
1493
+ const key = schemaIndex[0];
1494
+ const options = decorateDiscriminatorIndexOptions(this.schema, utils.clone(schemaIndex[1]));
1480
1495
  for (const index of indexes) {
1481
1496
  if (isDefaultIdIndex(index)) {
1482
1497
  continue;
@@ -1513,32 +1528,33 @@ Model.cleanIndexes = function cleanIndexes(callback) {
1513
1528
  return this.db.base._promiseOrCallback(callback, cb => {
1514
1529
  const collection = this.$__collection;
1515
1530
 
1516
- this.listIndexes((err, indexes) => {
1531
+ this.listIndexes((err, dbIndexes) => {
1517
1532
  if (err != null) {
1518
1533
  return cb(err);
1519
1534
  }
1520
1535
 
1521
- const schemaIndexes = this.schema.indexes();
1536
+ dbIndexes = getRelatedDBIndexes(this, dbIndexes);
1537
+ const schemaIndexes = getRelatedSchemaIndexes(this, this.schema.indexes());
1538
+
1522
1539
  const toDrop = [];
1523
1540
 
1524
- for (const index of indexes) {
1541
+ for (const dbIndex of dbIndexes) {
1525
1542
  let found = false;
1526
1543
  // Never try to drop `_id` index, MongoDB server doesn't allow it
1527
- if (isDefaultIdIndex(index)) {
1544
+ if (isDefaultIdIndex(dbIndex)) {
1528
1545
  continue;
1529
1546
  }
1530
1547
 
1531
- for (const schemaIndex of schemaIndexes) {
1532
- const key = schemaIndex[0];
1533
- const options = _decorateDiscriminatorIndexOptions(this,
1534
- utils.clone(schemaIndex[1]));
1535
- if (isIndexEqual(key, options, index)) {
1548
+ for (const [schemaIndexKeysObject, schemaIndexOptions] of schemaIndexes) {
1549
+ const options = decorateDiscriminatorIndexOptions(this.schema, utils.clone(schemaIndexOptions));
1550
+
1551
+ if (isIndexEqual(schemaIndexKeysObject, options, dbIndex)) {
1536
1552
  found = true;
1537
1553
  }
1538
1554
  }
1539
1555
 
1540
1556
  if (!found) {
1541
- toDrop.push(index.name);
1557
+ toDrop.push(dbIndex.name);
1542
1558
  }
1543
1559
  }
1544
1560
 
@@ -1651,7 +1667,7 @@ Model.ensureIndexes = function ensureIndexes(options, callback) {
1651
1667
  };
1652
1668
 
1653
1669
  /**
1654
- * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#createIndex)
1670
+ * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#createIndex)
1655
1671
  * function.
1656
1672
  *
1657
1673
  * @param {Object} [options] internal options
@@ -1694,7 +1710,7 @@ function _ensureIndexes(model, options, callback) {
1694
1710
  utils.warn('mongoose: Cannot specify a custom index on `_id` for ' +
1695
1711
  'model name "' + model.modelName + '", ' +
1696
1712
  'MongoDB does not allow overwriting the default `_id` index. See ' +
1697
- 'http://bit.ly/mongodb-id-index');
1713
+ 'https://bit.ly/mongodb-id-index');
1698
1714
  }
1699
1715
  }
1700
1716
 
@@ -1717,7 +1733,17 @@ function _ensureIndexes(model, options, callback) {
1717
1733
  const baseSchema = model.schema._baseSchema;
1718
1734
  const baseSchemaIndexes = baseSchema ? baseSchema.indexes() : [];
1719
1735
 
1720
- const create = function() {
1736
+ immediate(function() {
1737
+ // If buffering is off, do this manually.
1738
+ if (options._automatic && !model.collection.collection) {
1739
+ model.collection.addQueue(create, []);
1740
+ } else {
1741
+ create();
1742
+ }
1743
+ });
1744
+
1745
+
1746
+ function create() {
1721
1747
  if (options._automatic) {
1722
1748
  if (model.schema.options.autoIndex === false ||
1723
1749
  (model.schema.options.autoIndex == null && model.db.config.autoIndex === false)) {
@@ -1746,7 +1772,7 @@ function _ensureIndexes(model, options, callback) {
1746
1772
  }
1747
1773
  }
1748
1774
  delete indexOptions._autoIndex;
1749
- _decorateDiscriminatorIndexOptions(model, indexOptions);
1775
+ decorateDiscriminatorIndexOptions(model.schema, indexOptions);
1750
1776
  applyWriteConcern(model.schema, indexOptions);
1751
1777
 
1752
1778
  indexSingleStart(indexFields, options);
@@ -1772,34 +1798,7 @@ function _ensureIndexes(model, options, callback) {
1772
1798
  }
1773
1799
  create();
1774
1800
  }));
1775
- };
1776
-
1777
- immediate(function() {
1778
- // If buffering is off, do this manually.
1779
- if (options._automatic && !model.collection.collection) {
1780
- model.collection.addQueue(create, []);
1781
- } else {
1782
- create();
1783
- }
1784
- });
1785
- }
1786
-
1787
- function _decorateDiscriminatorIndexOptions(model, indexOptions) {
1788
- // If the model is a discriminator and it has a unique index, add a
1789
- // partialFilterExpression by default so the unique index will only apply
1790
- // to that discriminator.
1791
- if (model.baseModelName != null &&
1792
- !('partialFilterExpression' in indexOptions) &&
1793
- !('sparse' in indexOptions)) {
1794
- const value = (
1795
- model.schema.discriminatorMapping &&
1796
- model.schema.discriminatorMapping.value
1797
- ) || model.modelName;
1798
- const discriminatorKey = model.schema.options.discriminatorKey;
1799
-
1800
- indexOptions.partialFilterExpression = { [discriminatorKey]: value };
1801
- }
1802
- return indexOptions;
1801
+ }
1803
1802
  }
1804
1803
 
1805
1804
  /**
@@ -2019,7 +2018,7 @@ Model.remove = function remove(conditions, options, callback) {
2019
2018
  * [middleware docs](/docs/middleware.html#naming) to learn more.
2020
2019
  *
2021
2020
  * @param {Object} conditions
2022
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2021
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2023
2022
  * @param {Function} [callback]
2024
2023
  * @return {Query}
2025
2024
  * @api public
@@ -2062,7 +2061,7 @@ Model.deleteOne = function deleteOne(conditions, options, callback) {
2062
2061
  * [middleware docs](/docs/middleware.html#naming) to learn more.
2063
2062
  *
2064
2063
  * @param {Object} conditions
2065
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2064
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2066
2065
  * @param {Function} [callback]
2067
2066
  * @return {Query}
2068
2067
  * @api public
@@ -2113,8 +2112,8 @@ Model.deleteMany = function deleteMany(conditions, options, callback) {
2113
2112
  * await MyModel.find({ name: /john/i }, null, { skip: 10 }).exec();
2114
2113
  *
2115
2114
  * @param {Object|ObjectId} filter
2116
- * @param {Object|String|Array<String>} [projection] optional fields to return, see [`Query.prototype.select()`](http://mongoosejs.com/docs/api.html#query_Query-select)
2117
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2115
+ * @param {Object|String|Array<String>} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api.html#query_Query-select)
2116
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2118
2117
  * @param {Function} [callback]
2119
2118
  * @return {Query}
2120
2119
  * @see field selection #query_Query-select
@@ -2184,7 +2183,7 @@ Model.find = function find(conditions, projection, options, callback) {
2184
2183
  *
2185
2184
  * @param {Any} id value of `_id` to query by
2186
2185
  * @param {Object|String|Array<String>} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
2187
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2186
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2188
2187
  * @param {Function} [callback]
2189
2188
  * @return {Query}
2190
2189
  * @see field selection #query_Query-select
@@ -2227,7 +2226,7 @@ Model.findById = function findById(id, projection, options, callback) {
2227
2226
  *
2228
2227
  * @param {Object} [conditions]
2229
2228
  * @param {Object|String|Array<String>} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
2230
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2229
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2231
2230
  * @param {Function} [callback]
2232
2231
  * @return {Query}
2233
2232
  * @see field selection #query_Query-select
@@ -2481,9 +2480,9 @@ Model.$where = function $where() {
2481
2480
  * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2482
2481
  * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
2483
2482
  * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
2484
- * - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
2485
- * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2486
- * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
2483
+ * - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
2484
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2485
+ * - `strict`: overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) for this update
2487
2486
  *
2488
2487
  * ####Examples:
2489
2488
  *
@@ -2521,11 +2520,11 @@ Model.$where = function $where() {
2521
2520
  *
2522
2521
  * @param {Object} [conditions]
2523
2522
  * @param {Object} [update]
2524
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2523
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2525
2524
  * @param {String} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2526
2525
  * @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).
2527
2526
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](/docs/transactions.html).
2528
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2527
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2529
2528
  * @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.
2530
2529
  * @param {Boolean} [options.overwrite=false] By default, if you don't include any [update operators](https://docs.mongodb.com/manual/reference/operator/update/) in `update`, Mongoose will wrap `update` in `$set` for you. This prevents you from accidentally overwriting the document. This option tells Mongoose to skip adding `$set`. An alternative to this would be using [Model.findOneAndReplace(conditions, update, options, callback)](https://mongoosejs.com/docs/api/model.html#model_Model.findOneAndReplace).
2531
2530
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
@@ -2533,7 +2532,7 @@ Model.$where = function $where() {
2533
2532
  * @param {Function} [callback]
2534
2533
  * @return {Query}
2535
2534
  * @see Tutorial /docs/tutorials/findoneandupdate.html
2536
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2535
+ * @see mongodb https://www.mongodb.org/display/DOCS/findAndModify+Command
2537
2536
  * @api public
2538
2537
  */
2539
2538
 
@@ -2581,7 +2580,7 @@ Model.findOneAndUpdate = function(conditions, update, options, callback) {
2581
2580
  */
2582
2581
 
2583
2582
  function _decorateUpdateWithVersionKey(update, options, versionKey) {
2584
- if (!versionKey || !get(options, 'upsert', false)) {
2583
+ if (!versionKey || !(options && options.upsert || false)) {
2585
2584
  return;
2586
2585
  }
2587
2586
 
@@ -2615,11 +2614,11 @@ function _decorateUpdateWithVersionKey(update, options, versionKey) {
2615
2614
  * - `new`: bool - true to return the modified document rather than the original. defaults to false
2616
2615
  * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
2617
2616
  * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
2618
- * - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
2617
+ * - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
2619
2618
  * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
2620
2619
  * - `select`: sets the document fields to return
2621
- * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2622
- * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
2620
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2621
+ * - `strict`: overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) for this update
2623
2622
  *
2624
2623
  * ####Examples:
2625
2624
  *
@@ -2658,17 +2657,17 @@ function _decorateUpdateWithVersionKey(update, options, versionKey) {
2658
2657
  *
2659
2658
  * @param {Object|Number|String} id value of `_id` to query by
2660
2659
  * @param {Object} [update]
2661
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2660
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2662
2661
  * @param {String} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2663
2662
  * @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).
2664
2663
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](/docs/transactions.html).
2665
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2664
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2666
2665
  * @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.
2667
2666
  * @param {Boolean} [options.overwrite=false] By default, if you don't include any [update operators](https://docs.mongodb.com/manual/reference/operator/update/) in `update`, Mongoose will wrap `update` in `$set` for you. This prevents you from accidentally overwriting the document. This option tells Mongoose to skip adding `$set`. An alternative to this would be using [Model.findOneAndReplace({ _id: id }, update, options, callback)](https://mongoosejs.com/docs/api/model.html#model_Model.findOneAndReplace).
2668
2667
  * @param {Function} [callback]
2669
2668
  * @return {Query}
2670
2669
  * @see Model.findOneAndUpdate #model_Model.findOneAndUpdate
2671
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2670
+ * @see mongodb https://www.mongodb.org/display/DOCS/findAndModify+Command
2672
2671
  * @api public
2673
2672
  */
2674
2673
 
@@ -2719,8 +2718,8 @@ Model.findByIdAndUpdate = function(id, update, options, callback) {
2719
2718
  * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2720
2719
  * - `select`: sets the document fields to return, ex. `{ projection: { _id: 0 } }`
2721
2720
  * - `projection`: equivalent to `select`
2722
- * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2723
- * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
2721
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2722
+ * - `strict`: overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) for this update
2724
2723
  *
2725
2724
  * ####Examples:
2726
2725
  *
@@ -2743,8 +2742,8 @@ Model.findByIdAndUpdate = function(id, update, options, callback) {
2743
2742
  * });
2744
2743
  *
2745
2744
  * @param {Object} conditions
2746
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2747
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2745
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2746
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2748
2747
  * @param {Object|String|Array<String>} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
2749
2748
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](/docs/transactions.html).
2750
2749
  * @param {Function} [callback]
@@ -2791,12 +2790,12 @@ Model.findOneAndDelete = function(conditions, options, callback) {
2791
2790
  * - `findOneAndDelete()`
2792
2791
  *
2793
2792
  * @param {Object|Number|String} id value of `_id` to query by
2794
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2795
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2793
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2794
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2796
2795
  * @param {Function} [callback]
2797
2796
  * @return {Query}
2798
2797
  * @see Model.findOneAndRemove #model_Model.findOneAndRemove
2799
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2798
+ * @see mongodb https://www.mongodb.org/display/DOCS/findAndModify+Command
2800
2799
  */
2801
2800
 
2802
2801
  Model.findByIdAndDelete = function(id, options, callback) {
@@ -2832,8 +2831,8 @@ Model.findByIdAndDelete = function(id, options, callback) {
2832
2831
  * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2833
2832
  * - `select`: sets the document fields to return
2834
2833
  * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
2835
- * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2836
- * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
2834
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2835
+ * - `strict`: overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) for this update
2837
2836
  *
2838
2837
  * ####Examples:
2839
2838
  *
@@ -2845,11 +2844,11 @@ Model.findByIdAndDelete = function(id, options, callback) {
2845
2844
  *
2846
2845
  * @param {Object} filter Replace the first document that matches this filter
2847
2846
  * @param {Object} [replacement] Replace with this document
2848
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2847
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2849
2848
  * @param {String} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2850
2849
  * @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).
2851
2850
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](/docs/transactions.html).
2852
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2851
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2853
2852
  * @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.
2854
2853
  * @param {Object|String|Array<String>} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
2855
2854
  * @param {Function} [callback]
@@ -2909,8 +2908,8 @@ Model.findOneAndReplace = function(filter, replacement, options, callback) {
2909
2908
  * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2910
2909
  * - `select`: sets the document fields to return
2911
2910
  * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
2912
- * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2913
- * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
2911
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2912
+ * - `strict`: overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) for this update
2914
2913
  *
2915
2914
  * ####Examples:
2916
2915
  *
@@ -2933,13 +2932,13 @@ Model.findOneAndReplace = function(filter, replacement, options, callback) {
2933
2932
  * });
2934
2933
  *
2935
2934
  * @param {Object} conditions
2936
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2935
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
2937
2936
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](/docs/transactions.html).
2938
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2937
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2939
2938
  * @param {Object|String|Array<String>} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
2940
2939
  * @param {Function} [callback]
2941
2940
  * @return {Query}
2942
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2941
+ * @see mongodb https://www.mongodb.org/display/DOCS/findAndModify+Command
2943
2942
  * @api public
2944
2943
  */
2945
2944
 
@@ -2987,8 +2986,8 @@ Model.findOneAndRemove = function(conditions, options, callback) {
2987
2986
  *
2988
2987
  * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
2989
2988
  * - `select`: sets the document fields to return
2990
- * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2991
- * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
2989
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2990
+ * - `strict`: overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) for this update
2992
2991
  *
2993
2992
  * ####Examples:
2994
2993
  *
@@ -2999,14 +2998,14 @@ Model.findOneAndRemove = function(conditions, options, callback) {
2999
2998
  * A.findByIdAndRemove() // returns Query
3000
2999
  *
3001
3000
  * @param {Object|Number|String} id value of `_id` to query by
3002
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
3003
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3001
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
3002
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3004
3003
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](/docs/transactions.html).
3005
3004
  * @param {Object|String|Array<String>} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
3006
3005
  * @param {Function} [callback]
3007
3006
  * @return {Query}
3008
3007
  * @see Model.findOneAndRemove #model_Model.findOneAndRemove
3009
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
3008
+ * @see mongodb https://www.mongodb.org/display/DOCS/findAndModify+Command
3010
3009
  */
3011
3010
 
3012
3011
  Model.findByIdAndRemove = function(id, options, callback) {
@@ -3151,7 +3150,7 @@ Model.create = function create(doc, options, callback) {
3151
3150
  return cb(firstError, savedDocs);
3152
3151
  }
3153
3152
 
3154
- if (doc instanceof Array) {
3153
+ if (Array.isArray(doc)) {
3155
3154
  cb(null, savedDocs);
3156
3155
  } else {
3157
3156
  cb.apply(this, [null].concat(savedDocs));
@@ -3197,7 +3196,7 @@ Model.create = function create(doc, options, callback) {
3197
3196
  * await doc.remove();
3198
3197
  *
3199
3198
  * @param {Array} [pipeline]
3200
- * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#watch)
3199
+ * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#watch)
3201
3200
  * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
3202
3201
  * @api public
3203
3202
  */
@@ -3228,7 +3227,7 @@ Model.watch = function(pipeline, options) {
3228
3227
  /**
3229
3228
  * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://docs.mongodb.com/manual/release-notes/3.6/#client-sessions)
3230
3229
  * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/),
3231
- * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
3230
+ * and [transactions](https://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
3232
3231
  *
3233
3232
  * Calling `MyModel.startSession()` is equivalent to calling `MyModel.db.startSession()`.
3234
3233
  *
@@ -3245,7 +3244,7 @@ Model.watch = function(pipeline, options) {
3245
3244
  * // secondary that is experiencing replication lag.
3246
3245
  * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' });
3247
3246
  *
3248
- * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
3247
+ * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
3249
3248
  * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
3250
3249
  * @param {Function} [callback]
3251
3250
  * @return {Promise<ClientSession>} promise that resolves to a MongoDB driver `ClientSession`
@@ -3281,9 +3280,9 @@ Model.startSession = function() {
3281
3280
  * Movies.insertMany(arr, function(error, docs) {});
3282
3281
  *
3283
3282
  * @param {Array|Object|*} doc(s)
3284
- * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insertMany)
3283
+ * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insertMany)
3285
3284
  * @param {Boolean} [options.ordered = true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
3286
- * @param {Boolean} [options.rawResult = false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~insertWriteOpCallback) with a `mongoose` property that contains `validationErrors` if this is an unordered `insertMany`.
3285
+ * @param {Boolean} [options.rawResult = false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~insertWriteOpCallback) with a `mongoose` property that contains `validationErrors` if this is an unordered `insertMany`.
3287
3286
  * @param {Boolean} [options.lean = false] if `true`, skips hydrating and validating the documents. This option is useful if you need the extra performance, but Mongoose won't validate the documents before inserting.
3288
3287
  * @param {Number} [options.limit = null] this limits the number of documents being processed (validation/casting) by mongoose in parallel, this does **NOT** send the documents in batches to MongoDB. Use this option if you're processing a large number of documents and your app is running out of memory.
3289
3288
  * @param {String|Object|Array} [options.populate = null] populates the result documents. This option is a no-op if `rawResult` is set.
@@ -3320,10 +3319,10 @@ Model.$__insertMany = function(arr, options, callback) {
3320
3319
  }
3321
3320
  callback = callback || utils.noop;
3322
3321
  options = options || {};
3323
- const limit = get(options, 'limit', 1000);
3324
- const rawResult = get(options, 'rawResult', false);
3325
- const ordered = get(options, 'ordered', true);
3326
- const lean = get(options, 'lean', false);
3322
+ const limit = options.limit || 1000;
3323
+ const rawResult = !!options.rawResult;
3324
+ const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
3325
+ const lean = !!options.lean;
3327
3326
 
3328
3327
  if (!Array.isArray(arr)) {
3329
3328
  arr = [arr];
@@ -3374,7 +3373,7 @@ Model.$__insertMany = function(arr, options, callback) {
3374
3373
  return doc != null;
3375
3374
  });
3376
3375
  // Quickly escape while there aren't any valid docAttributes
3377
- if (docAttributes.length < 1) {
3376
+ if (docAttributes.length === 0) {
3378
3377
  if (rawResult) {
3379
3378
  const res = {
3380
3379
  mongoose: {
@@ -3401,12 +3400,12 @@ Model.$__insertMany = function(arr, options, callback) {
3401
3400
  // `writeErrors` is a property reported by the MongoDB driver,
3402
3401
  // just not if there's only 1 error.
3403
3402
  if (error.writeErrors == null &&
3404
- get(error, 'result.result.writeErrors') != null) {
3403
+ (error.result && error.result.result && error.result.result.writeErrors) != null) {
3405
3404
  error.writeErrors = error.result.result.writeErrors;
3406
3405
  }
3407
3406
 
3408
3407
  // `insertedDocs` is a Mongoose-specific property
3409
- const erroredIndexes = new Set(get(error, 'writeErrors', []).map(err => err.index));
3408
+ const erroredIndexes = new Set((error && error.writeErrors || []).map(err => err.index));
3410
3409
 
3411
3410
  let firstErroredIndex = -1;
3412
3411
  error.insertedDocs = docAttributes.
@@ -3493,7 +3492,7 @@ function _setIsNew(doc, val) {
3493
3492
  *
3494
3493
  * This function does **not** trigger any middleware, neither `save()`, nor `update()`.
3495
3494
  * If you need to trigger
3496
- * `save()` middleware for every document use [`create()`](http://mongoosejs.com/docs/api.html#model_Model.create) instead.
3495
+ * `save()` middleware for every document use [`create()`](https://mongoosejs.com/docs/api.html#model_Model.create) instead.
3497
3496
  *
3498
3497
  * ####Example:
3499
3498
  *
@@ -3517,9 +3516,7 @@ function _setIsNew(doc, val) {
3517
3516
  * },
3518
3517
  * {
3519
3518
  * deleteOne: {
3520
- * {
3521
- * filter: { name: 'Eddard Stark' }
3522
- * }
3519
+ * filter: { name: 'Eddard Stark' }
3523
3520
  * }
3524
3521
  * }
3525
3522
  * ]).then(res => {
@@ -3564,7 +3561,7 @@ function _setIsNew(doc, val) {
3564
3561
  * @param {Boolean} [options.bypassDocumentValidation=false] If true, disable [MongoDB server-side schema validation](https://docs.mongodb.com/manual/core/schema-validation/) for all writes in this bulk.
3565
3562
  * @param {Boolean} [options.strict=null] Overwrites the [`strict` option](/docs/guide.html#strict) on schema. If false, allows filtering and writing fields not defined in the schema for all writes in this bulk.
3566
3563
  * @param {Function} [callback] callback `function(error, bulkWriteOpResult) {}`
3567
- * @return {Promise} resolves to a [`BulkWriteOpResult`](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~BulkWriteOpResult) if the operation succeeds
3564
+ * @return {Promise} resolves to a [`BulkWriteOpResult`](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~BulkWriteOpResult) if the operation succeeds
3568
3565
  * @api public
3569
3566
  */
3570
3567
 
@@ -3609,20 +3606,25 @@ Model.bulkWrite = function(ops, options, callback) {
3609
3606
  * `bulkSave` uses `bulkWrite` under the hood, so it's mostly useful when dealing with many documents (10K+)
3610
3607
  *
3611
3608
  * @param {[Document]} documents
3609
+ * @param {Object} [options] options passed to the underlying `bulkWrite()`
3610
+ * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](/docs/transactions.html).
3611
+ * @param {String|number} [options.w=1] The [write concern](https://docs.mongodb.com/manual/reference/write-concern/). See [`Query#w()`](/docs/api.html#query_Query-w) for more information.
3612
+ * @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
3613
+ * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
3612
3614
  *
3613
3615
  */
3614
- Model.bulkSave = function(documents) {
3616
+ Model.bulkSave = function(documents, options) {
3615
3617
  const preSavePromises = documents.map(buildPreSavePromise);
3616
3618
 
3617
3619
  const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true });
3618
3620
 
3619
3621
  let bulkWriteResultPromise;
3620
3622
  return Promise.all(preSavePromises)
3621
- .then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations))
3623
+ .then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations, options))
3622
3624
  .then(() => documents.map(buildSuccessfulWriteHandlerPromise))
3623
3625
  .then(() => bulkWriteResultPromise)
3624
3626
  .catch((err) => {
3625
- if (!get(err, 'writeErrors.length')) {
3627
+ if (!(err && err.writeErrors && err.writeErrors.length)) {
3626
3628
  throw err;
3627
3629
  }
3628
3630
  return Promise.all(
@@ -3762,6 +3764,13 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
3762
3764
  Model.hydrate = function(obj, projection) {
3763
3765
  _checkContext(this, 'hydrate');
3764
3766
 
3767
+ if (projection != null) {
3768
+ if (obj != null && obj.$__ != null) {
3769
+ obj = obj.toObject(internalToObjectOptions);
3770
+ }
3771
+ obj = applyProjection(obj, projection);
3772
+ }
3773
+
3765
3774
  const document = require('./queryhelpers').createModel(this, obj, projection);
3766
3775
  document.$init(obj);
3767
3776
  return document;
@@ -3793,7 +3802,7 @@ Model.hydrate = function(obj, projection) {
3793
3802
  * - `writeConcern` (object): sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3794
3803
  * - `multi` (boolean): whether multiple documents should be updated (false)
3795
3804
  * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
3796
- * - `setDefaultsOnInsert` (boolean): if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
3805
+ * - `setDefaultsOnInsert` (boolean): if this and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
3797
3806
  * - `timestamps` (boolean): If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3798
3807
  * - `overwrite` (boolean): disables update-only mode, allowing you to overwrite the doc (false)
3799
3808
  *
@@ -3824,8 +3833,8 @@ Model.hydrate = function(obj, projection) {
3824
3833
  * Be careful to not use an existing model instance for the update clause (this won't work and can cause weird behavior like infinite loops). Also, ensure that the update clause does not have an _id property, which causes Mongo to return a "Mod on _id not allowed" error.
3825
3834
  *
3826
3835
  * @deprecated
3827
- * @see strict http://mongoosejs.com/docs/guide.html#strict
3828
- * @see response http://docs.mongodb.org/v2.6/reference/command/update/#output
3836
+ * @see strict https://mongoosejs.com/docs/guide.html#strict
3837
+ * @see response https://docs.mongodb.org/v2.6/reference/command/update/#output
3829
3838
  * @param {Object} filter
3830
3839
  * @param {Object} doc
3831
3840
  * @param {Object} [options] optional see [`Query.prototype.setOptions()`](/docs/api.html#query_Query-setOptions)
@@ -3834,7 +3843,7 @@ Model.hydrate = function(obj, projection) {
3834
3843
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3835
3844
  * @param {Boolean} [options.multi=false] whether multiple documents should be updated or just the first one that matches `filter`.
3836
3845
  * @param {Boolean} [options.runValidators=false] if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
3837
- * @param {Boolean} [options.setDefaultsOnInsert=false] `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
3846
+ * @param {Boolean} [options.setDefaultsOnInsert=false] `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
3838
3847
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3839
3848
  * @param {Boolean} [options.overwrite=false] By default, if you don't include any [update operators](https://docs.mongodb.com/manual/reference/operator/update/) in `doc`, Mongoose will wrap `doc` in `$set` for you. This prevents you from accidentally overwriting the document. This option tells Mongoose to skip adding `$set`.
3840
3849
  * @param {Function} [callback] params are (error, [updateWriteOpResult](https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#~updateWriteOpResult))
@@ -3874,8 +3883,8 @@ Model.update = function update(conditions, doc, options, callback) {
3874
3883
  *
3875
3884
  * @param {Object} filter
3876
3885
  * @param {Object|Array} update
3877
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
3878
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3886
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
3887
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3879
3888
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3880
3889
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3881
3890
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
@@ -3883,7 +3892,7 @@ Model.update = function update(conditions, doc, options, callback) {
3883
3892
  * @return {Query}
3884
3893
  * @see Query docs https://mongoosejs.com/docs/queries.html
3885
3894
  * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
3886
- * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3895
+ * @see writeOpResult https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3887
3896
  * @api public
3888
3897
  */
3889
3898
 
@@ -3914,8 +3923,8 @@ Model.updateMany = function updateMany(conditions, doc, options, callback) {
3914
3923
  *
3915
3924
  * @param {Object} filter
3916
3925
  * @param {Object|Array} update
3917
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
3918
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3926
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
3927
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3919
3928
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3920
3929
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3921
3930
  * @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.
@@ -3923,7 +3932,7 @@ Model.updateMany = function updateMany(conditions, doc, options, callback) {
3923
3932
  * @return {Query}
3924
3933
  * @see Query docs https://mongoosejs.com/docs/queries.html
3925
3934
  * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
3926
- * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3935
+ * @see writeOpResult https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3927
3936
  * @api public
3928
3937
  */
3929
3938
 
@@ -3951,15 +3960,15 @@ Model.updateOne = function updateOne(conditions, doc, options, callback) {
3951
3960
  *
3952
3961
  * @param {Object} filter
3953
3962
  * @param {Object} doc
3954
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
3955
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3963
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
3964
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3956
3965
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3957
3966
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3958
3967
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3959
3968
  * @param {Function} [callback] `function(error, res) {}` where `res` has 3 properties: `n`, `nModified`, `ok`.
3960
3969
  * @return {Query}
3961
3970
  * @see Query docs https://mongoosejs.com/docs/queries.html
3962
- * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3971
+ * @see writeOpResult https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3963
3972
  * @return {Query}
3964
3973
  * @api public
3965
3974
  */
@@ -3967,7 +3976,7 @@ Model.updateOne = function updateOne(conditions, doc, options, callback) {
3967
3976
  Model.replaceOne = function replaceOne(conditions, doc, options, callback) {
3968
3977
  _checkContext(this, 'replaceOne');
3969
3978
 
3970
- const versionKey = get(this, 'schema.options.versionKey', null);
3979
+ const versionKey = this && this.schema && this.schema.options && this.schema.options.versionKey || null;
3971
3980
  if (versionKey && !doc[versionKey]) {
3972
3981
  doc[versionKey] = 0;
3973
3982
  }
@@ -3992,7 +4001,10 @@ function _update(model, op, conditions, doc, options, callback) {
3992
4001
  }
3993
4002
  options = typeof options === 'function' ? options : utils.clone(options);
3994
4003
 
3995
- const versionKey = get(model, 'schema.options.versionKey', null);
4004
+ const versionKey = model &&
4005
+ model.schema &&
4006
+ model.schema.options &&
4007
+ model.schema.options.versionKey || null;
3996
4008
  _decorateUpdateWithVersionKey(doc, options, versionKey);
3997
4009
 
3998
4010
  return mq[op](conditions, doc, options, callback);
@@ -4001,7 +4013,7 @@ function _update(model, op, conditions, doc, options, callback) {
4001
4013
  /**
4002
4014
  * Executes a mapReduce command.
4003
4015
  *
4004
- * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#mapreduce) for more detail about options.
4016
+ * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. See [node-mongodb-native mapReduce() documentation](https://mongodb.github.io/node-mongodb-native/api-generated/collection.html#mapreduce) for more detail about options.
4005
4017
  *
4006
4018
  * This function does not trigger any middleware.
4007
4019
  *
@@ -4070,7 +4082,7 @@ function _update(model, op, conditions, doc, options, callback) {
4070
4082
  *
4071
4083
  * @param {Object} o an object specifying map-reduce options
4072
4084
  * @param {Function} [callback] optional callback
4073
- * @see http://www.mongodb.org/display/DOCS/MapReduce
4085
+ * @see https://www.mongodb.org/display/DOCS/MapReduce
4074
4086
  * @return {Promise}
4075
4087
  * @api public
4076
4088
  */
@@ -4123,7 +4135,7 @@ Model.mapReduce = function mapReduce(o, callback) {
4123
4135
  };
4124
4136
 
4125
4137
  /**
4126
- * Performs [aggregations](http://docs.mongodb.org/manual/applications/aggregation/) on the models collection.
4138
+ * Performs [aggregations](https://docs.mongodb.org/manual/applications/aggregation/) on the models collection.
4127
4139
  *
4128
4140
  * If a `callback` is passed, the `aggregate` is executed and a `Promise` is returned. If a callback is not passed, the `aggregate` itself is returned.
4129
4141
  *
@@ -4157,10 +4169,10 @@ Model.mapReduce = function mapReduce(o, callback) {
4157
4169
  *
4158
4170
  * - [Mongoose `Aggregate`](/docs/api/aggregate.html)
4159
4171
  * - [An Introduction to Mongoose Aggregate](https://masteringjs.io/tutorials/mongoose/aggregate)
4160
- * - [MongoDB Aggregation docs](http://docs.mongodb.org/manual/applications/aggregation/)
4172
+ * - [MongoDB Aggregation docs](https://docs.mongodb.org/manual/applications/aggregation/)
4161
4173
  *
4162
4174
  * @see Aggregate #aggregate_Aggregate
4163
- * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
4175
+ * @see MongoDB https://docs.mongodb.org/manual/applications/aggregation/
4164
4176
  * @param {Array} [pipeline] aggregation pipeline as an array of objects
4165
4177
  * @param {Object} [options] aggregation options
4166
4178
  * @param {Function} [callback]
@@ -4171,7 +4183,7 @@ Model.mapReduce = function mapReduce(o, callback) {
4171
4183
  Model.aggregate = function aggregate(pipeline, options, callback) {
4172
4184
  _checkContext(this, 'aggregate');
4173
4185
 
4174
- if (arguments.length > 3 || get(pipeline, 'constructor.name') === 'Object') {
4186
+ if (arguments.length > 3 || (pipeline && pipeline.constructor && pipeline.constructor.name) === 'Object') {
4175
4187
  throw new MongooseError('Mongoose 5.x disallows passing a spread of operators ' +
4176
4188
  'to `Model.aggregate()`. Instead of ' +
4177
4189
  '`Model.aggregate({ $match }, { $skip })`, do ' +
@@ -4379,6 +4391,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
4379
4391
  * @param {Document|Array} docs Either a single document or array of documents to populate.
4380
4392
  * @param {Object|String} options Either the paths to populate or an object specifying all parameters
4381
4393
  * @param {string} [options.path=null] The path to populate.
4394
+ * @param {string|PopulateOptions} [options.populate=null] Recursively populate paths in the populated documents. See [deep populate docs](/docs/populate.html#deep-populate).
4382
4395
  * @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.
4383
4396
  * @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).
4384
4397
  * @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.
@@ -4451,9 +4464,16 @@ const excludeIdRegGlobal = /\s?-_id\s?/g;
4451
4464
 
4452
4465
  function populate(model, docs, options, callback) {
4453
4466
  const populateOptions = { ...options };
4454
- if (model.base.options.strictPopulate != null && options.strictPopulate == null) {
4455
- populateOptions.strictPopulate = model.base.options.strictPopulate;
4467
+ if (options.strictPopulate == null) {
4468
+ if (options._localModel != null && options._localModel.schema._userProvidedOptions.strictPopulate != null) {
4469
+ populateOptions.strictPopulate = options._localModel.schema._userProvidedOptions.strictPopulate;
4470
+ } else if (options._localModel != null && model.base.options.strictPopulate != null) {
4471
+ populateOptions.strictPopulate = model.base.options.strictPopulate;
4472
+ } else if (model.base.options.strictPopulate != null) {
4473
+ populateOptions.strictPopulate = model.base.options.strictPopulate;
4474
+ }
4456
4475
  }
4476
+
4457
4477
  // normalize single / multiple docs passed
4458
4478
  if (!Array.isArray(docs)) {
4459
4479
  docs = [docs];
@@ -4487,7 +4507,10 @@ function populate(model, docs, options, callback) {
4487
4507
  ids = utils.array.unique(ids);
4488
4508
 
4489
4509
  const assignmentOpts = {};
4490
- assignmentOpts.sort = get(mod, 'options.options.sort', void 0);
4510
+ assignmentOpts.sort = mod &&
4511
+ mod.options &&
4512
+ mod.options.options &&
4513
+ mod.options.options.sort || void 0;
4491
4514
  assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
4492
4515
 
4493
4516
  if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
@@ -4524,7 +4547,7 @@ function populate(model, docs, options, callback) {
4524
4547
  }
4525
4548
  if (!hasOne) {
4526
4549
  // If models but no docs, skip further deep populate.
4527
- if (modelsMap.length > 0) {
4550
+ if (modelsMap.length !== 0) {
4528
4551
  return callback();
4529
4552
  }
4530
4553
  // If no models to populate but we have a nested populate,
@@ -4656,7 +4679,9 @@ function _assign(model, vals, mod, assignmentOpts) {
4656
4679
  const isVirtual = mod.isVirtual;
4657
4680
  const justOne = mod.justOne;
4658
4681
  let _val;
4659
- const lean = get(options, 'options.lean', false);
4682
+ const lean = options &&
4683
+ options.options &&
4684
+ options.options.lean || false;
4660
4685
  const len = vals.length;
4661
4686
  const rawOrder = {};
4662
4687
  const rawDocs = {};
@@ -4727,7 +4752,7 @@ function _assign(model, vals, mod, assignmentOpts) {
4727
4752
  }
4728
4753
  // flag each as result of population
4729
4754
  if (!lean) {
4730
- val.$__.wasPopulated = true;
4755
+ val.$__.wasPopulated = val.$__.wasPopulated || true;
4731
4756
  }
4732
4757
  }
4733
4758
  }