mongoose 6.2.6 → 6.2.9

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.
@@ -17,6 +17,7 @@ const isObject = require('./helpers/isObject');
17
17
  * Document constructor.
18
18
  *
19
19
  * @param {Object} obj the values to set
20
+ * @param {Object} schema
20
21
  * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
21
22
  * @param {Boolean} [skipId] bool, should we auto create an ObjectId _id
22
23
  * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#events_class_events_eventemitter
package/lib/connection.js CHANGED
@@ -788,10 +788,6 @@ Connection.prototype.openUri = function(uri, options, callback) {
788
788
  }
789
789
  _this.client = client;
790
790
 
791
- for (const db of this.otherDbs) {
792
- _setClient(db, client, {}, db.name);
793
- }
794
-
795
791
  client.setMaxListeners(0);
796
792
  client.connect((error) => {
797
793
  if (error) {
@@ -800,6 +796,10 @@ Connection.prototype.openUri = function(uri, options, callback) {
800
796
 
801
797
  _setClient(_this, client, options, dbName);
802
798
 
799
+ for (const db of this.otherDbs) {
800
+ _setClient(db, client, {}, db.name);
801
+ }
802
+
803
803
  resolve(_this);
804
804
  });
805
805
  });
@@ -26,7 +26,6 @@ const util = require('util');
26
26
  * Use [`Aggregate#cursor()`](/docs/api.html#aggregate_Aggregate-cursor) instead.
27
27
  *
28
28
  * @param {Aggregate} agg
29
- * @param {Object} options
30
29
  * @inherits Readable
31
30
  * @event `cursor`: Emitted when the cursor is created
32
31
  * @event `error`: Emitted when an error occurred
@@ -472,7 +472,8 @@ function _nextDoc(ctx, doc, pop, callback) {
472
472
  });
473
473
  }
474
474
 
475
- ctx.query._completeOne(doc, null, (err, doc) => {
475
+ const { model, _fields, _userProvidedFields, options } = ctx.query;
476
+ helpers.createModelAndInit(model, doc, _fields, _userProvidedFields, options, pop, (err, doc) => {
476
477
  if (err != null) {
477
478
  return callback(err);
478
479
  }
package/lib/document.js CHANGED
@@ -156,9 +156,9 @@ function Document(obj, fields, skipId, options) {
156
156
  if (obj) {
157
157
  // Skip set hooks
158
158
  if (this.$__original_set) {
159
- this.$__original_set(obj, undefined, true);
159
+ this.$__original_set(obj, undefined, true, options);
160
160
  } else {
161
- this.$set(obj, undefined, true);
161
+ this.$set(obj, undefined, true, options);
162
162
  }
163
163
 
164
164
  if (obj instanceof Document) {
@@ -801,7 +801,7 @@ function init(self, obj, doc, opts, prefix) {
801
801
  init(self, obj[i], doc[i], opts, path + '.');
802
802
  } else if (!schemaType) {
803
803
  doc[i] = obj[i];
804
- if (!strict) {
804
+ if (!strict && !prefix) {
805
805
  self[i] = obj[i];
806
806
  }
807
807
  } else {
@@ -1836,6 +1836,7 @@ Document.prototype.$__path = function(path) {
1836
1836
  */
1837
1837
 
1838
1838
  Document.prototype.markModified = function(path, scope) {
1839
+ // console.log('MarkModified', path, new Error().stack);
1839
1840
  this.$__.activePaths.modify(path);
1840
1841
  if (scope != null && !this.$isSubdocument) {
1841
1842
  this.$__.pathsToScopes = this.$__pathsToScopes || {};
@@ -2422,6 +2423,7 @@ Document.prototype.validate = function(pathsToValidate, options, callback) {
2422
2423
 
2423
2424
  this.$__validate(pathsToValidate, options, (error) => {
2424
2425
  this.$op = null;
2426
+ this.$__.validating = null;
2425
2427
  cb(error);
2426
2428
  });
2427
2429
  }, this.constructor.events);
@@ -2472,7 +2474,6 @@ function _getPathsToValidate(doc) {
2472
2474
  return true;
2473
2475
  }));
2474
2476
 
2475
-
2476
2477
  Object.keys(doc.$__.activePaths.states.init).forEach(addToPaths);
2477
2478
  Object.keys(doc.$__.activePaths.states.modify).forEach(addToPaths);
2478
2479
  Object.keys(doc.$__.activePaths.states.default).forEach(addToPaths);
@@ -2484,28 +2485,53 @@ function _getPathsToValidate(doc) {
2484
2485
  if (subdoc.$basePath) {
2485
2486
  // Remove child paths for now, because we'll be validating the whole
2486
2487
  // subdoc
2488
+ if (!subdoc.$__.fullPath) {
2489
+ subdoc.ownerDocument();
2490
+ }
2491
+ const fullPathToSubdoc = subdoc.$__.fullPath;
2492
+
2487
2493
  for (const p of paths) {
2488
- if (p === null || p.startsWith(subdoc.$basePath + '.')) {
2494
+ if (p === null || p.startsWith(fullPathToSubdoc + '.')) {
2489
2495
  paths.delete(p);
2490
2496
  }
2491
2497
  }
2492
2498
 
2493
- if (doc.$isModified(subdoc.$basePath, modifiedPaths) &&
2494
- !doc.isDirectModified(subdoc.$basePath) &&
2495
- !doc.$isDefault(subdoc.$basePath)) {
2496
- paths.add(subdoc.$basePath);
2499
+ if (doc.$isModified(fullPathToSubdoc, modifiedPaths) &&
2500
+ !doc.isDirectModified(fullPathToSubdoc) &&
2501
+ !doc.$isDefault(fullPathToSubdoc)) {
2502
+ paths.add(fullPathToSubdoc);
2497
2503
 
2498
- skipSchemaValidators[subdoc.$basePath] = true;
2504
+ skipSchemaValidators[fullPathToSubdoc] = true;
2499
2505
  }
2500
2506
  }
2501
2507
  }
2502
2508
 
2509
+ for (const path of paths) {
2510
+ const _pathType = doc.$__schema.path(path);
2511
+ if (!_pathType) {
2512
+ continue;
2513
+ }
2514
+
2515
+ // Optimization: if primitive path with no validators, or array of primitives
2516
+ // with no validators, skip validating this path entirely.
2517
+ if (!_pathType.caster && _pathType.validators.length === 0) {
2518
+ paths.delete(path);
2519
+ } else if (_pathType.$isMongooseArray &&
2520
+ !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2521
+ !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2522
+ _pathType.validators.length === 0 && // and arrays with top-level validators
2523
+ _pathType.$embeddedSchemaType.validators.length === 0) {
2524
+ paths.delete(path);
2525
+ }
2526
+ }
2527
+
2503
2528
  // from here on we're not removing items from paths
2504
2529
 
2505
2530
  // gh-661: if a whole array is modified, make sure to run validation on all
2506
2531
  // the children as well
2507
2532
  for (const path of paths) {
2508
2533
  const _pathType = doc.$__schema.path(path);
2534
+
2509
2535
  if (!_pathType ||
2510
2536
  !_pathType.$isMongooseArray ||
2511
2537
  // To avoid potential performance issues, skip doc arrays whose children
@@ -2517,6 +2543,16 @@ function _getPathsToValidate(doc) {
2517
2543
  continue;
2518
2544
  }
2519
2545
 
2546
+ // gh-11380: optimization. If the array isn't a document array and there's no validators
2547
+ // on the array type, there's no need to run validation on the individual array elements.
2548
+ if (_pathType &&
2549
+ _pathType.$isMongooseArray &&
2550
+ !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2551
+ !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2552
+ _pathType.$embeddedSchemaType.validators.length === 0) {
2553
+ continue;
2554
+ }
2555
+
2520
2556
  const val = doc.$__getValue(path);
2521
2557
  _pushNestedArrayPaths(val, paths, path);
2522
2558
  }
@@ -2602,7 +2638,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2602
2638
  const _this = this;
2603
2639
  const _complete = () => {
2604
2640
  let validationError = this.$__.validationError;
2605
- this.$__.validationError = undefined;
2641
+ this.$__.validationError = null;
2642
+ this.$__.validating = null;
2606
2643
 
2607
2644
  if (shouldValidateModifiedOnly && validationError != null) {
2608
2645
  // Remove any validation errors that aren't from modified paths
@@ -2648,6 +2685,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2648
2685
  } else if (pathsToSkip) {
2649
2686
  paths = _handlePathsToSkip(paths, pathsToSkip);
2650
2687
  }
2688
+
2651
2689
  if (paths.length === 0) {
2652
2690
  return immediate(function() {
2653
2691
  const error = _complete();
@@ -2,14 +2,32 @@
2
2
 
3
3
  const utils = require('../../utils');
4
4
 
5
+ const keysToSkip = new Set(['__index', '__parentArray', '_doc']);
6
+
5
7
  /**
6
8
  * Using spread operator on a Mongoose document gives you a
7
9
  * POJO that has a tendency to cause infinite recursion. So
8
10
  * we use this function on `set()` to prevent that.
9
11
  */
10
12
 
11
- module.exports = function handleSpreadDoc(v) {
13
+ module.exports = function handleSpreadDoc(v, includeExtraKeys) {
12
14
  if (utils.isPOJO(v) && v.$__ != null && v._doc != null) {
15
+ if (includeExtraKeys) {
16
+ const extraKeys = {};
17
+ for (const key of Object.keys(v)) {
18
+ if (typeof key === 'symbol') {
19
+ continue;
20
+ }
21
+ if (key[0] === '$') {
22
+ continue;
23
+ }
24
+ if (keysToSkip.has(key)) {
25
+ continue;
26
+ }
27
+ extraKeys[key] = v[key];
28
+ }
29
+ return { ...v._doc, ...extraKeys };
30
+ }
13
31
  return v._doc;
14
32
  }
15
33
 
@@ -41,7 +41,6 @@ module.exports = function castFilterPath(query, schematype, val) {
41
41
  }
42
42
  continue;
43
43
  }
44
- // cast(schematype.caster ? schematype.caster.schema : schema, nested, options, context);
45
44
  } else {
46
45
  val[$cond] = schematype.castForQueryWrapper({
47
46
  $conditional: $cond,
package/lib/index.js CHANGED
@@ -24,7 +24,6 @@ const legacyPluralize = require('./helpers/pluralize');
24
24
  const utils = require('./utils');
25
25
  const pkg = require('../package.json');
26
26
  const cast = require('./cast');
27
- const clearValidating = require('./plugins/clearValidating');
28
27
  const removeSubdocs = require('./plugins/removeSubdocs');
29
28
  const saveSubdocs = require('./plugins/saveSubdocs');
30
29
  const trackTransaction = require('./plugins/trackTransaction');
@@ -107,8 +106,7 @@ function Mongoose(options) {
107
106
  [validateBeforeSave, { deduplicate: true }],
108
107
  [shardingPlugin, { deduplicate: true }],
109
108
  [removeSubdocs, { deduplicate: true }],
110
- [trackTransaction, { deduplicate: true }],
111
- [clearValidating, { deduplicate: true }]
109
+ [trackTransaction, { deduplicate: true }]
112
110
  ]
113
111
  });
114
112
  }
package/lib/model.js CHANGED
@@ -245,8 +245,7 @@ function _applyCustomWhere(doc, where) {
245
245
  */
246
246
 
247
247
  Model.prototype.$__handleSave = function(options, callback) {
248
- const _this = this;
249
- let saveOptions = {};
248
+ const saveOptions = {};
250
249
 
251
250
  applyWriteConcern(this.$__schema, options);
252
251
  if (typeof options.writeConcern !== 'undefined') {
@@ -274,14 +273,10 @@ Model.prototype.$__handleSave = function(options, callback) {
274
273
  if ('checkKeys' in options) {
275
274
  saveOptions.checkKeys = options.checkKeys;
276
275
  }
277
- const session = this.$session();
278
276
  if (!saveOptions.hasOwnProperty('session')) {
279
- saveOptions.session = session;
277
+ saveOptions.session = this.$session();
280
278
  }
281
279
 
282
- if (Object.keys(saveOptions).length === 0) {
283
- saveOptions = null;
284
- }
285
280
  if (this.$isNew) {
286
281
  // send entire doc
287
282
  const obj = this.toObject(saveToObjectOptions);
@@ -298,9 +293,9 @@ Model.prototype.$__handleSave = function(options, callback) {
298
293
  }
299
294
 
300
295
  this.$__version(true, obj);
301
- this[modelCollectionSymbol].insertOne(obj, saveOptions, function(err, ret) {
296
+ this[modelCollectionSymbol].insertOne(obj, saveOptions, (err, ret) => {
302
297
  if (err) {
303
- _setIsNew(_this, true);
298
+ _setIsNew(this, true);
304
299
 
305
300
  callback(err, null);
306
301
  return;
@@ -308,64 +303,67 @@ Model.prototype.$__handleSave = function(options, callback) {
308
303
 
309
304
  callback(null, ret);
310
305
  });
306
+
311
307
  this.$__reset();
312
308
  _setIsNew(this, false);
313
309
  // Make it possible to retry the insert
314
310
  this.$__.inserting = true;
315
- } else {
316
- // Make sure we don't treat it as a new object on error,
317
- // since it already exists
318
- this.$__.inserting = false;
319
-
320
- const delta = this.$__delta();
321
- if (delta) {
322
- if (delta instanceof MongooseError) {
323
- callback(delta);
324
- return;
325
- }
326
311
 
327
- const where = this.$__where(delta[0]);
328
- if (where instanceof MongooseError) {
329
- callback(where);
330
- return;
331
- }
312
+ return;
313
+ }
332
314
 
333
- _applyCustomWhere(this, where);
334
- this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions, (err, ret) => {
335
- if (err) {
336
- this.$__undoReset();
315
+ // Make sure we don't treat it as a new object on error,
316
+ // since it already exists
317
+ this.$__.inserting = false;
337
318
 
338
- callback(err);
339
- return;
340
- }
341
- ret.$where = where;
342
- callback(null, ret);
343
- });
344
- } else {
345
- const optionsWithCustomValues = Object.assign({}, options, saveOptions);
346
- const where = this.$__where();
347
- if (this.$__schema.options.optimisticConcurrency) {
348
- const key = this.$__schema.options.versionKey;
349
- const val = this.$__getValue(key);
350
- if (val != null) {
351
- where[key] = val;
352
- }
353
- }
354
- this.constructor.exists(where, optionsWithCustomValues)
355
- .then(documentExists => {
356
- const matchedCount = !documentExists ? 0 : 1;
357
- callback(null, { $where: where, matchedCount });
358
- })
359
- .catch(callback);
319
+ const delta = this.$__delta();
320
+ if (delta) {
321
+ if (delta instanceof MongooseError) {
322
+ callback(delta);
360
323
  return;
361
324
  }
362
325
 
363
- // store the modified paths before the document is reset
364
- this.$__.modifiedPaths = this.modifiedPaths();
365
- this.$__reset();
326
+ const where = this.$__where(delta[0]);
327
+ if (where instanceof MongooseError) {
328
+ callback(where);
329
+ return;
330
+ }
366
331
 
367
- _setIsNew(this, false);
332
+ _applyCustomWhere(this, where);
333
+ this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions, (err, ret) => {
334
+ if (err) {
335
+ this.$__undoReset();
336
+
337
+ callback(err);
338
+ return;
339
+ }
340
+ ret.$where = where;
341
+ callback(null, ret);
342
+ });
343
+ } else {
344
+ const optionsWithCustomValues = Object.assign({}, options, saveOptions);
345
+ const where = this.$__where();
346
+ if (this.$__schema.options.optimisticConcurrency) {
347
+ const key = this.$__schema.options.versionKey;
348
+ const val = this.$__getValue(key);
349
+ if (val != null) {
350
+ where[key] = val;
351
+ }
352
+ }
353
+ this.constructor.exists(where, optionsWithCustomValues)
354
+ .then(documentExists => {
355
+ const matchedCount = !documentExists ? 0 : 1;
356
+ callback(null, { $where: where, matchedCount });
357
+ })
358
+ .catch(callback);
359
+ return;
368
360
  }
361
+
362
+ // store the modified paths before the document is reset
363
+ this.$__.modifiedPaths = this.modifiedPaths();
364
+ this.$__reset();
365
+
366
+ _setIsNew(this, false);
369
367
  };
370
368
 
371
369
  /*!
@@ -374,8 +372,8 @@ Model.prototype.$__handleSave = function(options, callback) {
374
372
 
375
373
  Model.prototype.$__save = function(options, callback) {
376
374
  this.$__handleSave(options, (error, result) => {
377
- const hooks = this.$__schema.s.hooks;
378
375
  if (error) {
376
+ const hooks = this.$__schema.s.hooks;
379
377
  return hooks.execPost('save:error', this, [this], { error: error }, (error) => {
380
378
  callback(error, this);
381
379
  });
@@ -423,6 +421,7 @@ Model.prototype.$__save = function(options, callback) {
423
421
  this.$__undoReset();
424
422
  error = new DocumentNotFoundError(result.$where,
425
423
  this.constructor.modelName, numAffected, result);
424
+ const hooks = this.$__schema.s.hooks;
426
425
  return hooks.execPost('save:error', this, [this], { error: error }, (error) => {
427
426
  callback(error, this);
428
427
  });
@@ -516,9 +515,9 @@ Model.prototype.save = function(options, fn) {
516
515
  this.$__.saveOptions = options;
517
516
 
518
517
  this.$__save(options, error => {
519
- this.$__.saving = undefined;
520
- delete this.$__.saveOptions;
521
- delete this.$__.$versionError;
518
+ this.$__.saving = null;
519
+ this.$__.saveOptions = null;
520
+ this.$__.$versionError = null;
522
521
  this.$op = null;
523
522
 
524
523
  if (error) {
@@ -1375,6 +1374,16 @@ Model.createCollection = function createCollection(options, callback) {
1375
1374
  this.schema.options.timeseries;
1376
1375
  if (timeseries != null) {
1377
1376
  options = Object.assign({ timeseries }, options);
1377
+ if (options.expireAfterSeconds != null) {
1378
+ // do nothing
1379
+ } else if (options.expires != null) {
1380
+ utils.expires(options);
1381
+ } else if (this.schema.options.expireAfterSeconds != null) {
1382
+ options.expireAfterSeconds = this.schema.options.expireAfterSeconds;
1383
+ } else if (this.schema.options.expires != null) {
1384
+ options.expires = this.schema.options.expires;
1385
+ utils.expires(options);
1386
+ }
1378
1387
  }
1379
1388
 
1380
1389
  callback = this.$handleCallbackError(callback);
@@ -1875,7 +1884,7 @@ Model.discriminators;
1875
1884
  * ####Note:
1876
1885
  * Only translate arguments of object type anything else is returned raw
1877
1886
  *
1878
- * @param {Object} raw fields/conditions that may contain aliased keys
1887
+ * @param {Object} fields fields/conditions that may contain aliased keys
1879
1888
  * @return {Object} the translated 'pure' fields/conditions
1880
1889
  */
1881
1890
  Model.translateAliases = function translateAliases(fields) {
@@ -2836,11 +2845,11 @@ Model.findByIdAndDelete = function(id, options, callback) {
2836
2845
  *
2837
2846
  * ####Examples:
2838
2847
  *
2839
- * A.findOneAndReplace(conditions, options, callback) // executes
2840
- * A.findOneAndReplace(conditions, options) // return Query
2841
- * A.findOneAndReplace(conditions, callback) // executes
2842
- * A.findOneAndReplace(conditions) // returns Query
2843
- * A.findOneAndReplace() // returns Query
2848
+ * A.findOneAndReplace(filter, replacement, options, callback) // executes
2849
+ * A.findOneAndReplace(filter, replacement, options) // return Query
2850
+ * A.findOneAndReplace(filter, replacement, callback) // executes
2851
+ * A.findOneAndReplace(filter, replacement) // returns Query
2852
+ * A.findOneAndReplace() // returns Query
2844
2853
  *
2845
2854
  * @param {Object} filter Replace the first document that matches this filter
2846
2855
  * @param {Object} [replacement] Replace with this document
@@ -2861,8 +2870,10 @@ Model.findOneAndReplace = function(filter, replacement, options, callback) {
2861
2870
 
2862
2871
  if (arguments.length === 1 && typeof filter === 'function') {
2863
2872
  const msg = 'Model.findOneAndReplace(): First argument must not be a function.\n\n'
2864
- + ' ' + this.modelName + '.findOneAndReplace(conditions, callback)\n'
2865
- + ' ' + this.modelName + '.findOneAndReplace(conditions)\n'
2873
+ + ' ' + this.modelName + '.findOneAndReplace(filter, replacement, options, callback)\n'
2874
+ + ' ' + this.modelName + '.findOneAndReplace(filter, replacement, callback)\n'
2875
+ + ' ' + this.modelName + '.findOneAndReplace(filter, replacement)\n'
2876
+ + ' ' + this.modelName + '.findOneAndReplace(filter, callback)\n'
2866
2877
  + ' ' + this.modelName + '.findOneAndReplace()\n';
2867
2878
  throw new TypeError(msg);
2868
2879
  }
@@ -48,6 +48,13 @@ Object.defineProperty(SchemaDateOptions.prototype, 'max', opts);
48
48
  /**
49
49
  * If set, Mongoose creates a TTL index on this path.
50
50
  *
51
+ * mongo TTL index `expireAfterSeconds` value will take 'expires' value expressed in seconds.
52
+ *
53
+ * ####Example:
54
+ *
55
+ * const schema = new Schema({ "expireAt": { type: Date, expires: 11 } });
56
+ * // if 'expireAt' is set, then document expires at expireAt + 11 seconds
57
+ *
51
58
  * @api public
52
59
  * @property expires
53
60
  * @memberOf SchemaDateOptions
@@ -61,4 +68,4 @@ Object.defineProperty(SchemaDateOptions.prototype, 'expires', opts);
61
68
  * ignore
62
69
  */
63
70
 
64
- module.exports = SchemaDateOptions;
71
+ module.exports = SchemaDateOptions;
package/lib/query.js CHANGED
@@ -39,6 +39,31 @@ const utils = require('./utils');
39
39
  const validOps = require('./helpers/query/validOps');
40
40
  const wrapThunk = require('./helpers/query/wrapThunk');
41
41
 
42
+ const queryOptionMethods = new Set([
43
+ 'allowDiskUse',
44
+ 'batchSize',
45
+ 'collation',
46
+ 'comment',
47
+ 'explain',
48
+ 'hint',
49
+ 'j',
50
+ 'lean',
51
+ 'limit',
52
+ 'maxScan',
53
+ 'maxTimeMS',
54
+ 'maxscan',
55
+ 'projection',
56
+ 'read',
57
+ 'select',
58
+ 'skip',
59
+ 'slice',
60
+ 'sort',
61
+ 'tailable',
62
+ 'w',
63
+ 'writeConcern',
64
+ 'wtimeout'
65
+ ]);
66
+
42
67
  /**
43
68
  * Query constructor used for building queries. You do not need
44
69
  * to instantiate a `Query` directly. Instead use Model functions like
@@ -1630,7 +1655,19 @@ Query.prototype.setOptions = function(options, overwrite) {
1630
1655
  }
1631
1656
  }
1632
1657
 
1633
- return Query.base.setOptions.call(this, options);
1658
+ // set arbitrary options
1659
+ for (const key of Object.keys(options)) {
1660
+ if (queryOptionMethods.has(key)) {
1661
+ const args = Array.isArray(options[key]) ?
1662
+ options[key] :
1663
+ [options[key]];
1664
+ this[key].apply(this, args);
1665
+ } else {
1666
+ this.options[key] = options[key];
1667
+ }
1668
+ }
1669
+
1670
+ return this;
1634
1671
  };
1635
1672
 
1636
1673
  /**
@@ -3168,23 +3205,14 @@ Query.prototype._deleteMany = wrapThunk(function(callback) {
3168
3205
  */
3169
3206
 
3170
3207
  function completeOne(model, doc, res, options, fields, userProvidedFields, pop, callback) {
3171
- const opts = pop ?
3172
- { populated: pop }
3173
- : undefined;
3174
-
3175
3208
  if (options.rawResult && doc == null) {
3176
3209
  _init(null);
3177
3210
  return null;
3178
3211
  }
3179
3212
 
3180
- const casted = helpers.createModel(model, doc, fields, userProvidedFields, options);
3181
- try {
3182
- casted.$init(doc, opts, _init);
3183
- } catch (error) {
3184
- _init(error);
3185
- }
3213
+ helpers.createModelAndInit(model, doc, fields, userProvidedFields, options, pop, _init);
3186
3214
 
3187
- function _init(err) {
3215
+ function _init(err, casted) {
3188
3216
  if (err) {
3189
3217
  return immediate(() => callback(err));
3190
3218
  }
@@ -5705,4 +5733,4 @@ Query.prototype.model;
5705
5733
  * Export
5706
5734
  */
5707
5735
 
5708
- module.exports = Query;
5736
+ module.exports = Query;
@@ -123,6 +123,23 @@ exports.createModel = function createModel(model, doc, fields, userProvidedField
123
123
  return new model(undefined, fields, _opts);
124
124
  };
125
125
 
126
+ /*!
127
+ * ignore
128
+ */
129
+
130
+ exports.createModelAndInit = function createModelAndInit(model, doc, fields, userProvidedFields, options, populatedIds, callback) {
131
+ const initOpts = populatedIds ?
132
+ { populated: populatedIds } :
133
+ undefined;
134
+
135
+ const casted = exports.createModel(model, doc, fields, userProvidedFields, options);
136
+ try {
137
+ casted.$init(doc, initOpts, callback);
138
+ } catch (error) {
139
+ callback(error, casted);
140
+ }
141
+ };
142
+
126
143
  /*!
127
144
  * ignore
128
145
  */
@@ -26,7 +26,7 @@ module.exports = SubdocumentPath;
26
26
  * Single nested subdocument SchemaType constructor.
27
27
  *
28
28
  * @param {Schema} schema
29
- * @param {String} key
29
+ * @param {String} path
30
30
  * @param {Object} options
31
31
  * @inherits SchemaType
32
32
  * @api public
@@ -243,6 +243,9 @@ SubdocumentPath.prototype.doValidate = function(value, fn, scope, options) {
243
243
  }
244
244
 
245
245
  if (options && options.skipSchemaValidators) {
246
+ if (!value) {
247
+ return fn(null);
248
+ }
246
249
  return value.validate(fn);
247
250
  }
248
251
 
@@ -32,6 +32,7 @@ const emptyOpts = Object.freeze({});
32
32
  * @param {String} key
33
33
  * @param {SchemaType} cast
34
34
  * @param {Object} options
35
+ * @param {Object} schemaOptions
35
36
  * @inherits SchemaType
36
37
  * @api public
37
38
  */