mongoose 9.0.0-rc1 → 9.0.1
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/README.md +1 -1
- package/lib/aggregate.js +1 -1
- package/lib/cast/double.js +1 -1
- package/lib/cast.js +1 -1
- package/lib/connection.js +10 -10
- package/lib/document.js +54 -17
- package/lib/drivers/node-mongodb-native/collection.js +16 -1
- package/lib/drivers/node-mongodb-native/connection.js +1 -1
- package/lib/helpers/common.js +1 -1
- package/lib/helpers/indexes/applySchemaCollation.js +1 -1
- package/lib/helpers/indexes/isDefaultIdIndex.js +1 -1
- package/lib/helpers/model/applyMethods.js +1 -1
- package/lib/helpers/model/castBulkWrite.js +13 -6
- package/lib/helpers/populate/getModelsMapForPopulate.js +3 -3
- package/lib/helpers/populate/modelNamesFromRefPath.js +1 -1
- package/lib/helpers/populate/removeDeselectedForeignField.js +1 -1
- package/lib/helpers/projection/applyProjection.js +2 -2
- package/lib/helpers/query/getEmbeddedDiscriminatorPath.js +1 -1
- package/lib/helpers/setDefaultsOnInsert.js +2 -2
- package/lib/helpers/timestamps/setupTimestamps.js +1 -1
- package/lib/helpers/update/applyTimestampsToUpdate.js +38 -25
- package/lib/helpers/update/decorateUpdateWithVersionKey.js +1 -1
- package/lib/model.js +8 -7
- package/lib/mongoose.js +3 -4
- package/lib/query.js +3 -3
- package/lib/schema/array.js +1 -1
- package/lib/schema/number.js +14 -2
- package/lib/schema/operators/text.js +1 -1
- package/lib/schema.js +21 -21
- package/lib/schemaType.js +8 -8
- package/lib/types/array/index.js +5 -5
- package/lib/types/documentArray/index.js +6 -6
- package/lib/types/objectid.js +1 -1
- package/lib/utils.js +12 -24
- package/lib/virtualType.js +1 -1
- package/package.json +8 -7
- package/types/index.d.ts +1 -0
- package/types/models.d.ts +314 -86
- package/types/query.d.ts +91 -1
- package/types/utility.d.ts +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed
|
|
|
13
13
|
|
|
14
14
|
The official documentation website is [mongoosejs.com](http://mongoosejs.com/).
|
|
15
15
|
|
|
16
|
-
Mongoose
|
|
16
|
+
Mongoose 9.0.0 was released on November 21, 2025. You can find more details on [backwards breaking changes in 9.0.0 on our docs site](https://mongoosejs.com/docs/migrating_to_9.html).
|
|
17
17
|
|
|
18
18
|
## Support
|
|
19
19
|
|
package/lib/aggregate.js
CHANGED
|
@@ -103,7 +103,7 @@ Aggregate.prototype._optionsForExec = function() {
|
|
|
103
103
|
const options = this.options || {};
|
|
104
104
|
|
|
105
105
|
const asyncLocalStorage = this.model()?.db?.base.transactionAsyncLocalStorage?.getStore();
|
|
106
|
-
if (!
|
|
106
|
+
if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) {
|
|
107
107
|
options.session = asyncLocalStorage.session;
|
|
108
108
|
}
|
|
109
109
|
|
package/lib/cast/double.js
CHANGED
|
@@ -34,7 +34,7 @@ module.exports = function castDouble(val) {
|
|
|
34
34
|
// ex: { a: 'im an object, valueOf: () => 'helloworld' } // throw an error
|
|
35
35
|
if (typeof tempVal === 'string') {
|
|
36
36
|
try {
|
|
37
|
-
coercedVal = BSON.Double.fromString(
|
|
37
|
+
coercedVal = BSON.Double.fromString(tempVal);
|
|
38
38
|
return coercedVal;
|
|
39
39
|
} catch {
|
|
40
40
|
assert.ok(false);
|
package/lib/cast.js
CHANGED
|
@@ -107,7 +107,7 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
107
107
|
val = cast(schema, val, options, context);
|
|
108
108
|
} else if (path === '$text') {
|
|
109
109
|
val = castTextSearch(val, path);
|
|
110
|
-
} else if (path === '$comment' && !schema.paths
|
|
110
|
+
} else if (path === '$comment' && !Object.hasOwn(schema.paths, '$comment')) {
|
|
111
111
|
val = castString(val, path);
|
|
112
112
|
obj[path] = val;
|
|
113
113
|
} else {
|
package/lib/connection.js
CHANGED
|
@@ -164,7 +164,7 @@ Object.defineProperty(Connection.prototype, 'readyState', {
|
|
|
164
164
|
*/
|
|
165
165
|
|
|
166
166
|
Connection.prototype.get = function getOption(key) {
|
|
167
|
-
if (this.config
|
|
167
|
+
if (Object.hasOwn(this.config, key)) {
|
|
168
168
|
return this.config[key];
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -192,7 +192,7 @@ Connection.prototype.get = function getOption(key) {
|
|
|
192
192
|
*/
|
|
193
193
|
|
|
194
194
|
Connection.prototype.set = function setOption(key, val) {
|
|
195
|
-
if (this.config
|
|
195
|
+
if (Object.hasOwn(this.config, key)) {
|
|
196
196
|
this.config[key] = val;
|
|
197
197
|
return val;
|
|
198
198
|
}
|
|
@@ -459,7 +459,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
459
459
|
|
|
460
460
|
const ordered = options.ordered == null ? true : options.ordered;
|
|
461
461
|
const asyncLocalStorage = this.base.transactionAsyncLocalStorage?.getStore();
|
|
462
|
-
if ((!options || !
|
|
462
|
+
if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) {
|
|
463
463
|
options = { ...options, session: asyncLocalStorage.session };
|
|
464
464
|
}
|
|
465
465
|
|
|
@@ -477,7 +477,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
477
477
|
if (op.name == null) {
|
|
478
478
|
throw new MongooseError('Must specify operation name in Connection.prototype.bulkWrite()');
|
|
479
479
|
}
|
|
480
|
-
if (!castBulkWrite.cast
|
|
480
|
+
if (!Object.hasOwn(castBulkWrite.cast, op.name)) {
|
|
481
481
|
throw new MongooseError(`Unrecognized bulkWrite() operation name ${op.name}`);
|
|
482
482
|
}
|
|
483
483
|
|
|
@@ -513,7 +513,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
513
513
|
results[i] = error;
|
|
514
514
|
continue;
|
|
515
515
|
}
|
|
516
|
-
if (!castBulkWrite.cast
|
|
516
|
+
if (!Object.hasOwn(castBulkWrite.cast, op.name)) {
|
|
517
517
|
const error = new MongooseError(`Unrecognized bulkWrite() operation name ${op.name}`);
|
|
518
518
|
validationErrors.push({ index: i, error: error });
|
|
519
519
|
results[i] = error;
|
|
@@ -772,10 +772,10 @@ async function _wrapUserTransaction(fn, session, mongoose) {
|
|
|
772
772
|
function _resetSessionDocuments(session) {
|
|
773
773
|
for (const doc of session[sessionNewDocuments].keys()) {
|
|
774
774
|
const state = session[sessionNewDocuments].get(doc);
|
|
775
|
-
if (
|
|
775
|
+
if (Object.hasOwn(state, 'isNew')) {
|
|
776
776
|
doc.$isNew = state.isNew;
|
|
777
777
|
}
|
|
778
|
-
if (
|
|
778
|
+
if (Object.hasOwn(state, 'versionKey')) {
|
|
779
779
|
doc.set(doc.schema.options.versionKey, state.versionKey);
|
|
780
780
|
}
|
|
781
781
|
|
|
@@ -1013,7 +1013,7 @@ Connection.prototype.onOpen = function() {
|
|
|
1013
1013
|
// avoid having the collection subscribe to our event emitter
|
|
1014
1014
|
// to prevent 0.3 warning
|
|
1015
1015
|
for (const i in this.collections) {
|
|
1016
|
-
if (
|
|
1016
|
+
if (Object.hasOwn(this.collections, i)) {
|
|
1017
1017
|
this.collections[i].onOpen();
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
@@ -1321,7 +1321,7 @@ Connection.prototype.onClose = function onClose(force) {
|
|
|
1321
1321
|
// avoid having the collection subscribe to our event emitter
|
|
1322
1322
|
// to prevent 0.3 warning
|
|
1323
1323
|
for (const i in this.collections) {
|
|
1324
|
-
if (
|
|
1324
|
+
if (Object.hasOwn(this.collections, i)) {
|
|
1325
1325
|
this.collections[i].onClose(force);
|
|
1326
1326
|
}
|
|
1327
1327
|
}
|
|
@@ -1782,7 +1782,7 @@ Connection.prototype.syncIndexes = async function syncIndexes(options = {}) {
|
|
|
1782
1782
|
};
|
|
1783
1783
|
|
|
1784
1784
|
/**
|
|
1785
|
-
* Switches to a different database using the same [connection pool](https://mongoosejs.com/docs/
|
|
1785
|
+
* Switches to a different database using the same [connection pool](https://mongoosejs.com/docs/connections.html#connection_pools).
|
|
1786
1786
|
*
|
|
1787
1787
|
* Returns a new connection object, with the new db.
|
|
1788
1788
|
*
|
package/lib/document.js
CHANGED
|
@@ -784,7 +784,7 @@ function init(self, obj, doc, opts, prefix) {
|
|
|
784
784
|
}
|
|
785
785
|
} else {
|
|
786
786
|
// Retain order when overwriting defaults
|
|
787
|
-
if (
|
|
787
|
+
if (Object.hasOwn(doc, i) && value !== void 0 && !opts.hydratedPopulatedDocs) {
|
|
788
788
|
delete doc[i];
|
|
789
789
|
}
|
|
790
790
|
if (value === null) {
|
|
@@ -1167,7 +1167,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1167
1167
|
const orderedKeys = Object.keys(this.$__schema.tree);
|
|
1168
1168
|
for (let i = 0, len = orderedKeys.length; i < len; ++i) {
|
|
1169
1169
|
(key = orderedKeys[i]) &&
|
|
1170
|
-
(this._doc
|
|
1170
|
+
(Object.hasOwn(this._doc, key)) &&
|
|
1171
1171
|
(orderedDoc[key] = undefined);
|
|
1172
1172
|
}
|
|
1173
1173
|
this._doc = Object.assign(orderedDoc, this._doc);
|
|
@@ -1217,8 +1217,8 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1217
1217
|
return this;
|
|
1218
1218
|
}
|
|
1219
1219
|
const wasModified = this.$isModified(path);
|
|
1220
|
-
const hasInitialVal = this.$__.savedState != null && this.$__.savedState
|
|
1221
|
-
if (this.$__.savedState != null && !this.$isNew && !this.$__.savedState
|
|
1220
|
+
const hasInitialVal = this.$__.savedState != null && Object.hasOwn(this.$__.savedState, path);
|
|
1221
|
+
if (this.$__.savedState != null && !this.$isNew && !Object.hasOwn(this.$__.savedState, path)) {
|
|
1222
1222
|
const initialVal = this.$__getValue(path);
|
|
1223
1223
|
this.$__.savedState[path] = initialVal;
|
|
1224
1224
|
|
|
@@ -1523,7 +1523,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1523
1523
|
this.$__.session[sessionNewDocuments].get(this).modifiedPaths &&
|
|
1524
1524
|
!this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath);
|
|
1525
1525
|
if (savedState != null &&
|
|
1526
|
-
|
|
1526
|
+
Object.hasOwn(savedState, savedStatePath) &&
|
|
1527
1527
|
(!isInTransaction || isModifiedWithinTransaction) &&
|
|
1528
1528
|
utils.deepEqual(val, savedState[savedStatePath])) {
|
|
1529
1529
|
this.unmarkModified(path);
|
|
@@ -2005,7 +2005,7 @@ Document.prototype.$get = Document.prototype.get;
|
|
|
2005
2005
|
|
|
2006
2006
|
Document.prototype.$__path = function(path) {
|
|
2007
2007
|
const adhocs = this.$__.adhocPaths;
|
|
2008
|
-
const adhocType = adhocs &&
|
|
2008
|
+
const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null;
|
|
2009
2009
|
|
|
2010
2010
|
if (adhocType) {
|
|
2011
2011
|
return adhocType;
|
|
@@ -2049,7 +2049,7 @@ Document.prototype.$__saveInitialState = function $__saveInitialState(path) {
|
|
|
2049
2049
|
if (savedState != null) {
|
|
2050
2050
|
const firstDot = savedStatePath.indexOf('.');
|
|
2051
2051
|
const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot);
|
|
2052
|
-
if (!
|
|
2052
|
+
if (!Object.hasOwn(savedState, topLevelPath)) {
|
|
2053
2053
|
savedState[topLevelPath] = clone(this.$__getValue(topLevelPath));
|
|
2054
2054
|
}
|
|
2055
2055
|
}
|
|
@@ -2360,7 +2360,7 @@ Document.prototype.$isDefault = function(path) {
|
|
|
2360
2360
|
}
|
|
2361
2361
|
|
|
2362
2362
|
if (typeof path === 'string' && path.indexOf(' ') === -1) {
|
|
2363
|
-
return this.$__.activePaths.getStatePaths('default')
|
|
2363
|
+
return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path);
|
|
2364
2364
|
}
|
|
2365
2365
|
|
|
2366
2366
|
let paths = path;
|
|
@@ -2368,7 +2368,7 @@ Document.prototype.$isDefault = function(path) {
|
|
|
2368
2368
|
paths = paths.split(' ');
|
|
2369
2369
|
}
|
|
2370
2370
|
|
|
2371
|
-
return paths.some(path => this.$__.activePaths.getStatePaths('default')
|
|
2371
|
+
return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path));
|
|
2372
2372
|
};
|
|
2373
2373
|
|
|
2374
2374
|
/**
|
|
@@ -2422,7 +2422,7 @@ Document.prototype.isDirectModified = function(path) {
|
|
|
2422
2422
|
}
|
|
2423
2423
|
|
|
2424
2424
|
if (typeof path === 'string' && path.indexOf(' ') === -1) {
|
|
2425
|
-
const res = this.$__.activePaths.getStatePaths('modify')
|
|
2425
|
+
const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path);
|
|
2426
2426
|
if (res || path.indexOf('.') === -1) {
|
|
2427
2427
|
return res;
|
|
2428
2428
|
}
|
|
@@ -2461,7 +2461,7 @@ Document.prototype.isInit = function(path) {
|
|
|
2461
2461
|
}
|
|
2462
2462
|
|
|
2463
2463
|
if (typeof path === 'string' && path.indexOf(' ') === -1) {
|
|
2464
|
-
return this.$__.activePaths.getStatePaths('init')
|
|
2464
|
+
return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path);
|
|
2465
2465
|
}
|
|
2466
2466
|
|
|
2467
2467
|
let paths = path;
|
|
@@ -2469,7 +2469,7 @@ Document.prototype.isInit = function(path) {
|
|
|
2469
2469
|
paths = paths.split(' ');
|
|
2470
2470
|
}
|
|
2471
2471
|
|
|
2472
|
-
return paths.some(path => this.$__.activePaths.getStatePaths('init')
|
|
2472
|
+
return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path));
|
|
2473
2473
|
};
|
|
2474
2474
|
|
|
2475
2475
|
/**
|
|
@@ -2607,7 +2607,7 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
|
|
|
2607
2607
|
return true;
|
|
2608
2608
|
}
|
|
2609
2609
|
|
|
2610
|
-
if (this.$__.selected
|
|
2610
|
+
if (Object.hasOwn(this.$__.selected, path)) {
|
|
2611
2611
|
return inclusive;
|
|
2612
2612
|
}
|
|
2613
2613
|
|
|
@@ -2779,7 +2779,7 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
|
|
|
2779
2779
|
if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) &&
|
|
2780
2780
|
// Avoid using isDirectModified() here because that does additional checks on whether the parent path
|
|
2781
2781
|
// is direct modified, which can cause performance issues re: gh-14897
|
|
2782
|
-
!subdocParent.$__.activePaths.getStatePaths('modify')
|
|
2782
|
+
!Object.hasOwn(subdocParent.$__.activePaths.getStatePaths('modify'), fullPathToSubdoc) &&
|
|
2783
2783
|
!subdocParent.$isDefault(fullPathToSubdoc)) {
|
|
2784
2784
|
paths.add(fullPathToSubdoc);
|
|
2785
2785
|
|
|
@@ -2850,7 +2850,12 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
|
|
|
2850
2850
|
// Single nested paths (paths embedded under single nested subdocs) will
|
|
2851
2851
|
// be validated on their own when we call `validate()` on the subdoc itself.
|
|
2852
2852
|
// Re: gh-8468
|
|
2853
|
-
|
|
2853
|
+
const singleNestedPaths = doc.$__schema.singleNestedPaths;
|
|
2854
|
+
for (const path of Object.keys(flat)) {
|
|
2855
|
+
if (!Object.hasOwn(singleNestedPaths, path)) {
|
|
2856
|
+
addToPaths(path);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2854
2859
|
}
|
|
2855
2860
|
}
|
|
2856
2861
|
|
|
@@ -4188,7 +4193,7 @@ function applyVirtuals(self, json, options, toObjectOptions) {
|
|
|
4188
4193
|
}
|
|
4189
4194
|
|
|
4190
4195
|
// Allow skipping aliases with `toObject({ virtuals: true, aliases: false })`
|
|
4191
|
-
if (!aliases && schema.aliases
|
|
4196
|
+
if (!aliases && Object.hasOwn(schema.aliases, path)) {
|
|
4192
4197
|
continue;
|
|
4193
4198
|
}
|
|
4194
4199
|
|
|
@@ -5101,7 +5106,7 @@ function checkDivergentArray(doc, path, array) {
|
|
|
5101
5106
|
// would be similarly destructive as we never received all
|
|
5102
5107
|
// elements of the array and potentially would overwrite data.
|
|
5103
5108
|
const check = pop.options.match ||
|
|
5104
|
-
pop.options.options &&
|
|
5109
|
+
pop.options.options && Object.hasOwn(pop.options.options, 'limit') || // 0 is not permitted
|
|
5105
5110
|
pop.options.options && pop.options.options.skip || // 0 is permitted
|
|
5106
5111
|
pop.options.select && // deselected _id?
|
|
5107
5112
|
(pop.options.select._id === 0 ||
|
|
@@ -5426,6 +5431,38 @@ Document.prototype.$__hasOnlyPrimitiveValues = function $__hasOnlyPrimitiveValue
|
|
|
5426
5431
|
}));
|
|
5427
5432
|
};
|
|
5428
5433
|
|
|
5434
|
+
/*!
|
|
5435
|
+
* Increment this document's version if necessary.
|
|
5436
|
+
*/
|
|
5437
|
+
|
|
5438
|
+
Document.prototype._applyVersionIncrement = function _applyVersionIncrement() {
|
|
5439
|
+
if (!this.$__.version) return;
|
|
5440
|
+
const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
|
|
5441
|
+
|
|
5442
|
+
this.$__.version = undefined;
|
|
5443
|
+
if (doIncrement) {
|
|
5444
|
+
const key = this.$__schema.options.versionKey;
|
|
5445
|
+
const version = this.$__getValue(key) || 0;
|
|
5446
|
+
this.$__setValue(key, version + 1); // increment version if was successful
|
|
5447
|
+
}
|
|
5448
|
+
};
|
|
5449
|
+
|
|
5450
|
+
/*!
|
|
5451
|
+
* Increment this document's version if necessary.
|
|
5452
|
+
*/
|
|
5453
|
+
|
|
5454
|
+
Document.prototype._applyVersionIncrement = function _applyVersionIncrement() {
|
|
5455
|
+
if (!this.$__.version) return;
|
|
5456
|
+
const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
|
|
5457
|
+
|
|
5458
|
+
this.$__.version = undefined;
|
|
5459
|
+
if (doIncrement) {
|
|
5460
|
+
const key = this.$__schema.options.versionKey;
|
|
5461
|
+
const version = this.$__getValue(key) || 0;
|
|
5462
|
+
this.$__setValue(key, version + 1); // increment version if was successful
|
|
5463
|
+
}
|
|
5464
|
+
};
|
|
5465
|
+
|
|
5429
5466
|
/*!
|
|
5430
5467
|
* Module exports.
|
|
5431
5468
|
*/
|
|
@@ -124,6 +124,7 @@ function iter(i) {
|
|
|
124
124
|
|
|
125
125
|
let _args = args;
|
|
126
126
|
let callback = null;
|
|
127
|
+
let timeout = null;
|
|
127
128
|
if (this._shouldBufferCommands() && this.buffer) {
|
|
128
129
|
this.conn.emit('buffer', {
|
|
129
130
|
_id: opId,
|
|
@@ -136,7 +137,6 @@ function iter(i) {
|
|
|
136
137
|
let callback;
|
|
137
138
|
let _args = args;
|
|
138
139
|
let promise = null;
|
|
139
|
-
let timeout = null;
|
|
140
140
|
if (syncCollectionMethods[i] && typeof lastArg === 'function') {
|
|
141
141
|
this.addQueue(i, _args);
|
|
142
142
|
callback = lastArg;
|
|
@@ -239,6 +239,9 @@ function iter(i) {
|
|
|
239
239
|
|
|
240
240
|
if (syncCollectionMethods[i] && typeof lastArg === 'function') {
|
|
241
241
|
const result = collection[i].apply(collection, _args.slice(0, _args.length - 1));
|
|
242
|
+
if (timeout != null) {
|
|
243
|
+
clearTimeout(timeout);
|
|
244
|
+
}
|
|
242
245
|
this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result });
|
|
243
246
|
return lastArg.call(this, null, result);
|
|
244
247
|
}
|
|
@@ -247,6 +250,9 @@ function iter(i) {
|
|
|
247
250
|
if (ret != null && typeof ret.then === 'function') {
|
|
248
251
|
return ret.then(
|
|
249
252
|
result => {
|
|
253
|
+
if (timeout != null) {
|
|
254
|
+
clearTimeout(timeout);
|
|
255
|
+
}
|
|
250
256
|
if (typeof lastArg === 'function') {
|
|
251
257
|
lastArg(null, result);
|
|
252
258
|
} else {
|
|
@@ -255,6 +261,9 @@ function iter(i) {
|
|
|
255
261
|
return result;
|
|
256
262
|
},
|
|
257
263
|
error => {
|
|
264
|
+
if (timeout != null) {
|
|
265
|
+
clearTimeout(timeout);
|
|
266
|
+
}
|
|
258
267
|
if (typeof lastArg === 'function') {
|
|
259
268
|
lastArg(error);
|
|
260
269
|
return;
|
|
@@ -265,10 +274,16 @@ function iter(i) {
|
|
|
265
274
|
}
|
|
266
275
|
);
|
|
267
276
|
}
|
|
277
|
+
if (timeout != null) {
|
|
278
|
+
clearTimeout(timeout);
|
|
279
|
+
}
|
|
268
280
|
return ret;
|
|
269
281
|
} catch (error) {
|
|
270
282
|
// Collection operation may throw because of max bson size, catch it here
|
|
271
283
|
// See gh-3906
|
|
284
|
+
if (timeout != null) {
|
|
285
|
+
clearTimeout(timeout);
|
|
286
|
+
}
|
|
272
287
|
if (typeof lastArg === 'function') {
|
|
273
288
|
return lastArg(error);
|
|
274
289
|
} else {
|
|
@@ -508,7 +508,7 @@ function _setClient(conn, client, options, dbName) {
|
|
|
508
508
|
conn.onOpen();
|
|
509
509
|
|
|
510
510
|
for (const i in conn.collections) {
|
|
511
|
-
if (
|
|
511
|
+
if (Object.hasOwn(conn.collections, i)) {
|
|
512
512
|
conn.collections[i].onOpen();
|
|
513
513
|
}
|
|
514
514
|
}
|
package/lib/helpers/common.js
CHANGED
|
@@ -55,7 +55,7 @@ function flatten(update, path, options, schema) {
|
|
|
55
55
|
if (isNested) {
|
|
56
56
|
const paths = Object.keys(schema.paths);
|
|
57
57
|
for (const p of paths) {
|
|
58
|
-
if (p.startsWith(path + key + '.') && !
|
|
58
|
+
if (p.startsWith(path + key + '.') && !Object.hasOwn(result, p)) {
|
|
59
59
|
result[p] = void 0;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -7,7 +7,7 @@ module.exports = function applySchemaCollation(indexKeys, indexOptions, schemaOp
|
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
if (
|
|
10
|
+
if (Object.hasOwn(schemaOptions, 'collation') && !Object.hasOwn(indexOptions, 'collation')) {
|
|
11
11
|
indexOptions.collation = schemaOptions.collation;
|
|
12
12
|
}
|
|
13
13
|
};
|
|
@@ -28,7 +28,7 @@ module.exports = function applyMethods(model, schema) {
|
|
|
28
28
|
}
|
|
29
29
|
for (const method of Object.keys(schema.methods)) {
|
|
30
30
|
const fn = schema.methods[method];
|
|
31
|
-
if (schema.tree
|
|
31
|
+
if (Object.hasOwn(schema.tree, method)) {
|
|
32
32
|
throw new Error('You have a method and a property in your schema both ' +
|
|
33
33
|
'named "' + method + '"');
|
|
34
34
|
}
|
|
@@ -118,7 +118,10 @@ module.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne,
|
|
|
118
118
|
if (model.schema.$timestamps != null && doInitTimestamps) {
|
|
119
119
|
const createdAt = model.schema.$timestamps.createdAt;
|
|
120
120
|
const updatedAt = model.schema.$timestamps.updatedAt;
|
|
121
|
-
applyTimestampsToUpdate(now, createdAt, updatedAt, update, {
|
|
121
|
+
applyTimestampsToUpdate(now, createdAt, updatedAt, update, {
|
|
122
|
+
timestamps: updateOne.timestamps,
|
|
123
|
+
overwriteImmutable: updateOne.overwriteImmutable
|
|
124
|
+
});
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
if (doInitTimestamps) {
|
|
@@ -150,7 +153,8 @@ module.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne,
|
|
|
150
153
|
strict: strict,
|
|
151
154
|
upsert: updateOne.upsert,
|
|
152
155
|
arrayFilters: updateOne.arrayFilters,
|
|
153
|
-
overwriteDiscriminatorKey: updateOne.overwriteDiscriminatorKey
|
|
156
|
+
overwriteDiscriminatorKey: updateOne.overwriteDiscriminatorKey,
|
|
157
|
+
overwriteImmutable: updateOne.overwriteImmutable
|
|
154
158
|
}, model, updateOne['filter']);
|
|
155
159
|
|
|
156
160
|
return updateOne;
|
|
@@ -185,7 +189,10 @@ module.exports.castUpdateMany = function castUpdateMany(originalModel, updateMan
|
|
|
185
189
|
if (model.schema.$timestamps != null && doInitTimestamps) {
|
|
186
190
|
const createdAt = model.schema.$timestamps.createdAt;
|
|
187
191
|
const updatedAt = model.schema.$timestamps.updatedAt;
|
|
188
|
-
applyTimestampsToUpdate(now, createdAt, updatedAt, updateMany['update'], {
|
|
192
|
+
applyTimestampsToUpdate(now, createdAt, updatedAt, updateMany['update'], {
|
|
193
|
+
timestamps: updateMany.timestamps,
|
|
194
|
+
overwriteImmutable: updateMany.overwriteImmutable
|
|
195
|
+
});
|
|
189
196
|
}
|
|
190
197
|
if (doInitTimestamps) {
|
|
191
198
|
applyTimestampsToChildren(now, updateMany['update'], model.schema);
|
|
@@ -208,7 +215,8 @@ module.exports.castUpdateMany = function castUpdateMany(originalModel, updateMan
|
|
|
208
215
|
strict: strict,
|
|
209
216
|
upsert: updateMany.upsert,
|
|
210
217
|
arrayFilters: updateMany.arrayFilters,
|
|
211
|
-
overwriteDiscriminatorKey: updateMany.overwriteDiscriminatorKey
|
|
218
|
+
overwriteDiscriminatorKey: updateMany.overwriteDiscriminatorKey,
|
|
219
|
+
overwriteImmutable: updateMany.overwriteImmutable
|
|
212
220
|
}, model, updateMany['filter']);
|
|
213
221
|
};
|
|
214
222
|
|
|
@@ -289,13 +297,12 @@ function _addDiscriminatorToObject(schema, obj) {
|
|
|
289
297
|
|
|
290
298
|
function decideModelByObject(model, object) {
|
|
291
299
|
const discriminatorKey = model.schema.options.discriminatorKey;
|
|
292
|
-
if (object != null &&
|
|
300
|
+
if (object != null && Object.hasOwn(object, discriminatorKey)) {
|
|
293
301
|
model = getDiscriminatorByValue(model.discriminators, object[discriminatorKey]) || model;
|
|
294
302
|
}
|
|
295
303
|
return model;
|
|
296
304
|
}
|
|
297
305
|
|
|
298
|
-
|
|
299
306
|
/**
|
|
300
307
|
* gets timestamps option for a given operation. If the option is set within an individual operation, use it. Otherwise, use the global timestamps option configured in the `bulkWrite` options. Overall default is `true`.
|
|
301
308
|
* @api private
|
|
@@ -386,13 +386,13 @@ function _virtualPopulate(model, docs, options, _virtualRes) {
|
|
|
386
386
|
}
|
|
387
387
|
data.count = virtual.options.count;
|
|
388
388
|
|
|
389
|
-
if (virtual.options.skip != null && !
|
|
389
|
+
if (virtual.options.skip != null && !Object.hasOwn(options, 'skip')) {
|
|
390
390
|
options.skip = virtual.options.skip;
|
|
391
391
|
}
|
|
392
|
-
if (virtual.options.limit != null && !
|
|
392
|
+
if (virtual.options.limit != null && !Object.hasOwn(options, 'limit')) {
|
|
393
393
|
options.limit = virtual.options.limit;
|
|
394
394
|
}
|
|
395
|
-
if (virtual.options.perDocumentLimit != null && !
|
|
395
|
+
if (virtual.options.perDocumentLimit != null && !Object.hasOwn(options, 'perDocumentLimit')) {
|
|
396
396
|
options.perDocumentLimit = virtual.options.perDocumentLimit;
|
|
397
397
|
}
|
|
398
398
|
let foreignField = virtual.options.foreignField;
|
|
@@ -56,7 +56,7 @@ module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, mod
|
|
|
56
56
|
const refValue = mpath.get(refPath, doc, lookupLocalFields);
|
|
57
57
|
|
|
58
58
|
let modelNames;
|
|
59
|
-
if (modelSchema != null && modelSchema.virtuals
|
|
59
|
+
if (modelSchema != null && Object.hasOwn(modelSchema.virtuals, refPath)) {
|
|
60
60
|
modelNames = [modelSchema.virtuals[refPath].applyGetters(void 0, doc)];
|
|
61
61
|
} else {
|
|
62
62
|
modelNames = Array.isArray(refValue) ? refValue : [refValue];
|
|
@@ -16,7 +16,7 @@ module.exports = function removeDeselectedForeignField(foreignFields, options, d
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
for (const foreignField of foreignFields) {
|
|
19
|
-
if (!
|
|
19
|
+
if (!Object.hasOwn(projection, '-' + foreignField)) {
|
|
20
20
|
continue;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -43,7 +43,7 @@ function applyExclusiveProjection(doc, projection, hasIncludedChildren, projecti
|
|
|
43
43
|
|
|
44
44
|
for (const key of Object.keys(ret)) {
|
|
45
45
|
const fullPath = prefix ? prefix + '.' + key : key;
|
|
46
|
-
if (
|
|
46
|
+
if (Object.hasOwn(projection, fullPath) || Object.hasOwn(projectionLimb, key)) {
|
|
47
47
|
if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) {
|
|
48
48
|
ret[key] = applyExclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath);
|
|
49
49
|
} else {
|
|
@@ -68,7 +68,7 @@ function applyInclusiveProjection(doc, projection, hasIncludedChildren, projecti
|
|
|
68
68
|
|
|
69
69
|
for (const key of Object.keys(ret)) {
|
|
70
70
|
const fullPath = prefix ? prefix + '.' + key : key;
|
|
71
|
-
if (
|
|
71
|
+
if (Object.hasOwn(projection, fullPath) || Object.hasOwn(projectionLimb, key)) {
|
|
72
72
|
if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) {
|
|
73
73
|
ret[key] = applyInclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath);
|
|
74
74
|
}
|
|
@@ -71,7 +71,7 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
|
|
|
71
71
|
const schemaKey = updatedPathsByFilter[filterKey] + '.' + key;
|
|
72
72
|
const arrayFilterKey = filterKey + '.' + key;
|
|
73
73
|
if (schemaKey === discriminatorFilterPath) {
|
|
74
|
-
const filter = arrayFilters.find(filter =>
|
|
74
|
+
const filter = arrayFilters.find(filter => Object.hasOwn(filter, arrayFilterKey));
|
|
75
75
|
if (filter != null) {
|
|
76
76
|
discriminatorKey = filter[arrayFilterKey];
|
|
77
77
|
}
|
|
@@ -136,14 +136,14 @@ function pathExistsInUpdate(update, targetPath, pathPieces) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// Check exact match
|
|
139
|
-
if (
|
|
139
|
+
if (Object.hasOwn(update, targetPath)) {
|
|
140
140
|
return true;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// Check if any parent path exists
|
|
144
144
|
let cur = pathPieces[0];
|
|
145
145
|
for (let i = 1; i < pathPieces.length; ++i) {
|
|
146
|
-
if (
|
|
146
|
+
if (Object.hasOwn(update, cur)) {
|
|
147
147
|
return true;
|
|
148
148
|
}
|
|
149
149
|
cur += '.' + pathPieces[i];
|
|
@@ -23,7 +23,7 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
23
23
|
}
|
|
24
24
|
const createdAt = handleTimestampOption(timestamps, 'createdAt');
|
|
25
25
|
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
|
26
|
-
const currentTime = timestamps != null &&
|
|
26
|
+
const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ?
|
|
27
27
|
timestamps.currentTime :
|
|
28
28
|
null;
|
|
29
29
|
const schemaAdditions = {};
|
|
@@ -74,39 +74,52 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
|
|
|
74
74
|
updates.$set[updatedAt] = now;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
if (
|
|
77
|
+
if (Object.hasOwn(updates, updatedAt)) {
|
|
78
78
|
delete updates[updatedAt];
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
if (!skipCreatedAt && createdAt) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
83
|
+
const overwriteImmutable = get(options, 'overwriteImmutable', false);
|
|
84
|
+
const hasUserCreatedAt = currentUpdate[createdAt] != null || currentUpdate?.$set[createdAt] != null;
|
|
85
|
+
|
|
86
|
+
// If overwriteImmutable is true and user provided createdAt, keep their value
|
|
87
|
+
if (overwriteImmutable && hasUserCreatedAt) {
|
|
88
|
+
// Move createdAt from top-level to $set if needed
|
|
89
|
+
if (currentUpdate[createdAt] != null) {
|
|
90
|
+
updates.$set[createdAt] = currentUpdate[createdAt];
|
|
91
|
+
delete currentUpdate[createdAt];
|
|
92
|
+
}
|
|
93
|
+
// User's value is already in $set, nothing more to do
|
|
94
|
+
} else {
|
|
95
|
+
if (currentUpdate[createdAt]) {
|
|
96
|
+
delete currentUpdate[createdAt];
|
|
97
|
+
}
|
|
98
|
+
if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
|
|
99
|
+
delete currentUpdate.$set[createdAt];
|
|
100
|
+
}
|
|
101
|
+
let timestampSet = false;
|
|
102
|
+
if (createdAt.indexOf('.') !== -1) {
|
|
103
|
+
const pieces = createdAt.split('.');
|
|
104
|
+
for (let i = 1; i < pieces.length; ++i) {
|
|
105
|
+
const remnant = pieces.slice(-i).join('.');
|
|
106
|
+
const start = pieces.slice(0, -i).join('.');
|
|
107
|
+
if (currentUpdate[start] != null) {
|
|
108
|
+
currentUpdate[start][remnant] = now;
|
|
109
|
+
timestampSet = true;
|
|
110
|
+
break;
|
|
111
|
+
} else if (currentUpdate.$set && currentUpdate.$set[start]) {
|
|
112
|
+
currentUpdate.$set[start][remnant] = now;
|
|
113
|
+
timestampSet = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
|
-
}
|
|
106
118
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
if (!timestampSet) {
|
|
120
|
+
updates.$setOnInsert = updates.$setOnInsert || {};
|
|
121
|
+
updates.$setOnInsert[createdAt] = now;
|
|
122
|
+
}
|
|
110
123
|
}
|
|
111
124
|
}
|
|
112
125
|
|