mongoose 9.0.1 → 9.1.0

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 (93) hide show
  1. package/lib/aggregate.js +1 -1
  2. package/lib/cast/string.js +1 -1
  3. package/lib/cast.js +7 -15
  4. package/lib/collection.js +2 -2
  5. package/lib/connection.js +20 -14
  6. package/lib/cursor/changeStream.js +5 -5
  7. package/lib/document.js +126 -79
  8. package/lib/drivers/node-mongodb-native/collection.js +33 -126
  9. package/lib/drivers/node-mongodb-native/connection.js +8 -23
  10. package/lib/error/cast.js +1 -1
  11. package/lib/helpers/aggregate/prepareDiscriminatorPipeline.js +4 -4
  12. package/lib/helpers/clone.js +8 -8
  13. package/lib/helpers/common.js +4 -4
  14. package/lib/helpers/cursor/eachAsync.js +1 -1
  15. package/lib/helpers/discriminator/getConstructor.js +1 -1
  16. package/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js +1 -1
  17. package/lib/helpers/discriminator/mergeDiscriminatorSchema.js +2 -2
  18. package/lib/helpers/document/applyDefaults.js +1 -1
  19. package/lib/helpers/document/applyTimestamps.js +2 -1
  20. package/lib/helpers/document/applyVirtuals.js +4 -3
  21. package/lib/helpers/document/cleanModifiedSubpaths.js +1 -1
  22. package/lib/helpers/document/compile.js +4 -4
  23. package/lib/helpers/document/getDeepestSubdocumentForPath.js +1 -1
  24. package/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js +1 -1
  25. package/lib/helpers/indexes/getRelatedIndexes.js +3 -3
  26. package/lib/helpers/model/castBulkWrite.js +5 -9
  27. package/lib/helpers/model/discriminator.js +1 -1
  28. package/lib/helpers/populate/assignRawDocsToIdStructure.js +1 -1
  29. package/lib/helpers/populate/assignVals.js +4 -4
  30. package/lib/helpers/populate/getModelsMapForPopulate.js +25 -23
  31. package/lib/helpers/populate/getSchemaTypes.js +6 -7
  32. package/lib/helpers/printJestWarning.js +1 -1
  33. package/lib/helpers/processConnectionOptions.js +1 -1
  34. package/lib/helpers/query/castUpdate.js +12 -12
  35. package/lib/helpers/query/getEmbeddedDiscriminatorPath.js +2 -2
  36. package/lib/helpers/query/handleImmutable.js +2 -2
  37. package/lib/helpers/query/sanitizeFilter.js +1 -1
  38. package/lib/helpers/schema/applyPlugins.js +1 -1
  39. package/lib/helpers/schema/applyReadConcern.js +1 -1
  40. package/lib/helpers/schema/applyWriteConcern.js +4 -2
  41. package/lib/helpers/schema/getIndexes.js +3 -3
  42. package/lib/helpers/schema/getSubdocumentStrictValue.js +1 -1
  43. package/lib/helpers/schema/handleIdOption.js +1 -1
  44. package/lib/helpers/schema/idGetter.js +1 -1
  45. package/lib/helpers/schematype/handleImmutable.js +1 -1
  46. package/lib/helpers/setDefaultsOnInsert.js +2 -5
  47. package/lib/helpers/timestamps/setDocumentTimestamps.js +2 -2
  48. package/lib/helpers/timestamps/setupTimestamps.js +2 -2
  49. package/lib/helpers/update/applyTimestampsToUpdate.js +10 -9
  50. package/lib/helpers/update/castArrayFilters.js +4 -4
  51. package/lib/helpers/update/decorateUpdateWithVersionKey.js +1 -1
  52. package/lib/helpers/updateValidators.js +4 -4
  53. package/lib/model.js +42 -48
  54. package/lib/mongoose.js +5 -5
  55. package/lib/options/virtualOptions.js +1 -1
  56. package/lib/plugins/saveSubdocs.js +2 -2
  57. package/lib/plugins/trackTransaction.js +3 -4
  58. package/lib/query.js +62 -59
  59. package/lib/queryHelpers.js +9 -12
  60. package/lib/schema/array.js +10 -12
  61. package/lib/schema/buffer.js +6 -6
  62. package/lib/schema/documentArray.js +15 -23
  63. package/lib/schema/documentArrayElement.js +3 -3
  64. package/lib/schema/map.js +1 -1
  65. package/lib/schema/mixed.js +2 -2
  66. package/lib/schema/number.js +22 -4
  67. package/lib/schema/objectId.js +1 -1
  68. package/lib/schema/operators/exists.js +1 -1
  69. package/lib/schema/operators/geospatial.js +1 -1
  70. package/lib/schema/string.js +2 -2
  71. package/lib/schema/subdocument.js +9 -12
  72. package/lib/schema/union.js +1 -1
  73. package/lib/schema.js +27 -28
  74. package/lib/schemaType.js +11 -11
  75. package/lib/types/array/index.js +2 -2
  76. package/lib/types/array/methods/index.js +38 -8
  77. package/lib/types/arraySubdocument.js +12 -2
  78. package/lib/types/buffer.js +1 -1
  79. package/lib/types/documentArray/index.js +2 -2
  80. package/lib/types/documentArray/methods/index.js +5 -5
  81. package/lib/types/map.js +8 -8
  82. package/lib/types/subdocument.js +15 -5
  83. package/lib/utils.js +23 -7
  84. package/package.json +3 -2
  85. package/types/index.d.ts +26 -9
  86. package/types/inferrawdoctype.d.ts +9 -3
  87. package/types/inferschematype.d.ts +20 -27
  88. package/types/middlewares.d.ts +11 -0
  89. package/types/models.d.ts +15 -5
  90. package/types/query.d.ts +1 -1
  91. package/types/schemaoptions.d.ts +4 -2
  92. package/types/utility.d.ts +1 -1
  93. package/types/virtuals.d.ts +3 -3
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  const get = require('../get');
8
+ const utils = require('../../utils');
8
9
 
9
10
  module.exports = applyTimestampsToUpdate;
10
11
 
@@ -22,11 +23,11 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
22
23
  return currentUpdate;
23
24
  }
24
25
 
25
- const skipCreatedAt = timestamps != null && timestamps.createdAt === false;
26
- const skipUpdatedAt = timestamps != null && timestamps.updatedAt === false;
26
+ const skipCreatedAt = timestamps?.createdAt === false;
27
+ const skipUpdatedAt = timestamps?.updatedAt === false;
27
28
 
28
29
  if (isReplace) {
29
- if (currentUpdate && currentUpdate.$set) {
30
+ if (currentUpdate?.$set) {
30
31
  currentUpdate = currentUpdate.$set;
31
32
  updates.$set = {};
32
33
  _updates = updates.$set;
@@ -51,7 +52,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
51
52
  }
52
53
  updates.$set = updates.$set || {};
53
54
  if (!skipUpdatedAt && updatedAt &&
54
- (!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt])) {
55
+ !currentUpdate.$currentDate?.[updatedAt]) {
55
56
  let timestampSet = false;
56
57
  if (updatedAt.indexOf('.') !== -1) {
57
58
  const pieces = updatedAt.split('.');
@@ -62,7 +63,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
62
63
  currentUpdate[start][remnant] = now;
63
64
  timestampSet = true;
64
65
  break;
65
- } else if (currentUpdate.$set && currentUpdate.$set[start]) {
66
+ } else if (currentUpdate.$set?.[start]) {
66
67
  currentUpdate.$set[start][remnant] = now;
67
68
  timestampSet = true;
68
69
  break;
@@ -81,7 +82,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
81
82
 
82
83
  if (!skipCreatedAt && createdAt) {
83
84
  const overwriteImmutable = get(options, 'overwriteImmutable', false);
84
- const hasUserCreatedAt = currentUpdate[createdAt] != null || currentUpdate?.$set[createdAt] != null;
85
+ const hasUserCreatedAt = currentUpdate[createdAt] != null || currentUpdate.$set?.[createdAt] != null;
85
86
 
86
87
  // If overwriteImmutable is true and user provided createdAt, keep their value
87
88
  if (overwriteImmutable && hasUserCreatedAt) {
@@ -95,7 +96,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
95
96
  if (currentUpdate[createdAt]) {
96
97
  delete currentUpdate[createdAt];
97
98
  }
98
- if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
99
+ if (currentUpdate.$set?.[createdAt]) {
99
100
  delete currentUpdate.$set[createdAt];
100
101
  }
101
102
  let timestampSet = false;
@@ -108,7 +109,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
108
109
  currentUpdate[start][remnant] = now;
109
110
  timestampSet = true;
110
111
  break;
111
- } else if (currentUpdate.$set && currentUpdate.$set[start]) {
112
+ } else if (currentUpdate.$set?.[start]) {
112
113
  currentUpdate.$set[start][remnant] = now;
113
114
  timestampSet = true;
114
115
  break;
@@ -123,7 +124,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
123
124
  }
124
125
  }
125
126
 
126
- if (Object.keys(updates.$set).length === 0) {
127
+ if (utils.hasOwnKeys(updates.$set) === false) {
127
128
  delete updates.$set;
128
129
  }
129
130
  return updates;
@@ -4,6 +4,7 @@ const castFilterPath = require('../query/castFilterPath');
4
4
  const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
5
5
  const getPath = require('../schema/getPath');
6
6
  const updatedPathsByArrayFilter = require('./updatedPathsByArrayFilter');
7
+ const utils = require('../../utils');
7
8
 
8
9
  module.exports = function castArrayFilters(query) {
9
10
  const arrayFilters = query.options.arrayFilters;
@@ -18,7 +19,7 @@ module.exports = function castArrayFilters(query) {
18
19
  if (query._mongooseOptions.strict != null) {
19
20
  strictQuery = query._mongooseOptions.strict;
20
21
  }
21
- if (query.model && query.model.base.options.strictQuery != null) {
22
+ if (query.model?.base.options.strictQuery != null) {
22
23
  strictQuery = query.model.base.options.strictQuery;
23
24
  }
24
25
  if (schema._userProvidedOptions.strictQuery != null) {
@@ -63,8 +64,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
63
64
 
64
65
  const baseSchematype = getPath(schema, baseFilterPath, discriminatorValueMap);
65
66
  let filterBaseSchema = baseSchematype != null ? baseSchematype.schema : null;
66
- if (filterBaseSchema != null &&
67
- filterBaseSchema.discriminators != null &&
67
+ if (filterBaseSchema?.discriminators != null &&
68
68
  filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]) {
69
69
  filterBaseSchema = filterBaseSchema.discriminators[filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]] || filterBaseSchema;
70
70
  discriminatorValueMap[baseFilterPath] = filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey];
@@ -74,7 +74,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
74
74
  if (updatedPathsByFilter[key] === null) {
75
75
  continue;
76
76
  }
77
- if (Object.keys(updatedPathsByFilter).length === 0) {
77
+ if (utils.hasOwnKeys(updatedPathsByFilter) === false) {
78
78
  continue;
79
79
  }
80
80
  const dot = key.indexOf('.');
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  module.exports = function decorateUpdateWithVersionKey(update, options, versionKey) {
9
- if (!versionKey || !(options && options.upsert || false)) {
9
+ if (!versionKey || !options?.upsert) {
10
10
  return;
11
11
  }
12
12
 
@@ -38,7 +38,7 @@ module.exports = async function updateValidators(query, schema, castedDoc, optio
38
38
  const _keys = Object.keys(castedDoc[keys[i]]);
39
39
  for (let ii = 0; ii < _keys.length; ++ii) {
40
40
  currentUpdate = castedDoc[keys[i]][_keys[ii]];
41
- if (currentUpdate && currentUpdate.$each) {
41
+ if (currentUpdate?.$each) {
42
42
  arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
43
43
  concat(currentUpdate.$each);
44
44
  } else {
@@ -109,7 +109,7 @@ module.exports = async function updateValidators(query, schema, castedDoc, optio
109
109
  continue;
110
110
  }
111
111
 
112
- if (schemaPath.$isMongooseDocumentArrayElement && v != null && v.$__ != null) {
112
+ if (schemaPath.$isMongooseDocumentArrayElement && v?.$__ != null) {
113
113
  alreadyValidated.push(updates[i]);
114
114
  validatorsToExecute.push(
115
115
  schemaPath.doValidate(v, context, { updateValidator: true }).catch(err => {
@@ -158,7 +158,7 @@ module.exports = async function updateValidators(query, schema, castedDoc, optio
158
158
  validatorsToExecute.push(
159
159
  schemaPath.doValidate(
160
160
  arrayAtomicUpdates[arrayUpdate],
161
- options && options.context === 'query' ? query : null
161
+ options?.context === 'query' ? query : null
162
162
  ).catch(err => {
163
163
  err.path = arrayUpdate;
164
164
  validationErrors.push(err);
@@ -170,7 +170,7 @@ module.exports = async function updateValidators(query, schema, castedDoc, optio
170
170
  validatorsToExecute.push(
171
171
  schemaPath.doValidate(
172
172
  atomicUpdate,
173
- options && options.context === 'query' ? query : null,
173
+ options?.context === 'query' ? query : null,
174
174
  { updateValidator: true }
175
175
  ).catch(err => {
176
176
  err.path = arrayUpdate;
package/lib/model.js CHANGED
@@ -434,7 +434,7 @@ Model.prototype.$__save = async function $__save(options) {
434
434
  continue;
435
435
  }
436
436
  minimize(updateOp[key]);
437
- if (Object.keys(updateOp[key]).length === 0) {
437
+ if (utils.hasOwnKeys(updateOp[key]) === false) {
438
438
  delete updateOp[key];
439
439
  update.$unset = update.$unset || {};
440
440
  update.$unset[key] = 1;
@@ -754,7 +754,7 @@ Model.prototype.deleteOne = function deleteOne(options) {
754
754
 
755
755
  const self = this;
756
756
  const where = this.$__where();
757
- const query = self.constructor.deleteOne(where, options);
757
+ const query = self.constructor.deleteOne();
758
758
 
759
759
  if (this.$session() != null) {
760
760
  if (!('session' in query.options)) {
@@ -763,12 +763,13 @@ Model.prototype.deleteOne = function deleteOne(options) {
763
763
  }
764
764
 
765
765
  query.pre(async function queryPreDeleteOne() {
766
- const res = await self.constructor._middleware.execPre('deleteOne', self, [self]);
766
+ const res = await self.constructor._middleware.execPre('deleteOne', self, [self, options]);
767
767
  // `self` is passed to pre hooks as argument for backwards compatibility, but that
768
768
  // isn't the actual arguments passed to the wrapped function.
769
- if (res?.length !== 1 || res[0] !== self) {
769
+ if (res[0] !== self || res[1] !== options) {
770
770
  throw new Error('Document deleteOne pre hooks cannot overwrite arguments');
771
771
  }
772
+ query.deleteOne(where, options);
772
773
  // Apply custom where conditions _after_ document deleteOne middleware for
773
774
  // consistency with save() - sharding plugin needs to set $where
774
775
  if (self.$where != null) {
@@ -790,6 +791,12 @@ Model.prototype.deleteOne = function deleteOne(options) {
790
791
  query.post(function queryPostDeleteOne() {
791
792
  return self.constructor._middleware.execPost('deleteOne', self, [self], {});
792
793
  });
794
+ query.transform(function setIsDeleted(result) {
795
+ if (result?.deletedCount > 0) {
796
+ self.$isDeleted(true);
797
+ }
798
+ return result;
799
+ });
793
800
 
794
801
  return query;
795
802
  };
@@ -1146,25 +1153,16 @@ Model.createCollection = async function createCollection(options) {
1146
1153
  throw err;
1147
1154
  });
1148
1155
 
1149
- const collectionOptions = this &&
1150
- this.schema &&
1151
- this.schema.options &&
1152
- this.schema.options.collectionOptions;
1156
+ const collectionOptions = this?.schema?.options?.collectionOptions;
1153
1157
  if (collectionOptions != null) {
1154
1158
  options = Object.assign({}, collectionOptions, options);
1155
1159
  }
1156
1160
 
1157
- const schemaCollation = this &&
1158
- this.schema &&
1159
- this.schema.options &&
1160
- this.schema.options.collation;
1161
+ const schemaCollation = this?.schema?.options?.collation;
1161
1162
  if (schemaCollation != null) {
1162
1163
  options = Object.assign({ collation: schemaCollation }, options);
1163
1164
  }
1164
- const capped = this &&
1165
- this.schema &&
1166
- this.schema.options &&
1167
- this.schema.options.capped;
1165
+ const capped = this?.schema?.options?.capped;
1168
1166
  if (capped != null) {
1169
1167
  if (typeof capped === 'number') {
1170
1168
  options = Object.assign({ capped: true, size: capped }, options);
@@ -1172,10 +1170,7 @@ Model.createCollection = async function createCollection(options) {
1172
1170
  options = Object.assign({ capped: true }, capped, options);
1173
1171
  }
1174
1172
  }
1175
- const timeseries = this &&
1176
- this.schema &&
1177
- this.schema.options &&
1178
- this.schema.options.timeseries;
1173
+ const timeseries = this?.schema?.options?.timeseries;
1179
1174
  if (timeseries != null) {
1180
1175
  options = Object.assign({ timeseries }, options);
1181
1176
  if (options.expireAfterSeconds != null) {
@@ -1190,10 +1185,7 @@ Model.createCollection = async function createCollection(options) {
1190
1185
  }
1191
1186
  }
1192
1187
 
1193
- const clusteredIndex = this &&
1194
- this.schema &&
1195
- this.schema.options &&
1196
- this.schema.options.clusteredIndex;
1188
+ const clusteredIndex = this?.schema?.options?.clusteredIndex;
1197
1189
  if (clusteredIndex != null) {
1198
1190
  options = Object.assign({ clusteredIndex: { ...clusteredIndex, unique: true } }, options);
1199
1191
  }
@@ -1495,7 +1487,7 @@ Model.cleanIndexes = async function cleanIndexes(options) {
1495
1487
  }
1496
1488
  const model = this;
1497
1489
 
1498
- if (Array.isArray(options && options.toDrop)) {
1490
+ if (Array.isArray(options?.toDrop)) {
1499
1491
  const res = await _dropIndexes(options.toDrop, model, options);
1500
1492
  return res;
1501
1493
  }
@@ -1510,7 +1502,7 @@ async function _dropIndexes(toDrop, model, options) {
1510
1502
  }
1511
1503
 
1512
1504
  const collection = model.$__collection;
1513
- if (options && options.hideIndexes) {
1505
+ if (options?.hideIndexes) {
1514
1506
  await Promise.all(toDrop.map(indexName => {
1515
1507
  return model.db.db.command({
1516
1508
  collMod: collection.collectionName,
@@ -1854,7 +1846,7 @@ Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
1854
1846
  let currentSchema = this.schema;
1855
1847
  for (const i in fieldKeys) {
1856
1848
  const name = fieldKeys[i];
1857
- if (currentSchema && currentSchema.aliases[name]) {
1849
+ if (currentSchema?.aliases[name]) {
1858
1850
  alias = currentSchema.aliases[name];
1859
1851
  if (errorOnDuplicates && alias in fields) {
1860
1852
  throw new MongooseError(`Provided object has both field "${name}" and its alias "${alias}"`);
@@ -1868,7 +1860,7 @@ Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
1868
1860
  }
1869
1861
 
1870
1862
  // Check if aliased path is a schema
1871
- if (currentSchema && currentSchema.paths[alias]) {
1863
+ if (currentSchema?.paths[alias]) {
1872
1864
  currentSchema = currentSchema.paths[alias].schema;
1873
1865
  }
1874
1866
  else
@@ -3054,13 +3046,13 @@ Model.insertMany = async function insertMany(arr, options) {
3054
3046
  // `writeErrors` is a property reported by the MongoDB driver,
3055
3047
  // just not if there's only 1 error.
3056
3048
  if (error.writeErrors == null &&
3057
- (error.result && error.result.result && error.result.result.writeErrors) != null) {
3049
+ error.result?.result?.writeErrors != null) {
3058
3050
  error.writeErrors = error.result.result.writeErrors;
3059
3051
  }
3060
3052
 
3061
3053
  // `insertedDocs` is a Mongoose-specific property
3062
- const hasWriteErrors = error && error.writeErrors;
3063
- const erroredIndexes = new Set((error && error.writeErrors || []).map(err => err.index));
3054
+ const hasWriteErrors = error?.writeErrors;
3055
+ const erroredIndexes = new Set((error?.writeErrors || []).map(err => err.index));
3064
3056
 
3065
3057
  if (error.writeErrors != null) {
3066
3058
  for (let i = 0; i < error.writeErrors.length; ++i) {
@@ -3248,12 +3240,14 @@ function _setIsNew(doc, val) {
3248
3240
  * @param {Object} [ops.updateOne.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/)
3249
3241
  * @param {Boolean} [ops.updateOne.upsert=false] If true, insert a doc if none match
3250
3242
  * @param {Boolean} [ops.updateOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3243
+ * @param {Boolean} [ops.updateOne.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
3251
3244
  * @param {Object} [ops.updateOne.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
3252
3245
  * @param {Array} [ops.updateOne.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update`
3253
3246
  * @param {Object} [ops.updateMany.filter] Update all the documents that match this filter
3254
3247
  * @param {Object} [ops.updateMany.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/)
3255
3248
  * @param {Boolean} [ops.updateMany.upsert=false] If true, insert a doc if no documents match `filter`
3256
3249
  * @param {Boolean} [ops.updateMany.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3250
+ * @param {Boolean} [ops.updateMany.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
3257
3251
  * @param {Object} [ops.updateMany.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
3258
3252
  * @param {Array} [ops.updateMany.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update`
3259
3253
  * @param {Object} [ops.deleteOne.filter] Delete the first document that matches this filter
@@ -3286,12 +3280,15 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3286
3280
  }
3287
3281
  options = options || {};
3288
3282
 
3289
- [ops, options] = await this.hooks.execPre('bulkWrite', this, [ops, options]).catch(err => {
3283
+ try {
3284
+ [ops, options] = await this.hooks.execPre('bulkWrite', this, [ops, options]);
3285
+ } catch (err) {
3290
3286
  if (err instanceof Kareem.skipWrappedFunction) {
3291
- return [err];
3287
+ ops = err;
3288
+ } else {
3289
+ await this.hooks.execPost('bulkWrite', this, [null], { error: err });
3292
3290
  }
3293
- throw err;
3294
- });
3291
+ }
3295
3292
 
3296
3293
  if (ops instanceof Kareem.skipWrappedFunction) {
3297
3294
  return ops.args[0];
@@ -3486,7 +3483,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3486
3483
  const successfulDocuments = [];
3487
3484
  for (let i = 0; i < documents.length; i++) {
3488
3485
  const document = documents[i];
3489
- const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
3486
+ const documentError = bulkWriteError?.writeErrors.find(writeError => {
3490
3487
  const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
3491
3488
  return writeErrorDocumentId.toString() === document._doc._id.toString();
3492
3489
  });
@@ -3650,7 +3647,7 @@ Model.castObject = function castObject(obj, options) {
3650
3647
 
3651
3648
  for (const path of paths) {
3652
3649
  const schemaType = schema.path(path);
3653
- if (!schemaType || !schemaType.$isMongooseArray) {
3650
+ if (!schemaType?.$isMongooseArray) {
3654
3651
  continue;
3655
3652
  }
3656
3653
 
@@ -3839,7 +3836,7 @@ Model.hydrate = function(obj, projection, options) {
3839
3836
  }
3840
3837
 
3841
3838
  if (projection != null) {
3842
- if (obj != null && obj.$__ != null) {
3839
+ if (obj?.$__ != null) {
3843
3840
  obj = obj.toObject(internalToObjectOptions);
3844
3841
  }
3845
3842
  obj = applyProjection(obj, projection);
@@ -3978,7 +3975,7 @@ Model.updateOne = function updateOne(conditions, doc, options) {
3978
3975
  Model.replaceOne = function replaceOne(conditions, doc, options) {
3979
3976
  _checkContext(this, 'replaceOne');
3980
3977
 
3981
- const versionKey = this && this.schema && this.schema.options && this.schema.options.versionKey || null;
3978
+ const versionKey = this?.schema?.options?.versionKey || null;
3982
3979
  if (versionKey && !doc[versionKey]) {
3983
3980
  doc[versionKey] = 0;
3984
3981
  }
@@ -4004,10 +4001,7 @@ function _update(model, op, conditions, doc, options) {
4004
4001
  }
4005
4002
  options = typeof options === 'function' ? options : clone(options);
4006
4003
 
4007
- const versionKey = model &&
4008
- model.schema &&
4009
- model.schema.options &&
4010
- model.schema.options.versionKey || null;
4004
+ const versionKey = model?.schema?.options?.versionKey || null;
4011
4005
  decorateUpdateWithVersionKey(doc, options, versionKey);
4012
4006
 
4013
4007
  return mq[op](conditions, doc, options);
@@ -4146,7 +4140,7 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
4146
4140
 
4147
4141
  for (const path of paths) {
4148
4142
  const schemaType = schema.path(path);
4149
- if (!schemaType || !schemaType.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4143
+ if (!schemaType?.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4150
4144
  continue;
4151
4145
  }
4152
4146
 
@@ -4294,7 +4288,7 @@ const excludeIdRegGlobal = /\s?-_id\s?/g;
4294
4288
 
4295
4289
  async function _populatePath(model, docs, populateOptions) {
4296
4290
  if (populateOptions.strictPopulate == null) {
4297
- if (populateOptions._localModel != null && populateOptions._localModel.schema._userProvidedOptions.strictPopulate != null) {
4291
+ if (populateOptions._localModel?.schema._userProvidedOptions.strictPopulate != null) {
4298
4292
  populateOptions.strictPopulate = populateOptions._localModel.schema._userProvidedOptions.strictPopulate;
4299
4293
  } else if (populateOptions._localModel != null && model.base.options.strictPopulate != null) {
4300
4294
  populateOptions.strictPopulate = model.base.options.strictPopulate;
@@ -4378,7 +4372,7 @@ async function _populatePath(model, docs, populateOptions) {
4378
4372
  }
4379
4373
  }
4380
4374
 
4381
- if (mod.options.options && mod.options.options.limit != null) {
4375
+ if (mod.options.options?.limit != null) {
4382
4376
  assignmentOpts.originalLimit = mod.options.options.limit;
4383
4377
  } else if (mod.options.limit != null) {
4384
4378
  assignmentOpts.originalLimit = mod.options.limit;
@@ -4432,7 +4426,7 @@ async function _populatePath(model, docs, populateOptions) {
4432
4426
  }
4433
4427
  for (const arr of params) {
4434
4428
  const mod = arr[0];
4435
- if (mod.options && mod.options.options && mod.options.options._leanTransform) {
4429
+ if (mod.options?.options?._leanTransform) {
4436
4430
  for (const doc of vals) {
4437
4431
  mod.options.options._leanTransform(doc);
4438
4432
  }
@@ -4987,7 +4981,7 @@ Model._applyQueryMiddleware = function _applyQueryMiddleware() {
4987
4981
  return !!contexts.query;
4988
4982
  }
4989
4983
  if (hook.name === 'deleteOne' || hook.name === 'updateOne') {
4990
- return !!contexts.query || Object.keys(contexts).length === 0;
4984
+ return !!contexts.query || utils.hasOwnKeys(contexts) === false;
4991
4985
  }
4992
4986
  if (hook.query != null || hook.document != null) {
4993
4987
  return !!hook.query;
package/lib/mongoose.js CHANGED
@@ -82,7 +82,7 @@ function Mongoose(options) {
82
82
 
83
83
  // If a user creates their own Mongoose instance, give them a separate copy
84
84
  // of the `Schema` constructor so they get separate custom types. (gh-6933)
85
- if (!options || !options[defaultMongooseSymbol]) {
85
+ if (!options?.[defaultMongooseSymbol]) {
86
86
  const _this = this;
87
87
  this.Schema = function() {
88
88
  this.base = _this;
@@ -308,8 +308,8 @@ Mongoose.prototype.set = function getsetOptions(key, value) {
308
308
  } else if (optionKey === 'createInitialConnection') {
309
309
  if (optionValue && !_mongoose.connection) {
310
310
  _createDefaultConnection(_mongoose);
311
- } else if (optionValue === false && _mongoose.connection && _mongoose.connection[defaultConnectionSymbol]) {
312
- if (_mongoose.connection.readyState === STATES.disconnected && Object.keys(_mongoose.connection.models).length === 0) {
311
+ } else if (optionValue === false && _mongoose.connection?.[defaultConnectionSymbol]) {
312
+ if (_mongoose.connection.readyState === STATES.disconnected && utils.hasOwnKeys(_mongoose.connection.models) === false) {
313
313
  _mongoose.connections.shift();
314
314
  }
315
315
  }
@@ -746,8 +746,8 @@ Mongoose.prototype._applyPlugins = function _applyPlugins(schema, options) {
746
746
  const _mongoose = this instanceof Mongoose ? this : mongoose;
747
747
 
748
748
  options = options || {};
749
- options.applyPluginsToDiscriminators = _mongoose.options && _mongoose.options.applyPluginsToDiscriminators || false;
750
- options.applyPluginsToChildSchemas = typeof (_mongoose.options && _mongoose.options.applyPluginsToChildSchemas) === 'boolean' ?
749
+ options.applyPluginsToDiscriminators = _mongoose.options?.applyPluginsToDiscriminators || false;
750
+ options.applyPluginsToChildSchemas = typeof _mongoose.options?.applyPluginsToChildSchemas === 'boolean' ?
751
751
  _mongoose.options.applyPluginsToChildSchemas :
752
752
  true;
753
753
  applyPlugins(schema, _mongoose.plugins, options, '$globalPluginsApplied');
@@ -6,7 +6,7 @@ class VirtualOptions {
6
6
  constructor(obj) {
7
7
  Object.assign(this, obj);
8
8
 
9
- if (obj != null && obj.options != null) {
9
+ if (obj?.options != null) {
10
10
  this.options = Object.assign({}, obj.options);
11
11
  }
12
12
  }
@@ -27,7 +27,7 @@ module.exports = function saveSubdocs(schema) {
27
27
 
28
28
  schema.s.hooks.pre('save', async function saveSubdocsPreDeleteOne() {
29
29
  const removedSubdocs = this.$__.removedSubdocs;
30
- if (!removedSubdocs || !removedSubdocs.length) {
30
+ if (!removedSubdocs?.length) {
31
31
  return;
32
32
  }
33
33
 
@@ -41,7 +41,7 @@ module.exports = function saveSubdocs(schema) {
41
41
 
42
42
  schema.s.hooks.post('save', async function saveSubdocsPostDeleteOne() {
43
43
  const removedSubdocs = this.$__.removedSubdocs;
44
- if (!removedSubdocs || !removedSubdocs.length) {
44
+ if (!removedSubdocs?.length) {
45
45
  return;
46
46
  }
47
47
 
@@ -39,12 +39,11 @@ function _getAtomics(doc, previous) {
39
39
 
40
40
  for (const path of pathsToCheck) {
41
41
  const val = doc.$__getValue(path);
42
- if (val != null &&
43
- Array.isArray(val) &&
42
+ if (Array.isArray(val) &&
44
43
  utils.isMongooseDocumentArray(val) &&
45
44
  val.length &&
46
45
  val[arrayAtomicsSymbol] != null &&
47
- Object.keys(val[arrayAtomicsSymbol]).length !== 0) {
46
+ utils.hasOwnKeys(val[arrayAtomicsSymbol])) {
48
47
  const existing = previous.get(path) || {};
49
48
  pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol]));
50
49
  }
@@ -55,7 +54,7 @@ function _getAtomics(doc, previous) {
55
54
  const path = dirt.path;
56
55
 
57
56
  const val = dirt.value;
58
- if (val != null && val[arrayAtomicsSymbol] != null && Object.keys(val[arrayAtomicsSymbol]).length !== 0) {
57
+ if (val?.[arrayAtomicsSymbol] != null && utils.hasOwnKeys(val[arrayAtomicsSymbol])) {
59
58
  const existing = previous.get(path) || {};
60
59
  pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol]));
61
60
  }