mongoose 6.2.11 → 6.3.2

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 (49) hide show
  1. package/.eslintrc.json +157 -157
  2. package/README.md +1 -1
  3. package/dist/browser.umd.js +2 -1704
  4. package/lib/aggregate.js +4 -3
  5. package/lib/collection.js +0 -7
  6. package/lib/connection.js +2 -1
  7. package/lib/cursor/ChangeStream.js +42 -2
  8. package/lib/cursor/QueryCursor.js +2 -0
  9. package/lib/document.js +22 -26
  10. package/lib/error/cast.js +8 -2
  11. package/lib/error/eachAsyncMultiError.js +41 -0
  12. package/lib/helpers/common.js +0 -8
  13. package/lib/helpers/cursor/eachAsync.js +44 -12
  14. package/lib/helpers/immediate.js +3 -1
  15. package/lib/helpers/isAsyncFunction.js +19 -7
  16. package/lib/helpers/model/discriminator.js +1 -3
  17. package/lib/helpers/populate/getModelsMapForPopulate.js +13 -10
  18. package/lib/helpers/query/applyGlobalOption.js +29 -0
  19. package/lib/helpers/schematype/handleImmutable.js +4 -1
  20. package/lib/helpers/timestamps/setupTimestamps.js +2 -2
  21. package/lib/index.js +11 -4
  22. package/lib/internal.js +0 -1
  23. package/lib/model.js +39 -27
  24. package/lib/query.js +23 -4
  25. package/lib/queryhelpers.js +11 -1
  26. package/lib/schema/SubdocumentPath.js +3 -15
  27. package/lib/schema/documentarray.js +7 -7
  28. package/lib/schema/number.js +1 -5
  29. package/lib/schema/objectid.js +2 -4
  30. package/lib/schema/string.js +1 -5
  31. package/lib/schema.js +115 -2
  32. package/lib/schematype.js +2 -2
  33. package/lib/types/DocumentArray/methods/index.js +9 -1
  34. package/lib/types/array/methods/index.js +8 -9
  35. package/lib/validoptions.js +1 -0
  36. package/package.json +25 -18
  37. package/tools/repl.js +2 -1
  38. package/tsconfig.json +2 -2
  39. package/types/aggregate.d.ts +1 -2
  40. package/types/connection.d.ts +4 -5
  41. package/types/cursor.d.ts +13 -6
  42. package/types/document.d.ts +17 -14
  43. package/types/error.d.ts +124 -124
  44. package/types/index.d.ts +350 -202
  45. package/types/mongooseoptions.d.ts +11 -6
  46. package/types/schemaoptions.d.ts +1 -2
  47. package/CHANGELOG.md +0 -7238
  48. package/History.md +0 -1
  49. package/lib/helpers/query/applyGlobalMaxTimeMS.js +0 -15
package/lib/aggregate.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  const AggregationCursor = require('./cursor/AggregationCursor');
8
8
  const Query = require('./query');
9
- const applyGlobalMaxTimeMS = require('./helpers/query/applyGlobalMaxTimeMS');
9
+ const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption');
10
10
  const getConstructorName = require('./helpers/getConstructorName');
11
11
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
12
12
  const promiseOrCallback = require('./helpers/promiseOrCallback');
@@ -15,6 +15,8 @@ const utils = require('./utils');
15
15
  const read = Query.prototype.read;
16
16
  const readConcern = Query.prototype.readConcern;
17
17
 
18
+ const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']);
19
+
18
20
  /**
19
21
  * Aggregate constructor used for building aggregation pipelines. Do not
20
22
  * instantiate this class directly, use [Model.aggregate()](/docs/api.html#model_Model.aggregate) instead.
@@ -672,8 +674,6 @@ Aggregate.prototype.readConcern = function(level) {
672
674
  * @api public
673
675
  */
674
676
 
675
- const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']);
676
-
677
677
  Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) {
678
678
  if (arguments.length === 3) {
679
679
  if ((typeof thenExpr === 'string' && !validRedactStringValues.has(thenExpr)) ||
@@ -973,6 +973,7 @@ Aggregate.prototype.exec = function(callback) {
973
973
  const collection = this._model.collection;
974
974
 
975
975
  applyGlobalMaxTimeMS(this.options, model);
976
+ applyGlobalDiskUse(this.options, model);
976
977
 
977
978
  if (this.options && this.options.cursor) {
978
979
  return new AggregationCursor(this);
package/lib/collection.js CHANGED
@@ -23,13 +23,6 @@ function Collection(name, conn, opts) {
23
23
  if (opts === void 0) {
24
24
  opts = {};
25
25
  }
26
- if (opts.capped === void 0) {
27
- opts.capped = {};
28
- }
29
-
30
- if (typeof opts.capped === 'number') {
31
- opts.capped = { size: opts.capped };
32
- }
33
26
 
34
27
  this.opts = opts;
35
28
  this.name = name;
package/lib/connection.js CHANGED
@@ -1357,7 +1357,8 @@ Connection.prototype.optionsProvideAuthenticationData = function(options) {
1357
1357
  * that this connection uses to talk to MongoDB.
1358
1358
  *
1359
1359
  * #### Example:
1360
- * const conn = await mongoose.createConnection('mongodb://localhost:27017/test');
1360
+ * const conn = await mongoose.createConnection('mongodb://localhost:27017/test').
1361
+ * asPromise();
1361
1362
  *
1362
1363
  * conn.getClient(); // MongoClient { ... }
1363
1364
  *
@@ -16,6 +16,7 @@ class ChangeStream extends EventEmitter {
16
16
 
17
17
  this.driverChangeStream = null;
18
18
  this.closed = false;
19
+ this.bindedEvents = false;
19
20
  this.pipeline = pipeline;
20
21
  this.options = options;
21
22
 
@@ -27,21 +28,60 @@ class ChangeStream extends EventEmitter {
27
28
  }
28
29
 
29
30
  this.driverChangeStream = driverChangeStream;
30
- this._bindEvents();
31
31
  this.emit('ready');
32
32
  });
33
33
  }
34
34
 
35
35
  _bindEvents() {
36
+ if (this.bindedEvents) {
37
+ return;
38
+ }
39
+
40
+ this.bindedEvents = true;
41
+
42
+ if (this.driverChangeStream == null) {
43
+ this.once('ready', () => {
44
+ this.driverChangeStream.on('close', () => {
45
+ this.closed = true;
46
+ });
47
+
48
+ ['close', 'change', 'end', 'error'].forEach(ev => {
49
+ this.driverChangeStream.on(ev, data => this.emit(ev, data));
50
+ });
51
+ });
52
+
53
+ return;
54
+ }
55
+
36
56
  this.driverChangeStream.on('close', () => {
37
57
  this.closed = true;
38
58
  });
39
59
 
40
60
  ['close', 'change', 'end', 'error'].forEach(ev => {
41
- this.driverChangeStream.on(ev, data => this.emit(ev, data));
61
+ this.driverChangeStream.on(ev, data => {
62
+ this.emit(ev, data);
63
+ });
42
64
  });
43
65
  }
44
66
 
67
+ hasNext(cb) {
68
+ return this.driverChangeStream.hasNext(cb);
69
+ }
70
+
71
+ next(cb) {
72
+ return this.driverChangeStream.next(cb);
73
+ }
74
+
75
+ on(event, handler) {
76
+ this._bindEvents();
77
+ return super.on(event, handler);
78
+ }
79
+
80
+ once(event, handler) {
81
+ this._bindEvents();
82
+ return super.once(event, handler);
83
+ }
84
+
45
85
  _queue(cb) {
46
86
  this.once('ready', () => cb());
47
87
  }
@@ -225,6 +225,8 @@ QueryCursor.prototype.next = function(callback) {
225
225
  * @param {Function} fn
226
226
  * @param {Object} [options]
227
227
  * @param {Number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
228
+ * @param {Number} [options.batchSize] if set, will call `fn()` with arrays of documents with length at most `batchSize`
229
+ * @param {Boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error.
228
230
  * @param {Function} [callback] executed when all docs have been processed
229
231
  * @return {Promise}
230
232
  * @api public
package/lib/document.js CHANGED
@@ -1048,7 +1048,6 @@ Document.prototype.$set = function $set(path, val, type, options) {
1048
1048
  const merge = options.merge;
1049
1049
  const adhoc = type && type !== true;
1050
1050
  const constructing = type === true;
1051
- const typeKey = this.$__schema.options.typeKey;
1052
1051
  let adhocs;
1053
1052
  let keys;
1054
1053
  let i = 0;
@@ -1150,14 +1149,15 @@ Document.prototype.$set = function $set(path, val, type, options) {
1150
1149
  }
1151
1150
  }
1152
1151
 
1153
- // Ensure all properties are in correct order by deleting and recreating every property.
1154
- for (const key of Object.keys(this.$__schema.tree)) {
1155
- if (this._doc.hasOwnProperty(key)) {
1156
- const val = this._doc[key];
1157
- delete this._doc[key];
1158
- this._doc[key] = val;
1159
- }
1152
+ // Ensure all properties are in correct order
1153
+ const orderedDoc = {};
1154
+ const orderedKeys = Object.keys(this.$__schema.tree);
1155
+ for (let i = 0, len = orderedKeys.length; i < len; ++i) {
1156
+ (key = orderedKeys[i]) &&
1157
+ (this._doc.hasOwnProperty(key)) &&
1158
+ (orderedDoc[key] = undefined);
1160
1159
  }
1160
+ this._doc = Object.assign(orderedDoc, this._doc);
1161
1161
 
1162
1162
  return this;
1163
1163
  }
@@ -1381,6 +1381,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1381
1381
  }
1382
1382
 
1383
1383
  let popOpts;
1384
+ const typeKey = this.$__schema.options.typeKey;
1384
1385
  if (schema.options &&
1385
1386
  Array.isArray(schema.options[typeKey]) &&
1386
1387
  schema.options[typeKey].length &&
@@ -1400,7 +1401,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1400
1401
  // later in `$__set()` because we don't take `_doc` when we iterate through
1401
1402
  // a single nested doc. That's to make sure we get the correct context.
1402
1403
  // Otherwise we would double-call the setter, see gh-7196.
1403
- val = schema.applySetters(val, this, false, priorVal);
1404
+ val = schema.applySetters(val, this, false, priorVal, options);
1404
1405
  }
1405
1406
 
1406
1407
  if (Array.isArray(val) &&
@@ -1454,14 +1455,18 @@ Document.prototype.$set = function $set(path, val, type, options) {
1454
1455
  }
1455
1456
 
1456
1457
  if (shouldSet) {
1457
- const doc = this.$isSubdocument ? this.ownerDocument() : this;
1458
- const savedState = doc.$__.savedState;
1459
- const savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path;
1460
- if (savedState != null) {
1461
- const firstDot = savedStatePath.indexOf('.');
1462
- const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot);
1463
- if (!savedState.hasOwnProperty(topLevelPath)) {
1464
- savedState[topLevelPath] = utils.clone(doc.$__getValue(topLevelPath));
1458
+ let savedState = null;
1459
+ let savedStatePath = null;
1460
+ if (!constructing) {
1461
+ const doc = this.$isSubdocument ? this.ownerDocument() : this;
1462
+ savedState = doc.$__.savedState;
1463
+ savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path;
1464
+ if (savedState != null) {
1465
+ const firstDot = savedStatePath.indexOf('.');
1466
+ const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot);
1467
+ if (!savedState.hasOwnProperty(topLevelPath)) {
1468
+ savedState[topLevelPath] = utils.clone(doc.$__getValue(topLevelPath));
1469
+ }
1465
1470
  }
1466
1471
  }
1467
1472
 
@@ -1618,7 +1623,6 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1618
1623
 
1619
1624
  const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts,
1620
1625
  schema, val, priorVal);
1621
- const _this = this;
1622
1626
 
1623
1627
  if (shouldModify) {
1624
1628
  this.markModified(pathToMark);
@@ -1634,14 +1638,6 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1634
1638
  item && item.__parentArray && (item.__parentArray = val);
1635
1639
  });
1636
1640
  }
1637
-
1638
- // Small hack for gh-1638: if we're overwriting the entire array, ignore
1639
- // paths that were modified before the array overwrite
1640
- this.$__.activePaths.forEach(function(modifiedPath) {
1641
- if (modifiedPath.startsWith(path + '.')) {
1642
- _this.$__.activePaths.ignore(modifiedPath);
1643
- }
1644
- });
1645
1641
  }
1646
1642
  } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) {
1647
1643
  val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol];
package/lib/error/cast.js CHANGED
@@ -23,7 +23,7 @@ class CastError extends MongooseError {
23
23
  const stringValue = getStringValue(value);
24
24
  const valueType = getValueType(value);
25
25
  const messageFormat = getMessageFormat(schemaType);
26
- const msg = formatMessage(null, type, stringValue, path, messageFormat, valueType);
26
+ const msg = formatMessage(null, type, stringValue, path, messageFormat, valueType, reason);
27
27
  super(msg);
28
28
  this.init(type, value, path, reason, schemaType);
29
29
  } else {
@@ -122,7 +122,7 @@ function getMessageFormat(schemaType) {
122
122
  * ignore
123
123
  */
124
124
 
125
- function formatMessage(model, kind, stringValue, path, messageFormat, valueType) {
125
+ function formatMessage(model, kind, stringValue, path, messageFormat, valueType, reason) {
126
126
  if (messageFormat != null) {
127
127
  let ret = messageFormat.
128
128
  replace('{KIND}', kind).
@@ -140,6 +140,12 @@ function formatMessage(model, kind, stringValue, path, messageFormat, valueType)
140
140
  if (model != null) {
141
141
  ret += ' for model "' + model.modelName + '"';
142
142
  }
143
+ if (reason != null &&
144
+ typeof reason.constructor === 'function' &&
145
+ reason.constructor.name !== 'AssertionError' &&
146
+ reason.constructor.name !== 'Error') {
147
+ ret += ' because of "' + reason.constructor.name + '"';
148
+ }
143
149
  return ret;
144
150
  }
145
151
  }
@@ -0,0 +1,41 @@
1
+ /*!
2
+ * Module dependencies.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const MongooseError = require('./');
8
+
9
+
10
+ /**
11
+ * If `eachAsync()` is called with `continueOnError: true`, there can be
12
+ * multiple errors. This error class contains an `errors` property, which
13
+ * contains an array of all errors that occurred in `eachAsync()`.
14
+ *
15
+ * @api private
16
+ */
17
+
18
+ class EachAsyncMultiError extends MongooseError {
19
+ /**
20
+ * @param {String} connectionString
21
+ */
22
+ constructor(errors) {
23
+ let preview = errors.map(e => e.message).join(', ');
24
+ if (preview.length > 50) {
25
+ preview = preview.slice(0, 50) + '...';
26
+ }
27
+ super(`eachAsync() finished with ${errors.length} errors: ${preview}`);
28
+
29
+ this.errors = errors;
30
+ }
31
+ }
32
+
33
+ Object.defineProperty(EachAsyncMultiError.prototype, 'name', {
34
+ value: 'EachAsyncMultiError'
35
+ });
36
+
37
+ /*!
38
+ * exports
39
+ */
40
+
41
+ module.exports = EachAsyncMultiError;
@@ -80,14 +80,6 @@ function modifiedPaths(update, path, result) {
80
80
 
81
81
  const _path = path + key;
82
82
  result[_path] = true;
83
- if (_path.indexOf('.') !== -1) {
84
- const sp = _path.split('.');
85
- let cur = sp[0];
86
- for (let i = 1; i < sp.length; ++i) {
87
- result[cur] = true;
88
- cur += '.' + sp[i];
89
- }
90
- }
91
83
  if (!Buffer.isBuffer(val) && isMongooseObject(val)) {
92
84
  val = val.toObject({ transform: false, virtuals: false });
93
85
  }
@@ -4,6 +4,7 @@
4
4
  * Module dependencies.
5
5
  */
6
6
 
7
+ const EachAsyncMultiError = require('../../error/eachAsyncMultiError');
7
8
  const immediate = require('../immediate');
8
9
  const promiseOrCallback = require('../promiseOrCallback');
9
10
 
@@ -24,10 +25,11 @@ const promiseOrCallback = require('../promiseOrCallback');
24
25
  module.exports = function eachAsync(next, fn, options, callback) {
25
26
  const parallel = options.parallel || 1;
26
27
  const batchSize = options.batchSize;
28
+ const continueOnError = options.continueOnError;
29
+ const aggregatedErrors = [];
27
30
  const enqueue = asyncQueue();
28
31
 
29
32
  return promiseOrCallback(callback, cb => {
30
-
31
33
  if (batchSize != null) {
32
34
  if (typeof batchSize !== 'number') {
33
35
  throw new TypeError('batchSize must be a number');
@@ -62,14 +64,22 @@ module.exports = function eachAsync(next, fn, options, callback) {
62
64
  return done();
63
65
  }
64
66
  if (err != null) {
65
- error = err;
66
- finalCallback(err);
67
- return done();
67
+ if (continueOnError) {
68
+ aggregatedErrors.push(err);
69
+ } else {
70
+ error = err;
71
+ finalCallback(err);
72
+ return done();
73
+ }
68
74
  }
69
75
  if (doc == null) {
70
76
  drained = true;
71
77
  if (handleResultsInProgress <= 0) {
72
- finalCallback(null);
78
+ const finalErr = continueOnError ?
79
+ createEachAsyncMultiError(aggregatedErrors) :
80
+ error;
81
+
82
+ finalCallback(finalErr);
73
83
  } else if (batchSize && documentsBatch.length) {
74
84
  handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack);
75
85
  }
@@ -102,11 +112,18 @@ module.exports = function eachAsync(next, fn, options, callback) {
102
112
  --handleResultsInProgress;
103
113
  }
104
114
  if (err != null) {
105
- error = err;
106
- return finalCallback(err);
115
+ if (continueOnError) {
116
+ aggregatedErrors.push(err);
117
+ } else {
118
+ error = err;
119
+ return finalCallback(err);
120
+ }
107
121
  }
108
122
  if (drained && handleResultsInProgress <= 0) {
109
- return finalCallback(null);
123
+ const finalErr = continueOnError ?
124
+ createEachAsyncMultiError(aggregatedErrors) :
125
+ error;
126
+ return finalCallback(finalErr);
110
127
  }
111
128
 
112
129
  immediate(() => enqueue(fetch));
@@ -118,11 +135,18 @@ module.exports = function eachAsync(next, fn, options, callback) {
118
135
  }
119
136
 
120
137
  function handleNextResult(doc, i, callback) {
121
- const promise = fn(doc, i);
122
- if (promise && typeof promise.then === 'function') {
123
- promise.then(
138
+ let maybePromise;
139
+ try {
140
+ maybePromise = fn(doc, i);
141
+ } catch (err) {
142
+ return callback(err);
143
+ }
144
+ if (maybePromise && typeof maybePromise.then === 'function') {
145
+ maybePromise.then(
124
146
  function() { callback(null); },
125
- function(error) { callback(error || new Error('`eachAsync()` promise rejected without error')); });
147
+ function(error) {
148
+ callback(error || new Error('`eachAsync()` promise rejected without error'));
149
+ });
126
150
  } else {
127
151
  callback(null);
128
152
  }
@@ -158,3 +182,11 @@ function asyncQueue() {
158
182
  }
159
183
  }
160
184
  }
185
+
186
+ function createEachAsyncMultiError(aggregatedErrors) {
187
+ if (aggregatedErrors.length === 0) {
188
+ return null;
189
+ }
190
+
191
+ return new EachAsyncMultiError(aggregatedErrors);
192
+ }
@@ -7,7 +7,9 @@
7
7
 
8
8
  'use strict';
9
9
 
10
- const nextTick = process.nextTick.bind(process);
10
+ const nextTick = typeof process !== 'undefined' && typeof process.nextTick === 'function' ?
11
+ process.nextTick.bind(process) :
12
+ cb => setTimeout(cb, 0); // Fallback for browser build
11
13
 
12
14
  module.exports = function immediate(cb) {
13
15
  return nextTick(cb);
@@ -1,10 +1,22 @@
1
1
  'use strict';
2
2
 
3
- const asyncFunctionPrototype = Object.getPrototypeOf(async function() {});
3
+ let asyncFunctionPrototype = null;
4
+ // try/catch for Babel compatibility, because Babel preset-env requires
5
+ // regenerator-runtime for async/await and we don't want to include that
6
+ // for a simple check.
7
+ try {
8
+ asyncFunctionPrototype = Object.getPrototypeOf(async function() {});
9
+ } catch (err) {}
4
10
 
5
- module.exports = function isAsyncFunction(v) {
6
- return (
7
- typeof v === 'function' &&
8
- Object.getPrototypeOf(v) === asyncFunctionPrototype
9
- );
10
- };
11
+ if (asyncFunctionPrototype == null) {
12
+ module.exports = function isAsyncFunction() {
13
+ return false;
14
+ };
15
+ } else {
16
+ module.exports = function isAsyncFunction(v) {
17
+ return (
18
+ typeof v === 'function' &&
19
+ Object.getPrototypeOf(v) === asyncFunctionPrototype
20
+ );
21
+ };
22
+ }
@@ -17,7 +17,6 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
17
17
  */
18
18
 
19
19
  module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins) {
20
-
21
20
  if (!(schema && schema.instanceOfSchema)) {
22
21
  throw new Error('You must pass a valid discriminator Schema');
23
22
  }
@@ -109,7 +108,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
109
108
 
110
109
  utils.merge(schema, baseSchema, {
111
110
  isDiscriminatorSchemaMerge: true,
112
- omit: { discriminators: true, base: true },
111
+ omit: { discriminators: true, base: true, _applyDiscriminators: true },
113
112
  omitNested: conflictingPaths.reduce((cur, path) => {
114
113
  cur['tree.' + path] = true;
115
114
  return cur;
@@ -141,7 +140,6 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
141
140
  obj[key][schema.options.typeKey] = existingPath ? existingPath.options[schema.options.typeKey] : String;
142
141
  schema.add(obj);
143
142
 
144
-
145
143
  schema.discriminatorMapping = { key: key, value: value, isRoot: false };
146
144
 
147
145
  if (baseSchema.options.collection) {
@@ -76,17 +76,20 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
76
76
  let schemaOptions = null;
77
77
  let modelNamesInOrder = null;
78
78
 
79
- if (schema != null && schema.instance === 'Embedded' && schema.options.ref) {
80
- const data = {
81
- localField: options.path + '._id',
82
- foreignField: '_id',
83
- justOne: true
84
- };
85
- const res = _getModelNames(doc, schema, modelNameFromQuery, model);
79
+ if (schema != null && schema.instance === 'Embedded') {
80
+ if (schema.options.ref) {
81
+ const data = {
82
+ localField: options.path + '._id',
83
+ foreignField: '_id',
84
+ justOne: true
85
+ };
86
+ const res = _getModelNames(doc, schema, modelNameFromQuery, model);
86
87
 
87
- const unpopulatedValue = mpath.get(options.path, doc);
88
- const id = mpath.get('_id', unpopulatedValue);
89
- addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
88
+ const unpopulatedValue = mpath.get(options.path, doc);
89
+ const id = mpath.get('_id', unpopulatedValue);
90
+ addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
91
+ }
92
+ // No-op if no `ref` set. See gh-11538
90
93
  continue;
91
94
  }
92
95
 
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const utils = require('../../utils');
4
+
5
+ function applyGlobalMaxTimeMS(options, model) {
6
+ applyGlobalOption(options, model, 'maxTimeMS');
7
+ }
8
+
9
+ function applyGlobalDiskUse(options, model) {
10
+ applyGlobalOption(options, model, 'allowDiskUse');
11
+ }
12
+
13
+ module.exports = {
14
+ applyGlobalMaxTimeMS,
15
+ applyGlobalDiskUse
16
+ };
17
+
18
+
19
+ function applyGlobalOption(options, model, optionName) {
20
+ if (utils.hasUserDefinedProperty(options, optionName)) {
21
+ return;
22
+ }
23
+
24
+ if (utils.hasUserDefinedProperty(model.db.options, optionName)) {
25
+ options[optionName] = model.db.options[optionName];
26
+ } else if (utils.hasUserDefinedProperty(model.base.options, optionName)) {
27
+ options[optionName] = model.base.options[optionName];
28
+ }
29
+ }
@@ -19,13 +19,16 @@ module.exports = function(schematype) {
19
19
  };
20
20
 
21
21
  function createImmutableSetter(path, immutable) {
22
- return function immutableSetter(v) {
22
+ return function immutableSetter(v, _priorVal, _doc, options) {
23
23
  if (this == null || this.$__ == null) {
24
24
  return v;
25
25
  }
26
26
  if (this.isNew) {
27
27
  return v;
28
28
  }
29
+ if (options && options.overwriteImmutable) {
30
+ return v;
31
+ }
29
32
 
30
33
  const _immutable = typeof immutable === 'function' ?
31
34
  immutable.call(this, this) :
@@ -48,8 +48,8 @@ module.exports = function setupTimestamps(schema, timestamps) {
48
48
  currentTime() :
49
49
  this.ownerDocument().constructor.base.now();
50
50
 
51
- if (!skipCreatedAt && this.isNew && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
52
- this.$set(createdAt, defaultTimestamp);
51
+ if (!skipCreatedAt && (this.isNew || this.$isSubdocument) && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
52
+ this.$set(createdAt, defaultTimestamp, undefined, { overwriteImmutable: true });
53
53
  }
54
54
 
55
55
  if (!skipUpdatedAt && updatedAt && (this.isNew || this.$isModified())) {
package/lib/index.js CHANGED
@@ -179,7 +179,9 @@ Mongoose.prototype.driver = driver;
179
179
  Mongoose.prototype.set = function(key, value) {
180
180
  const _mongoose = this instanceof Mongoose ? this : mongoose;
181
181
 
182
- if (VALID_OPTIONS.indexOf(key) === -1) throw new Error(`\`${key}\` is an invalid option.`);
182
+ if (VALID_OPTIONS.indexOf(key) === -1) {
183
+ throw new Error(`\`${key}\` is an invalid option.`);
184
+ }
183
185
 
184
186
  if (arguments.length === 1) {
185
187
  return _mongoose.options[key];
@@ -265,7 +267,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
265
267
  * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
266
268
  * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
267
269
  * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
268
- * @return {Connection} the created Connection object. Connections are thenable, so you can do `await mongoose.createConnection()`
270
+ * @return {Connection} the created Connection object. Connections are not thenable, so you can't do `await mongoose.createConnection()`. To await use mongoose.createConnection(uri).asPromise() instead.
269
271
  * @api public
270
272
  */
271
273
 
@@ -467,6 +469,7 @@ Mongoose.prototype.pluralize = function(fn) {
467
469
  */
468
470
 
469
471
  Mongoose.prototype.model = function(name, schema, collection, options) {
472
+
470
473
  const _mongoose = this instanceof Mongoose ? this : mongoose;
471
474
 
472
475
  if (typeof schema === 'string') {
@@ -519,7 +522,6 @@ Mongoose.prototype.model = function(name, schema, collection, options) {
519
522
  }
520
523
 
521
524
  const model = _mongoose._model(name, schema, collection, options);
522
-
523
525
  _mongoose.connection.models[name] = model;
524
526
  _mongoose.models[name] = model;
525
527
 
@@ -561,12 +563,17 @@ Mongoose.prototype._model = function(name, schema, collection, options) {
561
563
 
562
564
  const connection = options.connection || _mongoose.connection;
563
565
  model = _mongoose.Model.compile(model || name, schema, collection, connection, _mongoose);
564
-
565
566
  // Errors handled internally, so safe to ignore error
566
567
  model.init(function $modelInitNoop() {});
567
568
 
568
569
  connection.emit('model', model);
569
570
 
571
+ if (schema._applyDiscriminators != null) {
572
+ for (const disc of Object.keys(schema._applyDiscriminators)) {
573
+ model.discriminator(disc, schema._applyDiscriminators[disc]);
574
+ }
575
+ }
576
+
570
577
  return model;
571
578
  };
572
579
 
package/lib/internal.js CHANGED
@@ -11,7 +11,6 @@ module.exports = exports = InternalCache;
11
11
 
12
12
  function InternalCache() {
13
13
  this.activePaths = new ActiveRoster();
14
- this.strictMode = undefined;
15
14
  }
16
15
 
17
16
  InternalCache.prototype.fullPath = undefined;