mongoose 7.1.0 → 7.1.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.
package/lib/connection.js CHANGED
@@ -825,7 +825,6 @@ async function _createMongoClient(conn, uri, options) {
825
825
  options = processConnectionOptions(uri, options);
826
826
 
827
827
  if (options) {
828
- options = clone(options);
829
828
 
830
829
  const autoIndex = options.config && options.config.autoIndex != null ?
831
830
  options.config.autoIndex :
package/lib/document.js CHANGED
@@ -26,6 +26,7 @@ const flattenObjectWithDottedPaths = require('./helpers/path/flattenObjectWithDo
26
26
  const get = require('./helpers/get');
27
27
  const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDiscriminatorPath');
28
28
  const getKeysInSchemaOrder = require('./helpers/schema/getKeysInSchemaOrder');
29
+ const getSubdocumentStrictValue = require('./helpers/schema/getSubdocumentStrictValue');
29
30
  const handleSpreadDoc = require('./helpers/document/handleSpreadDoc');
30
31
  const immediate = require('./helpers/immediate');
31
32
  const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
@@ -52,6 +53,7 @@ const populateModelSymbol = require('./helpers/symbols').populateModelSymbol;
52
53
  const scopeSymbol = require('./helpers/symbols').scopeSymbol;
53
54
  const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
54
55
  const parentPaths = require('./helpers/path/parentPaths');
56
+ const getDeepestSubdocumentForPath = require('./helpers/document/getDeepestSubdocumentForPath');
55
57
 
56
58
  let DocumentArray;
57
59
  let MongooseArray;
@@ -1032,7 +1034,8 @@ Document.prototype.$set = function $set(path, val, type, options) {
1032
1034
  let key;
1033
1035
  let prefix;
1034
1036
 
1035
- const strict = options && 'strict' in options
1037
+ const userSpecifiedStrict = options && 'strict' in options;
1038
+ let strict = userSpecifiedStrict
1036
1039
  ? options.strict
1037
1040
  : this.$__.strictMode;
1038
1041
 
@@ -1140,8 +1143,20 @@ Document.prototype.$set = function $set(path, val, type, options) {
1140
1143
  }
1141
1144
 
1142
1145
  let pathType = this.$__schema.pathType(path);
1146
+ let parts = null;
1143
1147
  if (pathType === 'adhocOrUndefined') {
1144
- pathType = getEmbeddedDiscriminatorPath(this, path, { typeOnly: true });
1148
+ parts = path.indexOf('.') === -1 ? [path] : path.split('.');
1149
+ pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true });
1150
+ }
1151
+ if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) {
1152
+ // May be path underneath non-strict schema
1153
+ if (parts == null) {
1154
+ parts = path.indexOf('.') === -1 ? [path] : path.split('.');
1155
+ }
1156
+ const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts);
1157
+ if (subdocStrict !== undefined) {
1158
+ strict = subdocStrict;
1159
+ }
1145
1160
  }
1146
1161
 
1147
1162
  // Assume this is a Mongoose document that was copied into a POJO using
@@ -1204,7 +1219,9 @@ Document.prototype.$set = function $set(path, val, type, options) {
1204
1219
  }
1205
1220
 
1206
1221
  let schema;
1207
- const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
1222
+ if (parts == null) {
1223
+ parts = path.indexOf('.') === -1 ? [path] : path.split('.');
1224
+ }
1208
1225
 
1209
1226
  // Might need to change path for top-level alias
1210
1227
  if (typeof this.$__schema.aliases[parts[0]] === 'string') {
@@ -1233,6 +1250,11 @@ Document.prototype.$set = function $set(path, val, type, options) {
1233
1250
  // allow changes to sub paths of mixed types
1234
1251
  mixed = true;
1235
1252
  break;
1253
+ } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) {
1254
+ // Map of mixed and not the last element in the path resolves to mixed
1255
+ mixed = true;
1256
+ schema = schema.$__schemaType;
1257
+ break;
1236
1258
  }
1237
1259
  }
1238
1260
 
@@ -1377,15 +1399,19 @@ Document.prototype.$set = function $set(path, val, type, options) {
1377
1399
  didPopulate = true;
1378
1400
  }
1379
1401
 
1380
- if (this.$__schema.singleNestedPaths[path] == null && (!refMatches || !schema.$isSingleNested || !val.$__)) {
1402
+ if (!refMatches || !schema.$isSingleNested || !val.$__) {
1381
1403
  // If this path is underneath a single nested schema, we'll call the setter
1382
1404
  // later in `$__set()` because we don't take `_doc` when we iterate through
1383
1405
  // a single nested doc. That's to make sure we get the correct context.
1384
1406
  // Otherwise we would double-call the setter, see gh-7196.
1407
+ let setterContext = this;
1408
+ if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) {
1409
+ setterContext = getDeepestSubdocumentForPath(this, parts, this.schema);
1410
+ }
1385
1411
  if (options != null && options.overwriteImmutable) {
1386
- val = schema.applySetters(val, this, false, priorVal, { overwriteImmutable: true });
1412
+ val = schema.applySetters(val, setterContext, false, priorVal, { overwriteImmutable: true });
1387
1413
  } else {
1388
- val = schema.applySetters(val, this, false, priorVal);
1414
+ val = schema.applySetters(val, setterContext, false, priorVal);
1389
1415
  }
1390
1416
  }
1391
1417
 
@@ -1560,13 +1586,6 @@ Document.prototype.$__shouldModify = function(pathToMark, path, options, constru
1560
1586
  return true;
1561
1587
  }
1562
1588
 
1563
- // Re: the note about gh-7196, `val` is the raw value without casting or
1564
- // setters if the full path is under a single nested subdoc because we don't
1565
- // want to double run setters. So don't set it as modified. See gh-7264.
1566
- if (this.$__schema.singleNestedPaths[path] != null) {
1567
- return false;
1568
- }
1569
-
1570
1589
  if (val === void 0 && !this.$__isSelected(path)) {
1571
1590
  // when a path is not selected in a query, its initial
1572
1591
  // value will be undefined.
@@ -1674,17 +1693,26 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1674
1693
  obj[parts[i]] = val;
1675
1694
  }
1676
1695
  } else {
1677
- if (utils.isPOJO(obj[parts[i]])) {
1678
- obj = obj[parts[i]];
1679
- } else if (obj[parts[i]] && obj[parts[i]] instanceof Embedded) {
1680
- obj = obj[parts[i]];
1681
- } else if (obj[parts[i]] && !Array.isArray(obj[parts[i]]) && obj[parts[i]].$isSingleNested) {
1682
- obj = obj[parts[i]];
1683
- } else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
1684
- obj = obj[parts[i]];
1696
+ const isMap = obj instanceof Map;
1697
+ let value = isMap ? obj.get(parts[i]) : obj[parts[i]];
1698
+ if (utils.isPOJO(value)) {
1699
+ obj = value;
1700
+ } else if (value && value instanceof Embedded) {
1701
+ obj = value;
1702
+ } else if (value && !Array.isArray(value) && value.$isSingleNested) {
1703
+ obj = value._doc;
1704
+ } else if (value && Array.isArray(value)) {
1705
+ obj = value;
1706
+ } else if (value == null) {
1707
+ value = {};
1708
+ if (isMap) {
1709
+ obj.set(parts[i], value);
1710
+ } else {
1711
+ obj[parts[i]] = value;
1712
+ }
1713
+ obj = value;
1685
1714
  } else {
1686
- obj[parts[i]] = obj[parts[i]] || {};
1687
- obj = obj[parts[i]];
1715
+ obj = value;
1688
1716
  }
1689
1717
  }
1690
1718
  }
@@ -1764,7 +1792,11 @@ Document.prototype.$inc = function $inc(path, val) {
1764
1792
 
1765
1793
  if (shouldSet) {
1766
1794
  this.$__.primitiveAtomics = this.$__.primitiveAtomics || {};
1767
- this.$__.primitiveAtomics[path] = { $inc: valToInc };
1795
+ if (this.$__.primitiveAtomics[path] == null) {
1796
+ this.$__.primitiveAtomics[path] = { $inc: valToInc };
1797
+ } else {
1798
+ this.$__.primitiveAtomics[path].$inc += valToInc;
1799
+ }
1768
1800
  this.markModified(path);
1769
1801
  this.$__setValue(path, valToSet);
1770
1802
  }
@@ -2181,19 +2213,26 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
2181
2213
 
2182
2214
  Document.prototype.isModified = function(paths, modifiedPaths) {
2183
2215
  if (paths) {
2184
- const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify'));
2185
- if (directModifiedPaths.length === 0) {
2186
- return false;
2187
- }
2188
-
2189
2216
  if (!Array.isArray(paths)) {
2190
2217
  paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' ');
2191
2218
  }
2219
+
2220
+ const directModifiedPathsObj = this.$__.activePaths.states.modify;
2221
+ if (directModifiedPathsObj == null) {
2222
+ return false;
2223
+ }
2224
+ for (const path of paths) {
2225
+ if (Object.prototype.hasOwnProperty.call(directModifiedPathsObj, path)) {
2226
+ return true;
2227
+ }
2228
+ }
2229
+
2192
2230
  const modified = modifiedPaths || this[documentModifiedPaths]();
2193
2231
  const isModifiedChild = paths.some(function(path) {
2194
2232
  return !!~modified.indexOf(path);
2195
2233
  });
2196
2234
 
2235
+ const directModifiedPaths = Object.keys(directModifiedPathsObj);
2197
2236
  return isModifiedChild || paths.some(function(path) {
2198
2237
  return directModifiedPaths.some(function(mod) {
2199
2238
  return mod === path || path.startsWith(mod + '.');
@@ -208,8 +208,14 @@ function iter(i) {
208
208
 
209
209
  if (debug) {
210
210
  if (typeof debug === 'function') {
211
+ let argsToAdd = null;
212
+ if (typeof args[args.length - 1] == 'function') {
213
+ argsToAdd = args.slice(0, args.length - 1);
214
+ } else {
215
+ argsToAdd = args;
216
+ }
211
217
  debug.apply(_this,
212
- [_this.name, i].concat(args.slice(0, args.length - 1)));
218
+ [_this.name, i].concat(argsToAdd));
213
219
  } else if (debug instanceof stream.Writable) {
214
220
  this.$printToStream(_this.name, i, args, debug);
215
221
  } else {
@@ -18,7 +18,7 @@ class ObjectParameterError extends MongooseError {
18
18
  */
19
19
  constructor(value, paramName, fnName) {
20
20
  super('Parameter "' + paramName + '" to ' + fnName +
21
- '() must be an object, got ' + value.toString());
21
+ '() must be an object, got "' + value.toString() + '" (type ' + typeof value + ')');
22
22
  }
23
23
  }
24
24
 
@@ -55,7 +55,8 @@ module.exports = function mergeDiscriminatorSchema(to, from, path, seen) {
55
55
  // base schema has a given path as a single nested but discriminator schema
56
56
  // has the path as a document array, or vice versa (gh-9534)
57
57
  if ((from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
58
- (from[key].$isMongooseDocumentArray && to[key].$isSingleNested)) {
58
+ (from[key].$isMongooseDocumentArray && to[key].$isSingleNested) ||
59
+ (from[key].$isMongooseDocumentArrayElement && to[key].$isMongooseDocumentArrayElement)) {
59
60
  continue;
60
61
  } else if (from[key].instanceOfSchema) {
61
62
  if (to[key].instanceOfSchema) {
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Find the deepest subdocument along a given path to ensure setter functions run
5
+ * with the correct subdocument as `this`. If no subdocuments, returns the top-level
6
+ * document.
7
+ *
8
+ * @param {Document} doc
9
+ * @param {String[]} parts
10
+ * @param {Schema} schema
11
+ * @returns Document
12
+ */
13
+
14
+ module.exports = function getDeepestSubdocumentForPath(doc, parts, schema) {
15
+ let curPath = parts[0];
16
+ let curSchema = schema;
17
+ let subdoc = doc;
18
+ for (let i = 0; i < parts.length - 1; ++i) {
19
+ const curSchemaType = curSchema.path(curPath);
20
+ if (curSchemaType && curSchemaType.schema) {
21
+ let newSubdoc = subdoc.get(curPath);
22
+ curSchema = curSchemaType.schema;
23
+ curPath = parts[i + 1];
24
+ if (Array.isArray(newSubdoc) && !isNaN(curPath)) {
25
+ newSubdoc = newSubdoc[curPath];
26
+ curPath = '';
27
+ }
28
+ if (newSubdoc == null) {
29
+ break;
30
+ }
31
+ subdoc = newSubdoc;
32
+ } else {
33
+ curPath += curPath.length ? '.' + parts[i + 1] : parts[i + 1];
34
+ }
35
+ }
36
+
37
+ return subdoc;
38
+ };
@@ -6,8 +6,9 @@ const getSchemaDiscriminatorByValue = require('../discriminator/getSchemaDiscrim
6
6
  /**
7
7
  * Like `schema.path()`, except with a document, because impossible to
8
8
  * determine path type without knowing the embedded discriminator key.
9
+ *
9
10
  * @param {Document} doc
10
- * @param {String} path
11
+ * @param {String|String[]} path
11
12
  * @param {Object} [options]
12
13
  * @api private
13
14
  */
@@ -15,7 +16,9 @@ const getSchemaDiscriminatorByValue = require('../discriminator/getSchemaDiscrim
15
16
  module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
16
17
  options = options || {};
17
18
  const typeOnly = options.typeOnly;
18
- const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
19
+ const parts = Array.isArray(path) ?
20
+ path :
21
+ (path.indexOf('.') === -1 ? [path] : path.split('.'));
19
22
  let schemaType = null;
20
23
  let type = 'adhocOrUndefined';
21
24
 
@@ -389,8 +389,7 @@ function _virtualPopulate(model, docs, options, _virtualRes) {
389
389
  let foreignField = virtual.options.foreignField;
390
390
 
391
391
  if (!localField || !foreignField) {
392
- return new MongooseError('If you are populating a virtual, you must set the ' +
393
- 'localField and foreignField options');
392
+ return new MongooseError(`Cannot populate virtual \`${options.path}\` on model \`${model.modelName}\`, because options \`localField\` and / or \`foreignField\` are missing`);
394
393
  }
395
394
 
396
395
  if (typeof localField === 'function') {
@@ -2,16 +2,20 @@
2
2
 
3
3
  const utils = require('../utils');
4
4
 
5
- if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
6
- utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
7
- 'with Jest\'s default jsdom test environment. Please make sure you read ' +
8
- 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
9
- 'https://mongoosejs.com/docs/jest.html');
10
- }
5
+ if (typeof jest !== 'undefined' && !process.env.SUPPRESS_JEST_WARNINGS) {
6
+ if (typeof window !== 'undefined') {
7
+ utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
8
+ 'with Jest\'s default jsdom test environment. Please make sure you read ' +
9
+ 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
10
+ 'https://mongoosejs.com/docs/jest.html. Set the SUPPRESS_JEST_WARNINGS to true ' +
11
+ 'to hide this warning.');
12
+ }
11
13
 
12
- if (typeof jest !== 'undefined' && setTimeout.clock != null && typeof setTimeout.clock.Date === 'function') {
13
- utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
14
- 'with Jest\'s mock timers enabled. Please make sure you read ' +
15
- 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
16
- 'https://mongoosejs.com/docs/jest.html');
14
+ if (setTimeout.clock != null && typeof setTimeout.clock.Date === 'function') {
15
+ utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
16
+ 'with Jest\'s mock timers enabled. Please make sure you read ' +
17
+ 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
18
+ 'https://mongoosejs.com/docs/jest.html. Set the SUPPRESS_JEST_WARNINGS to true ' +
19
+ 'to hide this warning.');
20
+ }
17
21
  }
@@ -9,11 +9,12 @@ function processConnectionOptions(uri, options) {
9
9
  ? opts.readPreference
10
10
  : getUriReadPreference(uri);
11
11
 
12
+ const clonedOpts = clone(opts);
12
13
  const resolvedOpts = (readPreference && readPreference !== 'primary' && readPreference !== 'primaryPreferred')
13
- ? resolveOptsConflicts(readPreference, opts)
14
- : opts;
14
+ ? resolveOptsConflicts(readPreference, clonedOpts)
15
+ : clonedOpts;
15
16
 
16
- return clone(resolvedOpts);
17
+ return resolvedOpts;
17
18
  }
18
19
 
19
20
  function resolveOptsConflicts(pref, opts) {
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Find the `strict` mode setting for the deepest subdocument along a given path
5
+ * to ensure we have the correct default value for `strict`. When setting values
6
+ * underneath a subdocument, we should use the subdocument's `strict` setting by
7
+ * default, not the top-level document's.
8
+ *
9
+ * @param {Schema} schema
10
+ * @param {String[]} parts
11
+ * @returns {boolean | 'throw' | undefined}
12
+ */
13
+
14
+ module.exports = function getSubdocumentStrictValue(schema, parts) {
15
+ if (parts.length === 1) {
16
+ return undefined;
17
+ }
18
+ let cur = parts[0];
19
+ let strict = undefined;
20
+ for (let i = 0; i < parts.length - 1; ++i) {
21
+ const curSchemaType = schema.path(cur);
22
+ if (curSchemaType && curSchemaType.schema) {
23
+ strict = curSchemaType.schema.options.strict;
24
+ schema = curSchemaType.schema;
25
+ cur = curSchemaType.$isMongooseDocumentArray && !isNaN(parts[i + 1]) ? '' : parts[i + 1];
26
+ } else {
27
+ cur += cur.length ? ('.' + parts[i + 1]) : parts[i + 1];
28
+ }
29
+ }
30
+
31
+ return strict;
32
+ };
@@ -18,11 +18,9 @@ module.exports = function setupTimestamps(schema, timestamps) {
18
18
  const ts = s.schema.options.timestamps;
19
19
  return !!ts;
20
20
  }
21
-
22
21
  if (!timestamps && !childHasTimestamp) {
23
22
  return;
24
23
  }
25
-
26
24
  const createdAt = handleTimestampOption(timestamps, 'createdAt');
27
25
  const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
28
26
  const currentTime = timestamps != null && timestamps.hasOwnProperty('currentTime') ?
@@ -57,15 +55,15 @@ module.exports = function setupTimestamps(schema, timestamps) {
57
55
 
58
56
  schema.methods.initializeTimestamps = function() {
59
57
  const ts = currentTime != null ?
60
- currentTime() :
61
- this.constructor.base.now();
58
+ currentTime() : this.constructor.base.now();
59
+
60
+
62
61
  if (createdAt && !this.get(createdAt)) {
63
62
  this.$set(createdAt, ts);
64
63
  }
65
64
  if (updatedAt && !this.get(updatedAt)) {
66
65
  this.$set(updatedAt, ts);
67
66
  }
68
-
69
67
  if (this.$isSubdocument) {
70
68
  return this;
71
69
  }
@@ -98,8 +96,14 @@ module.exports = function setupTimestamps(schema, timestamps) {
98
96
  if (replaceOps.has(this.op) && this.getUpdate() == null) {
99
97
  this.setUpdate({});
100
98
  }
101
- applyTimestampsToUpdate(now, createdAt, updatedAt, this.getUpdate(),
102
- this.options, this.schema);
99
+ applyTimestampsToUpdate(
100
+ now,
101
+ createdAt,
102
+ updatedAt,
103
+ this.getUpdate(),
104
+ this._mongooseOptions,
105
+ this.schema
106
+ );
103
107
  applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
104
108
  next();
105
109
  }
package/lib/query.js CHANGED
@@ -1606,7 +1606,14 @@ Query.prototype.setOptions = function(options, overwrite) {
1606
1606
  this._mongooseOptions.sanitizeFilter = options.sanitizeFilter;
1607
1607
  delete options.sanitizeFilter;
1608
1608
  }
1609
-
1609
+ if ('overwrite' in options) {
1610
+ this._mongooseOptions.overwrite = options.overwrite;
1611
+ delete options.overwrite;
1612
+ }
1613
+ if ('timestamps' in options) {
1614
+ this._mongooseOptions.timestamps = options.timestamps;
1615
+ delete options.timestamps;
1616
+ }
1610
1617
  if ('defaults' in options) {
1611
1618
  this._mongooseOptions.defaults = options.defaults;
1612
1619
  // deleting options.defaults will cause 7287 to fail
@@ -1864,7 +1871,7 @@ Query.prototype._updateForExec = function() {
1864
1871
  while (i--) {
1865
1872
  const op = ops[i];
1866
1873
 
1867
- if (this.options.overwrite) {
1874
+ if (this._mongooseOptions.overwrite) {
1868
1875
  ret[op] = update[op];
1869
1876
  continue;
1870
1877
  }
@@ -1960,9 +1967,13 @@ Query.prototype._optionsForExec = function(model) {
1960
1967
  }
1961
1968
  }
1962
1969
 
1963
- const projection = this._fieldsForExec();
1964
- if (projection != null) {
1965
- options.projection = projection;
1970
+ this._applyPaths();
1971
+ if (this._fields != null) {
1972
+ this._fields = this._castFields(this._fields);
1973
+ const projection = this._fieldsForExec();
1974
+ if (projection != null) {
1975
+ options.projection = projection;
1976
+ }
1966
1977
  }
1967
1978
 
1968
1979
  return options;
@@ -2211,10 +2222,6 @@ Query.prototype._find = async function _find() {
2211
2222
  throw this.error();
2212
2223
  }
2213
2224
 
2214
- this._applyPaths();
2215
- this._fields = this._castFields(this._fields);
2216
-
2217
- const fields = this._fieldsForExec();
2218
2225
  const mongooseOptions = this._mongooseOptions;
2219
2226
  const _this = this;
2220
2227
  const userProvidedFields = _this._userProvidedFields || {};
@@ -2230,8 +2237,8 @@ Query.prototype._find = async function _find() {
2230
2237
  });
2231
2238
 
2232
2239
  const options = this._optionsForExec();
2233
- options.projection = this._fieldsForExec();
2234
2240
  const filter = this._conditions;
2241
+ const fields = options.projection;
2235
2242
 
2236
2243
  const cursor = await this._collection.collection.find(filter, options);
2237
2244
  if (options.explain) {
@@ -2474,8 +2481,6 @@ Query.prototype._findOne = async function _findOne() {
2474
2481
  throw err;
2475
2482
  }
2476
2483
 
2477
- this._applyPaths();
2478
- this._fields = this._castFields(this._fields);
2479
2484
  applyGlobalMaxTimeMS(this.options, this.model);
2480
2485
  applyGlobalDiskUse(this.options, this.model);
2481
2486
 
@@ -3146,8 +3151,8 @@ function prepareDiscriminatorCriteria(query) {
3146
3151
  * @api public
3147
3152
  */
3148
3153
 
3149
- Query.prototype.findOneAndUpdate = function(criteria, doc, options) {
3150
- if (typeof conditions === 'function' ||
3154
+ Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3155
+ if (typeof filter === 'function' ||
3151
3156
  typeof doc === 'function' ||
3152
3157
  typeof options === 'function' ||
3153
3158
  typeof arguments[3] === 'function') {
@@ -3163,13 +3168,17 @@ Query.prototype.findOneAndUpdate = function(criteria, doc, options) {
3163
3168
  options = undefined;
3164
3169
  break;
3165
3170
  case 1:
3166
- doc = criteria;
3167
- criteria = options = undefined;
3171
+ doc = filter;
3172
+ filter = options = undefined;
3168
3173
  break;
3169
3174
  }
3170
3175
 
3171
- if (mquery.canMerge(criteria)) {
3172
- this.merge(criteria);
3176
+ if (mquery.canMerge(filter)) {
3177
+ this.merge(filter);
3178
+ } else if (filter != null) {
3179
+ this.error(
3180
+ new ObjectParameterError(filter, 'filter', 'findOneAndUpdate')
3181
+ );
3173
3182
  }
3174
3183
 
3175
3184
  // apply doc
@@ -3371,7 +3380,7 @@ Query.prototype.findOneAndRemove = function(conditions, options) {
3371
3380
  *
3372
3381
  * @method findOneAndDelete
3373
3382
  * @memberOf Query
3374
- * @param {Object} [conditions]
3383
+ * @param {Object} [filter]
3375
3384
  * @param {Object} [options]
3376
3385
  * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html)
3377
3386
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
@@ -3381,8 +3390,8 @@ Query.prototype.findOneAndRemove = function(conditions, options) {
3381
3390
  * @api public
3382
3391
  */
3383
3392
 
3384
- Query.prototype.findOneAndDelete = function(conditions, options) {
3385
- if (typeof conditions === 'function' ||
3393
+ Query.prototype.findOneAndDelete = function(filter, options) {
3394
+ if (typeof filter === 'function' ||
3386
3395
  typeof options === 'function' ||
3387
3396
  typeof arguments[2] === 'function') {
3388
3397
  throw new MongooseError('Query.prototype.findOneAndDelete() no longer accepts a callback');
@@ -3392,8 +3401,8 @@ Query.prototype.findOneAndDelete = function(conditions, options) {
3392
3401
  this._validateOp();
3393
3402
  this._validate();
3394
3403
 
3395
- if (mquery.canMerge(conditions)) {
3396
- this.merge(conditions);
3404
+ if (mquery.canMerge(filter)) {
3405
+ this.merge(filter);
3397
3406
  }
3398
3407
 
3399
3408
  options && this.setOptions(options);
@@ -3505,6 +3514,10 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
3505
3514
 
3506
3515
  if (mquery.canMerge(filter)) {
3507
3516
  this.merge(filter);
3517
+ } else if (filter != null) {
3518
+ this.error(
3519
+ new ObjectParameterError(filter, 'filter', 'findOneAndReplace')
3520
+ );
3508
3521
  }
3509
3522
 
3510
3523
  if (replacement != null) {
@@ -3548,16 +3561,6 @@ Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
3548
3561
  const filter = this._conditions;
3549
3562
  const options = this._optionsForExec();
3550
3563
  convertNewToReturnDocument(options);
3551
- let fields = null;
3552
-
3553
- this._applyPaths();
3554
- if (this._fields != null) {
3555
- options.projection = this._castFields(clone(this._fields));
3556
- fields = options.projection;
3557
- if (fields instanceof Error) {
3558
- throw fields;
3559
- }
3560
- }
3561
3564
 
3562
3565
  const runValidators = _getOption(this, 'runValidators', false);
3563
3566
  if (runValidators === false) {
@@ -3786,7 +3789,7 @@ async function _updateThunk(op) {
3786
3789
  const options = this._optionsForExec(this.model);
3787
3790
 
3788
3791
  this._update = clone(this._update, options);
3789
- const isOverwriting = this.options.overwrite && !hasDollarKeys(this._update);
3792
+ const isOverwriting = this._mongooseOptions.overwrite && !hasDollarKeys(this._update);
3790
3793
  if (isOverwriting) {
3791
3794
  if (op === 'updateOne' || op === 'updateMany') {
3792
3795
  throw new MongooseError('The MongoDB server disallows ' +
@@ -3795,7 +3798,7 @@ async function _updateThunk(op) {
3795
3798
  }
3796
3799
  this._update = new this.model(this._update, null, true);
3797
3800
  } else {
3798
- this._update = this._castUpdate(this._update, options.overwrite);
3801
+ this._update = this._castUpdate(this._update, this._mongooseOptions.overwrite);
3799
3802
 
3800
3803
  if (this._update == null || Object.keys(this._update).length === 0) {
3801
3804
  return { acknowledged: false };
@@ -4883,6 +4886,9 @@ Query.prototype._castFields = function _castFields(fields) {
4883
4886
  */
4884
4887
 
4885
4888
  Query.prototype._applyPaths = function applyPaths() {
4889
+ if (!this.model) {
4890
+ return;
4891
+ }
4886
4892
  this._fields = this._fields || {};
4887
4893
  helpers.applyPaths(this._fields, this.model.schema);
4888
4894
 
@@ -4941,9 +4947,6 @@ Query.prototype._applyPaths = function applyPaths() {
4941
4947
  */
4942
4948
 
4943
4949
  Query.prototype.cursor = function cursor(opts) {
4944
- this._applyPaths();
4945
- this._fields = this._castFields(this._fields);
4946
-
4947
4950
  if (opts) {
4948
4951
  this.setOptions(opts);
4949
4952
  }