mongoose 9.0.2 → 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 (88) 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 +117 -77
  8. package/lib/drivers/node-mongodb-native/collection.js +5 -17
  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 +33 -44
  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 +2 -2
  85. package/types/index.d.ts +15 -5
  86. package/types/middlewares.d.ts +11 -0
  87. package/types/models.d.ts +15 -5
  88. package/types/schemaoptions.d.ts +4 -2
@@ -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) {
@@ -3491,7 +3483,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3491
3483
  const successfulDocuments = [];
3492
3484
  for (let i = 0; i < documents.length; i++) {
3493
3485
  const document = documents[i];
3494
- const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
3486
+ const documentError = bulkWriteError?.writeErrors.find(writeError => {
3495
3487
  const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
3496
3488
  return writeErrorDocumentId.toString() === document._doc._id.toString();
3497
3489
  });
@@ -3655,7 +3647,7 @@ Model.castObject = function castObject(obj, options) {
3655
3647
 
3656
3648
  for (const path of paths) {
3657
3649
  const schemaType = schema.path(path);
3658
- if (!schemaType || !schemaType.$isMongooseArray) {
3650
+ if (!schemaType?.$isMongooseArray) {
3659
3651
  continue;
3660
3652
  }
3661
3653
 
@@ -3844,7 +3836,7 @@ Model.hydrate = function(obj, projection, options) {
3844
3836
  }
3845
3837
 
3846
3838
  if (projection != null) {
3847
- if (obj != null && obj.$__ != null) {
3839
+ if (obj?.$__ != null) {
3848
3840
  obj = obj.toObject(internalToObjectOptions);
3849
3841
  }
3850
3842
  obj = applyProjection(obj, projection);
@@ -3983,7 +3975,7 @@ Model.updateOne = function updateOne(conditions, doc, options) {
3983
3975
  Model.replaceOne = function replaceOne(conditions, doc, options) {
3984
3976
  _checkContext(this, 'replaceOne');
3985
3977
 
3986
- const versionKey = this && this.schema && this.schema.options && this.schema.options.versionKey || null;
3978
+ const versionKey = this?.schema?.options?.versionKey || null;
3987
3979
  if (versionKey && !doc[versionKey]) {
3988
3980
  doc[versionKey] = 0;
3989
3981
  }
@@ -4009,10 +4001,7 @@ function _update(model, op, conditions, doc, options) {
4009
4001
  }
4010
4002
  options = typeof options === 'function' ? options : clone(options);
4011
4003
 
4012
- const versionKey = model &&
4013
- model.schema &&
4014
- model.schema.options &&
4015
- model.schema.options.versionKey || null;
4004
+ const versionKey = model?.schema?.options?.versionKey || null;
4016
4005
  decorateUpdateWithVersionKey(doc, options, versionKey);
4017
4006
 
4018
4007
  return mq[op](conditions, doc, options);
@@ -4151,7 +4140,7 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
4151
4140
 
4152
4141
  for (const path of paths) {
4153
4142
  const schemaType = schema.path(path);
4154
- if (!schemaType || !schemaType.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4143
+ if (!schemaType?.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4155
4144
  continue;
4156
4145
  }
4157
4146
 
@@ -4299,7 +4288,7 @@ const excludeIdRegGlobal = /\s?-_id\s?/g;
4299
4288
 
4300
4289
  async function _populatePath(model, docs, populateOptions) {
4301
4290
  if (populateOptions.strictPopulate == null) {
4302
- if (populateOptions._localModel != null && populateOptions._localModel.schema._userProvidedOptions.strictPopulate != null) {
4291
+ if (populateOptions._localModel?.schema._userProvidedOptions.strictPopulate != null) {
4303
4292
  populateOptions.strictPopulate = populateOptions._localModel.schema._userProvidedOptions.strictPopulate;
4304
4293
  } else if (populateOptions._localModel != null && model.base.options.strictPopulate != null) {
4305
4294
  populateOptions.strictPopulate = model.base.options.strictPopulate;
@@ -4383,7 +4372,7 @@ async function _populatePath(model, docs, populateOptions) {
4383
4372
  }
4384
4373
  }
4385
4374
 
4386
- if (mod.options.options && mod.options.options.limit != null) {
4375
+ if (mod.options.options?.limit != null) {
4387
4376
  assignmentOpts.originalLimit = mod.options.options.limit;
4388
4377
  } else if (mod.options.limit != null) {
4389
4378
  assignmentOpts.originalLimit = mod.options.limit;
@@ -4437,7 +4426,7 @@ async function _populatePath(model, docs, populateOptions) {
4437
4426
  }
4438
4427
  for (const arr of params) {
4439
4428
  const mod = arr[0];
4440
- if (mod.options && mod.options.options && mod.options.options._leanTransform) {
4429
+ if (mod.options?.options?._leanTransform) {
4441
4430
  for (const doc of vals) {
4442
4431
  mod.options.options._leanTransform(doc);
4443
4432
  }
@@ -4992,7 +4981,7 @@ Model._applyQueryMiddleware = function _applyQueryMiddleware() {
4992
4981
  return !!contexts.query;
4993
4982
  }
4994
4983
  if (hook.name === 'deleteOne' || hook.name === 'updateOne') {
4995
- return !!contexts.query || Object.keys(contexts).length === 0;
4984
+ return !!contexts.query || utils.hasOwnKeys(contexts) === false;
4996
4985
  }
4997
4986
  if (hook.query != null || hook.document != null) {
4998
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
  }