From 335141c2272ef457e8e64d0b06e4d11130217938 Mon Sep 17 00:00:00 2001 From: Hafez Date: Thu, 4 Dec 2025 03:15:49 +0100 Subject: [PATCH 1/4] perf: optimize Object.keys() checks and optional chaining --- lib/cast.js | 13 +-- lib/connection.js | 14 ++- lib/cursor/changeStream.js | 4 +- lib/document.js | 26 +++--- lib/drivers/node-mongodb-native/collection.js | 18 +--- lib/drivers/node-mongodb-native/connection.js | 19 +---- lib/helpers/document/compile.js | 4 +- .../populate/getModelsMapForPopulate.js | 11 ++- lib/helpers/query/castUpdate.js | 6 +- lib/helpers/schema/applyReadConcern.js | 2 +- lib/helpers/schema/applyWriteConcern.js | 6 +- lib/helpers/schema/getIndexes.js | 2 +- lib/helpers/update/applyTimestampsToUpdate.js | 3 +- lib/helpers/update/castArrayFilters.js | 3 +- lib/model.js | 36 ++------ lib/mongoose.js | 5 +- lib/plugins/trackTransaction.js | 4 +- lib/query.js | 85 ++++++++++--------- lib/queryHelpers.js | 9 +- lib/schema.js | 4 +- lib/schema/array.js | 4 +- lib/schema/mixed.js | 2 +- lib/schema/number.js | 4 +- lib/schema/string.js | 4 +- lib/schema/subdocument.js | 5 +- lib/schemaType.js | 2 +- lib/types/array/index.js | 4 +- lib/types/array/methods/index.js | 2 +- lib/types/documentArray/index.js | 4 +- lib/types/documentArray/methods/index.js | 6 +- lib/types/subdocument.js | 2 +- lib/utils.js | 24 +++++- 32 files changed, 157 insertions(+), 180 deletions(-) diff --git a/lib/cast.js b/lib/cast.js index d5ea75227b9..5655601b038 100644 --- a/lib/cast.js +++ b/lib/cast.js @@ -79,7 +79,7 @@ module.exports = function cast(schema, obj, options, context) { val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context); } - if (Object.keys(val[k]).length === 0 && beforeCastKeysLength !== 0) { + if (utils.hasNoOwnKeys(val[k]) && beforeCastKeysLength !== 0) { val.splice(k, 1); } } @@ -126,10 +126,7 @@ module.exports = function cast(schema, obj, options, context) { const pathFirstHalf = split.slice(0, j).join('.'); const pathLastHalf = split.slice(j).join('.'); const _schematype = schema.path(pathFirstHalf); - const discriminatorKey = _schematype && - _schematype.schema && - _schematype.schema.options && - _schematype.schema.options.discriminatorKey; + const discriminatorKey = _schematype?.schema?.options?.discriminatorKey; // gh-6027: if we haven't found the schematype but this path is // underneath an embedded discriminator and the embedded discriminator @@ -430,11 +427,7 @@ function getStrictQuery(queryOptions, schemaUserProvidedOptions, schemaOptions, if ('strictQuery' in schemaUserProvidedOptions) { return schemaUserProvidedOptions.strictQuery; } - const mongooseOptions = context && - context.mongooseCollection && - context.mongooseCollection.conn && - context.mongooseCollection.conn.base && - context.mongooseCollection.conn.base.options; + const mongooseOptions = context?.mongooseCollection?.conn?.base?.options; if (mongooseOptions) { if ('strictQuery' in mongooseOptions) { return mongooseOptions.strictQuery; diff --git a/lib/connection.js b/lib/connection.js index 8d9d27e8ff0..e4aad8e4f80 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -1250,8 +1250,11 @@ Connection.prototype._close = async function _close(force, destroy) { const conn = this; switch (this.readyState) { case STATES.disconnected: - if (destroy && this.base.connections.indexOf(conn) !== -1) { - this.base.connections.splice(this.base.connections.indexOf(conn), 1); + if (destroy) { + const index = this.base.connections.indexOf(conn); + if (index !== -1) { + this.base.connections.splice(index, 1); + } } if (!closeCalled) { await this.doClose(force); @@ -1262,8 +1265,11 @@ Connection.prototype._close = async function _close(force, destroy) { case STATES.connected: this.readyState = STATES.disconnecting; await this.doClose(force); - if (destroy && _this.base.connections.indexOf(conn) !== -1) { - this.base.connections.splice(this.base.connections.indexOf(conn), 1); + if (destroy) { + const index = _this.base.connections.indexOf(conn); + if (index !== -1) { + this.base.connections.splice(index, 1); + } } this.onClose(force); diff --git a/lib/cursor/changeStream.js b/lib/cursor/changeStream.js index b41e2379e83..3221a29fb9c 100644 --- a/lib/cursor/changeStream.js +++ b/lib/cursor/changeStream.js @@ -83,7 +83,7 @@ class ChangeStream extends EventEmitter { driverChangeStreamEvents.forEach(ev => { this.driverChangeStream.on(ev, data => { - if (data != null && data.fullDocument != null && this.options && this.options.hydrate) { + if (data?.fullDocument != null && this.options?.hydrate) { data.fullDocument = this.options.model.hydrate(data.fullDocument); } this.emit(ev, data); @@ -102,7 +102,7 @@ class ChangeStream extends EventEmitter { driverChangeStreamEvents.forEach(ev => { this.driverChangeStream.on(ev, data => { - if (data != null && data.fullDocument != null && this.options && this.options.hydrate) { + if (data?.fullDocument != null && this.options?.hydrate) { data.fullDocument = this.options.model.hydrate(data.fullDocument); } this.emit(ev, data); diff --git a/lib/document.js b/lib/document.js index 45da5eb3f13..f339cc3c9ef 100644 --- a/lib/document.js +++ b/lib/document.js @@ -1015,17 +1015,21 @@ Document.prototype.$timestamps = function $timestamps(value) { */ Document.prototype.overwrite = function overwrite(obj) { - const keys = Array.from(new Set(Object.keys(this._doc).concat(Object.keys(obj)))); + const keys = new Set(Object.keys(this._doc)); + for (const key of Object.keys(obj)) { + keys.add(key); + } + const schemaOptions = this.$__schema.options; for (const key of keys) { if (key === '_id') { continue; } // Explicitly skip version key - if (this.$__schema.options.versionKey && key === this.$__schema.options.versionKey) { + if (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } - if (this.$__schema.options.discriminatorKey && key === this.$__schema.options.discriminatorKey) { + if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); @@ -1697,7 +1701,7 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa if (shouldModify) { if (this.$__.primitiveAtomics && this.$__.primitiveAtomics[path]) { delete this.$__.primitiveAtomics[path]; - if (Object.keys(this.$__.primitiveAtomics).length === 0) { + if (utils.hasNoOwnKeys(this.$__.primitiveAtomics)) { delete this.$__.primitiveAtomics; } } @@ -3001,7 +3005,7 @@ Document.prototype.$__validate = async function $__validate(pathsToValidate, opt delete validationError.errors[errPath]; } } - if (Object.keys(validationError.errors).length === 0) { + if (utils.hasNoOwnKeys(validationError.errors)) { validationError = void 0; } } @@ -3402,7 +3406,7 @@ Document.prototype.$markValid = function(path) { } delete this.$__.validationError.errors[path]; - if (Object.keys(this.$__.validationError.errors).length === 0) { + if (utils.hasNoOwnKeys(this.$__.validationError.errors)) { this.$__.validationError = null; } }; @@ -3422,7 +3426,7 @@ function _markValidSubpaths(doc, path) { delete doc.$__.validationError.errors[key]; } } - if (Object.keys(doc.$__.validationError.errors).length === 0) { + if (utils.hasNoOwnKeys(doc.$__.validationError.errors)) { doc.$__.validationError = null; } } @@ -3497,7 +3501,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) { */ Document.prototype.$isValid = function(path) { - if (this.$__.validationError == null || Object.keys(this.$__.validationError.errors).length === 0) { + if (this.$__.validationError == null || utils.hasNoOwnKeys(this.$__.validationError.errors)) { return true; } if (path == null) { @@ -4375,7 +4379,7 @@ function omitDeselectedFields(self, json) { selected = {}; queryhelpers.applyPaths(selected, schema); } - if (selected == null || Object.keys(selected).length === 0) { + if (selected == null || utils.hasNoOwnKeys(selected)) { return json; } @@ -5057,7 +5061,7 @@ Document.prototype.$__delta = function $__delta() { this.$__version(where, delta); } - if (Object.keys(delta).length === 0) { + if (utils.hasNoOwnKeys(delta)) { return [where, null]; } @@ -5109,7 +5113,7 @@ function checkDivergentArray(doc, path, array) { if (check) { const atomics = array[arrayAtomicsSymbol]; - if (Object.keys(atomics).length === 0 || atomics.$set || atomics.$pop) { + if (utils.hasNoOwnKeys(atomics) || atomics.$set || atomics.$pop) { return path; } } diff --git a/lib/drivers/node-mongodb-native/collection.js b/lib/drivers/node-mongodb-native/collection.js index 1e4678ff93f..cf25c74ddc6 100644 --- a/lib/drivers/node-mongodb-native/collection.js +++ b/lib/drivers/node-mongodb-native/collection.js @@ -97,15 +97,8 @@ function iter(i) { const collection = this._getCollection(); const args = Array.from(arguments); const _this = this; - const globalDebug = _this && - _this.conn && - _this.conn.base && - _this.conn.base.options && - _this.conn.base.options.debug; - const connectionDebug = _this && - _this.conn && - _this.conn.options && - _this.conn.options.debug; + const globalDebug = _this?.conn?.base?.options?.debug; + const connectionDebug = _this?.conn?.options?.debug; const debug = connectionDebug == null ? globalDebug : connectionDebug; const lastArg = arguments[arguments.length - 1]; const opId = new ObjectId(); @@ -428,12 +421,7 @@ function format(obj, sub, color, shell) { formatDate(x, key, shell); } else if (_constructorName === 'ClientSession') { x[key] = inspectable('ClientSession("' + - ( - x[key] && - x[key].id && - x[key].id.id && - x[key].id.id.buffer || '' - ).toString('hex') + '")'); + (x[key]?.id?.id?.buffer || '').toString('hex') + '")'); } else if (Array.isArray(x[key])) { x[key] = x[key].map(map); } else if (error != null) { diff --git a/lib/drivers/node-mongodb-native/connection.js b/lib/drivers/node-mongodb-native/connection.js index 3f4be863c2e..f00a90224d8 100644 --- a/lib/drivers/node-mongodb-native/connection.js +++ b/lib/drivers/node-mongodb-native/connection.js @@ -435,18 +435,8 @@ function _setClient(conn, client, options, dbName) { const db = dbName != null ? client.db(dbName) : client.db(); conn.db = db; conn.client = client; - conn.host = client && - client.s && - client.s.options && - client.s.options.hosts && - client.s.options.hosts[0] && - client.s.options.hosts[0].host || void 0; - conn.port = client && - client.s && - client.s.options && - client.s.options.hosts && - client.s.options.hosts[0] && - client.s.options.hosts[0].port || void 0; + conn.host = client?.s?.options?.hosts?.[0]?.host; + conn.port = client?.s?.options?.hosts?.[0]?.port; conn.name = dbName != null ? dbName : db.databaseName; conn._closeCalled = client._closeCalled; @@ -463,10 +453,7 @@ function _setClient(conn, client, options, dbName) { } }; - const type = client && - client.topology && - client.topology.description && - client.topology.description.type || ''; + const type = client?.topology?.description?.type || ''; if (type === 'Single') { client.on('serverDescriptionChanged', ev => { diff --git a/lib/helpers/document/compile.js b/lib/helpers/document/compile.js index 51721e7a490..c5d2aa3977f 100644 --- a/lib/helpers/document/compile.js +++ b/lib/helpers/document/compile.js @@ -132,7 +132,7 @@ function defineKey({ prop, subprops, prototype, prefix, options }) { writable: false, value: function() { return _this.get(path, null, { - virtuals: this && this.schema && this.schema.options && this.schema.options.toObject && this.schema.options.toObject.virtuals || null + virtuals: this?.schema?.options?.toObject?.virtuals || null }); } }); @@ -143,7 +143,7 @@ function defineKey({ prop, subprops, prototype, prefix, options }) { writable: false, value: function() { return _this.get(path, null, { - virtuals: this && this.schema && this.schema.options && this.schema.options.toJSON && this.schema.options.toJSON.virtuals || null + virtuals: this?.schema?.options?.toJSON?.virtuals || null }); } }); diff --git a/lib/helpers/populate/getModelsMapForPopulate.js b/lib/helpers/populate/getModelsMapForPopulate.js index 7d1b3f47fde..b706bf5a92c 100644 --- a/lib/helpers/populate/getModelsMapForPopulate.js +++ b/lib/helpers/populate/getModelsMapForPopulate.js @@ -26,6 +26,7 @@ module.exports = function getModelsMapForPopulate(model, docs, options) { let schema; let refPath; let modelNames; + let modelNamesSet; const available = {}; const modelSchema = model.schema; @@ -123,8 +124,10 @@ module.exports = function getModelsMapForPopulate(model, docs, options) { continue; } modelNames = modelNames || []; + modelNamesSet = modelNamesSet || new Set(); for (const modelName of _modelNames) { - if (modelNames.indexOf(modelName) === -1) { + if (modelNamesSet.has(modelName) === false) { + modelNamesSet.add(modelName); modelNames.push(modelName); } } @@ -225,8 +228,8 @@ module.exports = function getModelsMapForPopulate(model, docs, options) { schema = schema.$__schemaType; } - const ref = schema && schema.options && schema.options.ref; - refPath = schema && schema.options && schema.options.refPath; + const ref = schema?.options?.ref; + refPath = schema?.options?.refPath; if (schema != null && schema[schemaMixedSymbol] && !ref && @@ -721,7 +724,7 @@ function _findRefPathForDiscriminators(doc, modelSchema, data, options, normaliz if (schematype != null && schematype.$isMongooseDocumentArray && schematype.Constructor.discriminators != null && - Object.keys(schematype.Constructor.discriminators).length !== 0) { + utils.hasNoOwnKeys(schematype.Constructor.discriminators) === false) { const subdocs = utils.getValue(cur, doc); const remnant = options.path.substring(cur.length + 1); const discriminatorKey = schematype.Constructor.schema.options.discriminatorKey; diff --git a/lib/helpers/query/castUpdate.js b/lib/helpers/query/castUpdate.js index bc8bdf627bb..114d19a9e06 100644 --- a/lib/helpers/query/castUpdate.js +++ b/lib/helpers/query/castUpdate.js @@ -153,9 +153,9 @@ module.exports = function castUpdate(schema, obj, options, context, filter) { } } - if (Object.keys(ret).length === 0 && + if (utils.hasNoOwnKeys(ret) && options.upsert && - Object.keys(filter).length > 0) { + utils.hasNoOwnKeys(filter) === false) { // Trick the driver into allowing empty upserts to work around // https://github.com/mongodb/node-mongodb-native/pull/2490 // Shallow clone to avoid passing defaults in re: gh-13962 @@ -376,7 +376,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, prefix) { // we should be able to set a schema-less field // to an empty object literal hasKeys |= walkUpdatePath(schema, val, op, options, context, filter, prefix + key) || - (utils.isObject(val) && Object.keys(val).length === 0); + (utils.isObject(val) && utils.hasNoOwnKeys(val)); } } else { const isModifier = (key === '$each' || key === '$or' || key === '$and' || key === '$in'); diff --git a/lib/helpers/schema/applyReadConcern.js b/lib/helpers/schema/applyReadConcern.js index 050fa9c6df0..1367368abdd 100644 --- a/lib/helpers/schema/applyReadConcern.js +++ b/lib/helpers/schema/applyReadConcern.js @@ -9,7 +9,7 @@ module.exports = function applyReadConcern(schema, options) { // because you shouldn't set read concern on individual operations // within a transaction. // See: https://www.mongodb.com/docs/manual/reference/read-concern/ - if (options && options.session && options.session.transaction) { + if (options?.session?.transaction) { return; } diff --git a/lib/helpers/schema/applyWriteConcern.js b/lib/helpers/schema/applyWriteConcern.js index 28338cf58c3..d54d4803b70 100644 --- a/lib/helpers/schema/applyWriteConcern.js +++ b/lib/helpers/schema/applyWriteConcern.js @@ -1,5 +1,7 @@ 'use strict'; +const utils = require('../../utils'); + module.exports = function applyWriteConcern(schema, options) { if (options.writeConcern != null) { return; @@ -7,11 +9,11 @@ module.exports = function applyWriteConcern(schema, options) { // Don't apply default write concern to operations in transactions, // because setting write concern on an operation in a transaction is an error // See: https://www.mongodb.com/docs/manual/reference/write-concern/ - if (options && options.session && options.session.transaction) { + if (options?.session?.transaction) { return; } const writeConcern = schema.options.writeConcern ?? {}; - if (Object.keys(writeConcern).length != 0) { + if (utils.hasNoOwnKeys(writeConcern) === false) { options.writeConcern = {}; if (!('w' in options) && writeConcern.w != null) { options.writeConcern.w = writeConcern.w; diff --git a/lib/helpers/schema/getIndexes.js b/lib/helpers/schema/getIndexes.js index 362b0785260..80e3a96db00 100644 --- a/lib/helpers/schema/getIndexes.js +++ b/lib/helpers/schema/getIndexes.js @@ -69,7 +69,7 @@ module.exports = function getIndexes(schema) { const index = path._index || (path.embeddedSchemaType && path.embeddedSchemaType._index); - if (index !== false && index !== null && index !== undefined) { + if (index !== false && index != null) { const field = {}; const isObject = helperIsObject(index); const options = isObject ? { ...index } : {}; diff --git a/lib/helpers/update/applyTimestampsToUpdate.js b/lib/helpers/update/applyTimestampsToUpdate.js index e8d3217fbb9..23a06d002af 100644 --- a/lib/helpers/update/applyTimestampsToUpdate.js +++ b/lib/helpers/update/applyTimestampsToUpdate.js @@ -5,6 +5,7 @@ */ const get = require('../get'); +const utils = require('../../utils'); module.exports = applyTimestampsToUpdate; @@ -110,7 +111,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio } } - if (Object.keys(updates.$set).length === 0) { + if (utils.hasNoOwnKeys(updates.$set)) { delete updates.$set; } return updates; diff --git a/lib/helpers/update/castArrayFilters.js b/lib/helpers/update/castArrayFilters.js index ed59af49b7e..1a4e3233b03 100644 --- a/lib/helpers/update/castArrayFilters.js +++ b/lib/helpers/update/castArrayFilters.js @@ -4,6 +4,7 @@ const castFilterPath = require('../query/castFilterPath'); const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); const getPath = require('../schema/getPath'); const updatedPathsByArrayFilter = require('./updatedPathsByArrayFilter'); +const utils = require('../../utils'); module.exports = function castArrayFilters(query) { const arrayFilters = query.options.arrayFilters; @@ -74,7 +75,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt if (updatedPathsByFilter[key] === null) { continue; } - if (Object.keys(updatedPathsByFilter).length === 0) { + if (utils.hasNoOwnKeys(updatedPathsByFilter)) { continue; } const dot = key.indexOf('.'); diff --git a/lib/model.js b/lib/model.js index 74d990b68c4..2a36c255639 100644 --- a/lib/model.js +++ b/lib/model.js @@ -434,7 +434,7 @@ Model.prototype.$__save = async function $__save(options) { continue; } minimize(updateOp[key]); - if (Object.keys(updateOp[key]).length === 0) { + if (utils.hasNoOwnKeys(updateOp[key])) { delete updateOp[key]; update.$unset = update.$unset || {}; update.$unset[key] = 1; @@ -1146,25 +1146,16 @@ Model.createCollection = async function createCollection(options) { throw err; }); - const collectionOptions = this && - this.schema && - this.schema.options && - this.schema.options.collectionOptions; + const collectionOptions = this?.schema?.options?.collectionOptions; if (collectionOptions != null) { options = Object.assign({}, collectionOptions, options); } - const schemaCollation = this && - this.schema && - this.schema.options && - this.schema.options.collation; + const schemaCollation = this?.schema?.options?.collation; if (schemaCollation != null) { options = Object.assign({ collation: schemaCollation }, options); } - const capped = this && - this.schema && - this.schema.options && - this.schema.options.capped; + const capped = this?.schema?.options?.capped; if (capped != null) { if (typeof capped === 'number') { options = Object.assign({ capped: true, size: capped }, options); @@ -1172,10 +1163,7 @@ Model.createCollection = async function createCollection(options) { options = Object.assign({ capped: true }, capped, options); } } - const timeseries = this && - this.schema && - this.schema.options && - this.schema.options.timeseries; + const timeseries = this?.schema?.options?.timeseries; if (timeseries != null) { options = Object.assign({ timeseries }, options); if (options.expireAfterSeconds != null) { @@ -1190,10 +1178,7 @@ Model.createCollection = async function createCollection(options) { } } - const clusteredIndex = this && - this.schema && - this.schema.options && - this.schema.options.clusteredIndex; + const clusteredIndex = this?.schema?.options?.clusteredIndex; if (clusteredIndex != null) { options = Object.assign({ clusteredIndex: { ...clusteredIndex, unique: true } }, options); } @@ -3977,7 +3962,7 @@ Model.updateOne = function updateOne(conditions, doc, options) { Model.replaceOne = function replaceOne(conditions, doc, options) { _checkContext(this, 'replaceOne'); - const versionKey = this && this.schema && this.schema.options && this.schema.options.versionKey || null; + const versionKey = this?.schema?.options?.versionKey || null; if (versionKey && !doc[versionKey]) { doc[versionKey] = 0; } @@ -4003,10 +3988,7 @@ function _update(model, op, conditions, doc, options) { } options = typeof options === 'function' ? options : clone(options); - const versionKey = model && - model.schema && - model.schema.options && - model.schema.options.versionKey || null; + const versionKey = model?.schema?.options?.versionKey || null; decorateUpdateWithVersionKey(doc, options, versionKey); return mq[op](conditions, doc, options); @@ -4986,7 +4968,7 @@ Model._applyQueryMiddleware = function _applyQueryMiddleware() { return !!contexts.query; } if (hook.name === 'deleteOne' || hook.name === 'updateOne') { - return !!contexts.query || Object.keys(contexts).length === 0; + return !!contexts.query || utils.hasNoOwnKeys(contexts); } if (hook.query != null || hook.document != null) { return !!hook.query; diff --git a/lib/mongoose.js b/lib/mongoose.js index 864b686dd55..9febdf4fc8a 100644 --- a/lib/mongoose.js +++ b/lib/mongoose.js @@ -309,7 +309,7 @@ Mongoose.prototype.set = function getsetOptions(key, value) { if (optionValue && !_mongoose.connection) { _createDefaultConnection(_mongoose); } else if (optionValue === false && _mongoose.connection && _mongoose.connection[defaultConnectionSymbol]) { - if (_mongoose.connection.readyState === STATES.disconnected && Object.keys(_mongoose.connection.models).length === 0) { + if (_mongoose.connection.readyState === STATES.disconnected && utils.hasNoOwnKeys(_mongoose.connection.models)) { _mongoose.connections.shift(); } } @@ -605,8 +605,7 @@ Mongoose.prototype.model = function model(name, schema, collection, options) { _mongoose.options.overwriteModels : options.overwriteModels; if (_mongoose.models.hasOwnProperty(name) && options.cache !== false && overwriteModels !== true) { - if (originalSchema && - originalSchema.instanceOfSchema && + if (originalSchema?.instanceOfSchema && originalSchema !== _mongoose.models[name].schema) { throw new _mongoose.Error.OverwriteModelError(name); } diff --git a/lib/plugins/trackTransaction.js b/lib/plugins/trackTransaction.js index 857caac6044..d7018d03cb1 100644 --- a/lib/plugins/trackTransaction.js +++ b/lib/plugins/trackTransaction.js @@ -44,7 +44,7 @@ function _getAtomics(doc, previous) { utils.isMongooseDocumentArray(val) && val.length && val[arrayAtomicsSymbol] != null && - Object.keys(val[arrayAtomicsSymbol]).length !== 0) { + utils.hasNoOwnKeys(val[arrayAtomicsSymbol]) === false) { const existing = previous.get(path) || {}; pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol])); } @@ -55,7 +55,7 @@ function _getAtomics(doc, previous) { const path = dirt.path; const val = dirt.value; - if (val != null && val[arrayAtomicsSymbol] != null && Object.keys(val[arrayAtomicsSymbol]).length !== 0) { + if (val != null && val[arrayAtomicsSymbol] != null && utils.hasNoOwnKeys(val[arrayAtomicsSymbol]) === false) { const existing = previous.get(path) || {}; pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol])); } diff --git a/lib/query.js b/lib/query.js index 57bc291340f..f4eacfdb221 100644 --- a/lib/query.js +++ b/lib/query.js @@ -156,10 +156,7 @@ function Query(conditions, options, model, collection) { // versions of MongoDB this.$useProjection = true; - const collation = this && - this.schema && - this.schema.options && - this.schema.options.collation || null; + const collation = this?.schema?.options?.collation || null; if (collation != null) { this.options.collation = collation; } @@ -169,7 +166,7 @@ function Query(conditions, options, model, collection) { function isEmptyFilter(obj) { if (obj == null) return true; if (typeof obj !== 'object') return true; - if (Object.keys(obj).length === 0) return true; + if (utils.hasNoOwnKeys(obj)) return true; // Check $and, $or, $nor arrays for (const key of ['$and', '$or', '$nor']) { @@ -457,8 +454,8 @@ Query.prototype.clone = function() { * @api public */ -Query.prototype.slice = function() { - if (arguments.length === 0) { +Query.prototype.slice = function(...args) { + if (args.length === 0) { return this; } @@ -467,8 +464,8 @@ Query.prototype.slice = function() { let path; let val; - if (arguments.length === 1) { - const arg = arguments[0]; + if (args.length === 1) { + const arg = args[0]; if (typeof arg === 'object' && !Array.isArray(arg)) { const keys = Object.keys(arg); const numKeys = keys.length; @@ -479,19 +476,19 @@ Query.prototype.slice = function() { } this._ensurePath('slice'); path = this._path; - val = arguments[0]; - } else if (arguments.length === 2) { - if ('number' === typeof arguments[0]) { + val = args[0]; + } else if (args.length === 2) { + if ('number' === typeof args[0]) { this._ensurePath('slice'); path = this._path; - val = [arguments[0], arguments[1]]; + val = [args[0], args[1]]; } else { - path = arguments[0]; - val = arguments[1]; + path = args[0]; + val = args[1]; } - } else if (arguments.length === 3) { - path = arguments[0]; - val = [arguments[1], arguments[2]]; + } else if (args.length === 3) { + path = args[0]; + val = [args[1], args[2]]; } const p = {}; @@ -2006,7 +2003,7 @@ Query.prototype._fieldsForExec = function() { if (this._fields == null) { return null; } - if (Object.keys(this._fields).length === 0) { + if (utils.hasNoOwnKeys(this._fields)) { return null; } return clone(this._fields); @@ -2104,10 +2101,7 @@ Query.prototype._optionsForExec = function(model) { options.session = asyncLocalStorage.session; } - const readPreference = model && - model.schema && - model.schema.options && - model.schema.options.read; + const readPreference = model?.schema?.options?.read; if (!('readPreference' in options) && readPreference) { options.readPreference = readPreference; } @@ -2413,13 +2407,15 @@ Query.prototype._find = async function _find() { const mongooseOptions = this._mongooseOptions; const userProvidedFields = this._userProvidedFields || {}; - applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options); - applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options); + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); // Separate options to pass down to `completeMany()` in case we need to // set a session on the document const completeManyOptions = { - session: this && this.options && this.options.session || null, + session: this?.options?.session || null, lean: mongooseOptions.lean || null }; @@ -2712,8 +2708,10 @@ Query.prototype._findOne = async function _findOne() { throw err; } - applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options); - applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options); + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); const options = this._optionsForExec(); @@ -2808,8 +2806,10 @@ Query.prototype._countDocuments = async function _countDocuments() { throw this.error(); } - applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options); - applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options); + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); const options = this._optionsForExec(); @@ -2974,8 +2974,10 @@ Query.prototype.__distinct = async function __distinct() { throw this.error(); } - applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options); - applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options); + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); const options = this._optionsForExec(); @@ -3492,8 +3494,10 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { throw this.error(); } - applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options); - applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options); + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); if ('strict' in this.options) { this._mongooseOptions.strict = this.options.strict; @@ -3509,7 +3513,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { this._update = setDefaultsOnInsert(this._conditions, this.model.schema, this._update, _opts); - if (!this._update || Object.keys(this._update).length === 0) { + if (!this._update || utils.hasNoOwnKeys(this._update)) { if (options.upsert) { // still need to do the upsert to empty doc const $set = clone(this._update); @@ -3526,7 +3530,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { // In order to make MongoDB 2.6 happy (see // https://jira.mongodb.org/browse/SERVER-12266 and related issues) // if we have an actual update document but $set is empty, junk the $set. - if (this._update.$set && Object.keys(this._update.$set).length === 0) { + if (this._update.$set && utils.hasNoOwnKeys(this._update.$set)) { delete this._update.$set; } } @@ -4015,7 +4019,7 @@ Query.prototype._mergeUpdate = function(update) { this._update = Array.isArray(update) ? [] : {}; } - if (update == null || (typeof update === 'object' && Object.keys(update).length === 0)) { + if (update == null || (typeof update === 'object' && utils.hasNoOwnKeys(update))) { return; } @@ -4067,7 +4071,7 @@ async function _updateThunk(op) { } else { this._update = this._castUpdate(this._update); - if (this._update == null || Object.keys(this._update).length === 0) { + if (this._update == null || utils.hasNoOwnKeys(this._update)) { return { acknowledged: false }; } @@ -4888,8 +4892,7 @@ Query.prototype._castUpdate = function _castUpdate(obj) { * @api public */ -Query.prototype.populate = function() { - const args = Array.from(arguments); +Query.prototype.populate = function(...args) { // Bail when given no truthy arguments if (!args.some(Boolean)) { return this; @@ -4902,7 +4905,7 @@ Query.prototype.populate = function() { if (opts.lean != null) { const lean = opts.lean; for (const populateOptions of res) { - if ((populateOptions && populateOptions.options && populateOptions.options.lean) == null) { + if (populateOptions?.options?.lean == null) { populateOptions.options = populateOptions.options || {}; populateOptions.options.lean = lean; } diff --git a/lib/queryHelpers.js b/lib/queryHelpers.js index 9cb2f546756..4681023f496 100644 --- a/lib/queryHelpers.js +++ b/lib/queryHelpers.js @@ -30,11 +30,11 @@ exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query, // lean options should trickle through all queries if (options.lean != null) { pop - .filter(p => (p && p.options && p.options.lean) == null) + .filter(p => p?.options?.lean == null) .forEach(makeLean(options.lean)); } - const session = query && query.options && query.options.session || null; + const session = query?.options?.session || null; if (session != null) { pop.forEach(path => { if (path.options == null) { @@ -204,10 +204,7 @@ exports.applyPaths = function applyPaths(fields, schema, sanitizeProjection) { } break; case false: - if (schema && - schema.paths['_id'] && - schema.paths['_id'].options && - schema.paths['_id'].options.select === false) { + if (schema?.paths['_id']?.options?.select === false) { fields._id = 0; } diff --git a/lib/schema.js b/lib/schema.js index d62fe80cdb6..03734997077 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -722,9 +722,7 @@ Schema.prototype._defaultToObjectOptions = function(json) { return this._defaultToObjectOptionsMap[path]; } - const baseOptions = this.base && - this.base.options && - this.base.options[path] || {}; + const baseOptions = this.base?.options?.[path] || {}; const schemaOptions = this.options[path] || {}; // merge base default options with Schema's set default options if available. // `clone` is necessary here because `utils.options` directly modifies the second input. diff --git a/lib/schema/array.js b/lib/schema/array.js index 14b60f22ea2..945cc1a90eb 100644 --- a/lib/schema/array.js +++ b/lib/schema/array.js @@ -255,9 +255,7 @@ SchemaArray.prototype.checkRequired = function checkRequired(value, doc) { SchemaArray.prototype.enum = function() { let arr = this; while (true) { - const instance = arr && - arr.embeddedSchemaType && - arr.embeddedSchemaType.instance; + const instance = arr?.embeddedSchemaType?.instance; if (instance === 'Array') { arr = arr.embeddedSchemaType; continue; diff --git a/lib/schema/mixed.js b/lib/schema/mixed.js index 93740640891..c4673d5df29 100644 --- a/lib/schema/mixed.js +++ b/lib/schema/mixed.js @@ -26,7 +26,7 @@ function SchemaMixed(path, options, _schemaOptions, parentSchema) { if (Array.isArray(def) && def.length === 0) { // make sure empty array defaults are handled options.default = Array; - } else if (!options.shared && isObject(def) && Object.keys(def).length === 0) { + } else if (!options.shared && isObject(def) && utils.hasNoOwnKeys(def)) { // prevent odd "shared" objects between documents options.default = function() { return {}; diff --git a/lib/schema/number.js b/lib/schema/number.js index 33d9615fb9f..5e172639380 100644 --- a/lib/schema/number.js +++ b/lib/schema/number.js @@ -221,7 +221,7 @@ SchemaNumber.prototype.min = function(value, message) { }, this); } - if (value !== null && value !== undefined) { + if (value != null) { let msg = message || MongooseError.messages.Number.min; msg = msg.replace(/{MIN}/, value); this.validators.push({ @@ -275,7 +275,7 @@ SchemaNumber.prototype.max = function(value, message) { }, this); } - if (value !== null && value !== undefined) { + if (value != null) { let msg = message || MongooseError.messages.Number.max; msg = msg.replace(/{MAX}/, value); this.validators.push({ diff --git a/lib/schema/string.js b/lib/schema/string.js index fbc88b38c02..7197bc0e103 100644 --- a/lib/schema/string.js +++ b/lib/schema/string.js @@ -416,7 +416,7 @@ SchemaString.prototype.minlength = function(value, message) { }, this); } - if (value !== null && value !== undefined) { + if (value != null) { let msg = message || MongooseError.messages.String.minlength; msg = msg.replace(/{MINLENGTH}/, value); this.validators.push({ @@ -472,7 +472,7 @@ SchemaString.prototype.maxlength = function(value, message) { }, this); } - if (value !== null && value !== undefined) { + if (value != null) { let msg = message || MongooseError.messages.String.maxlength; msg = msg.replace(/{MAXLENGTH}/, value); this.validators.push({ diff --git a/lib/schema/subdocument.js b/lib/schema/subdocument.js index dcc42327e5b..183bc68484f 100644 --- a/lib/schema/subdocument.js +++ b/lib/schema/subdocument.js @@ -41,8 +41,7 @@ function SchemaSubdocument(schema, path, options, parentSchema) { if (schema.options.timeseries) { throw new InvalidSchemaOptionError(path, 'timeseries'); } - const schemaTypeIdOption = SchemaSubdocument.defaultOptions && - SchemaSubdocument.defaultOptions._id; + const schemaTypeIdOption = SchemaSubdocument.defaultOptions?._id; if (schemaTypeIdOption != null) { options = options || {}; options._id = schemaTypeIdOption; @@ -216,7 +215,7 @@ SchemaSubdocument.prototype.cast = function(val, doc, init, priorVal, options) { applyDefaults(subdoc, selected, exclude); } else { options = Object.assign({}, options, { priorDoc: priorVal }); - if (Object.keys(val).length === 0) { + if (utils.hasNoOwnKeys(val)) { return new Constructor({}, selected, doc, options); } diff --git a/lib/schemaType.js b/lib/schemaType.js index 063c5b73047..a30f90cdeca 100644 --- a/lib/schemaType.js +++ b/lib/schemaType.js @@ -1209,7 +1209,7 @@ SchemaType.prototype.getDefault = function(scope, init, options) { ret = this.defaultValue; } - if (ret !== null && ret !== undefined) { + if (ret != null) { if (typeof ret === 'object' && (!this.options || !this.options.shared)) { ret = clone(ret); } diff --git a/lib/types/array/index.js b/lib/types/array/index.js index b21c23bfd12..19f37cfd039 100644 --- a/lib/types/array/index.js +++ b/lib/types/array/index.js @@ -85,7 +85,7 @@ function MongooseArray(values, path, doc, schematype) { if (mongooseArrayMethods.hasOwnProperty(prop)) { return mongooseArrayMethods[prop]; } - if (schematype && schematype.virtuals && schematype.virtuals.hasOwnProperty(prop)) { + if (schematype?.virtuals?.hasOwnProperty(prop)) { return schematype.virtuals[prop].applyGetters(undefined, target); } if (typeof prop === 'string' && numberRE.test(prop) && schematype?.embeddedSchemaType != null) { @@ -99,7 +99,7 @@ function MongooseArray(values, path, doc, schematype) { mongooseArrayMethods.set.call(proxy, prop, value, false); } else if (internals.hasOwnProperty(prop)) { internals[prop] = value; - } else if (schematype && schematype.virtuals && schematype.virtuals.hasOwnProperty(prop)) { + } else if (schematype?.virtuals?.hasOwnProperty(prop)) { schematype.virtuals[prop].applySetters(value, target); } else { __array[prop] = value; diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index 46fe80815b8..9ec1f9d50ee 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -995,7 +995,7 @@ function _minimizePath(obj, parts, i) { } _minimizePath(obj[parts[0]], parts, i + 1); - if (obj[parts[0]] != null && typeof obj[parts[0]] === 'object' && Object.keys(obj[parts[0]]).length === 0) { + if (obj[parts[0]] != null && typeof obj[parts[0]] === 'object' && utils.hasNoOwnKeys(obj[parts[0]])) { delete obj[parts[0]]; } } diff --git a/lib/types/documentArray/index.js b/lib/types/documentArray/index.js index ccc0d230fdb..0fd138757db 100644 --- a/lib/types/documentArray/index.js +++ b/lib/types/documentArray/index.js @@ -79,7 +79,7 @@ function MongooseDocumentArray(values, path, doc, schematype) { if (DocumentArrayMethods.hasOwnProperty(prop)) { return DocumentArrayMethods[prop]; } - if (schematype && schematype.virtuals && schematype.virtuals.hasOwnProperty(prop)) { + if (schematype?.virtuals?.hasOwnProperty(prop)) { return schematype.virtuals[prop].applyGetters(undefined, target); } if (ArrayMethods.hasOwnProperty(prop)) { @@ -93,7 +93,7 @@ function MongooseDocumentArray(values, path, doc, schematype) { DocumentArrayMethods.set.call(proxy, prop, value, false); } else if (internals.hasOwnProperty(prop)) { internals[prop] = value; - } else if (schematype && schematype.virtuals && schematype.virtuals.hasOwnProperty(prop)) { + } else if (schematype?.virtuals?.hasOwnProperty(prop)) { schematype.virtuals[prop].applySetters(value, target); } else { __array[prop] = value; diff --git a/lib/types/documentArray/methods/index.js b/lib/types/documentArray/methods/index.js index b17a31d0638..435725a95c6 100644 --- a/lib/types/documentArray/methods/index.js +++ b/lib/types/documentArray/methods/index.js @@ -65,7 +65,7 @@ const methods = { value instanceof Constructor; if (isInstance || // Hack re: #5001, see #5005 - (value && value.constructor && value.constructor.baseCasterConstructor === Constructor)) { + value?.constructor?.baseCasterConstructor === Constructor) { if (!(value[documentArrayParent] && value.__parentArray)) { // value may have been created using array.create() value[documentArrayParent] = this[arrayParentSymbol]; @@ -133,9 +133,9 @@ const methods = { const schemaType = this[arraySchemaSymbol]; let idSchemaType = null; - if (schemaType && schemaType.schema) { + if (schemaType?.schema) { idSchemaType = schemaType.schema.path('_id'); - } else if (schemaType && schemaType.casterConstructor && schemaType.casterConstructor.schema) { + } else if (schemaType?.casterConstructor?.schema) { idSchemaType = schemaType.casterConstructor.schema.path('_id'); } diff --git a/lib/types/subdocument.js b/lib/types/subdocument.js index 5d39e1c214e..bdc913d3df9 100644 --- a/lib/types/subdocument.js +++ b/lib/types/subdocument.js @@ -422,7 +422,7 @@ Subdocument.prototype.$toObject = function $toObject(options, json) { // If `$toObject()` was called recursively, respect the minimize option, including schematype level minimize. // If minimize is set, then we can minimize out the whole object. - if (Object.keys(ret).length === 0 && options?._calledWithOptions != null) { + if (utils.hasNoOwnKeys(ret) && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; diff --git a/lib/utils.js b/lib/utils.js index 2b896dce28b..f2deaa09fe7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -436,10 +436,26 @@ exports.isNativeObject = function(arg) { * @param {Any} val */ -exports.isEmptyObject = function(val) { - return val != null && - typeof val === 'object' && - Object.keys(val).length === 0; +exports.isEmptyObject = function isEmptyObject(val) { + if (val == null || typeof val !== 'object') { + return false; + } + return exports.hasNoOwnKeys(val); +}; + +/** + * Determines if `obj` has no own keys. Assumes obj is already an object. + * Faster than Object.keys(obj).length === 0. + * @param {Object} obj + */ + +exports.hasNoOwnKeys = function hasNoOwnKeys(obj) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + return false; + } + } + return true; }; /** From 1267b8fe0465562ed6befc160c3478260e732772 Mon Sep 17 00:00:00 2001 From: Hafez Date: Thu, 4 Dec 2025 03:31:32 +0100 Subject: [PATCH 2/4] refactor(utils): rename `hasNoOwnKeys` to `hasOwnKeys` to avoid double negation --- lib/cast.js | 2 +- lib/connection.js | 4 ++-- lib/document.js | 18 +++++++++--------- lib/drivers/node-mongodb-native/connection.js | 6 +++--- lib/helpers/document/applyTimestamps.js | 3 ++- lib/helpers/document/applyVirtuals.js | 3 ++- lib/helpers/document/compile.js | 2 +- .../populate/getModelsMapForPopulate.js | 2 +- lib/helpers/query/castUpdate.js | 6 +++--- lib/helpers/schema/applyWriteConcern.js | 2 +- lib/helpers/update/applyTimestampsToUpdate.js | 2 +- lib/helpers/update/castArrayFilters.js | 2 +- lib/model.js | 4 ++-- lib/mongoose.js | 2 +- lib/plugins/trackTransaction.js | 4 ++-- lib/query.js | 14 +++++++------- lib/schema.js | 8 ++++---- lib/schema/map.js | 2 +- lib/schema/mixed.js | 2 +- lib/schema/subdocument.js | 2 +- lib/types/array/methods/index.js | 4 ++-- lib/types/subdocument.js | 2 +- lib/utils.js | 12 ++++++------ 23 files changed, 55 insertions(+), 53 deletions(-) diff --git a/lib/cast.js b/lib/cast.js index 5655601b038..f91cdb007dc 100644 --- a/lib/cast.js +++ b/lib/cast.js @@ -79,7 +79,7 @@ module.exports = function cast(schema, obj, options, context) { val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context); } - if (utils.hasNoOwnKeys(val[k]) && beforeCastKeysLength !== 0) { + if (utils.hasOwnKeys(val[k]) === false && beforeCastKeysLength !== 0) { val.splice(k, 1); } } diff --git a/lib/connection.js b/lib/connection.js index e4aad8e4f80..d31138e63cb 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -630,7 +630,7 @@ Connection.prototype.createCollections = async function createCollections(option } } - if (!continueOnError && Object.keys(errorsMap).length) { + if (!continueOnError && utils.hasOwnKeys(errorsMap)) { const message = Object.entries(errorsMap).map(([modelName, err]) => `${modelName}: ${err.message}`).join(', '); const createCollectionsError = new CreateCollectionsError(message, errorsMap); throw createCollectionsError; @@ -1778,7 +1778,7 @@ Connection.prototype.syncIndexes = async function syncIndexes(options = {}) { } } - if (!continueOnError && Object.keys(errorsMap).length) { + if (!continueOnError && utils.hasOwnKeys(errorsMap)) { const message = Object.entries(errorsMap).map(([modelName, err]) => `${modelName}: ${err.message}`).join(', '); const syncIndexesError = new SyncIndexesError(message, errorsMap); throw syncIndexesError; diff --git a/lib/document.js b/lib/document.js index f339cc3c9ef..2a5f97b1219 100644 --- a/lib/document.js +++ b/lib/document.js @@ -149,7 +149,7 @@ function Document(obj, fields, options) { // determine if this doc is a result of a query with // excluded fields - if (utils.isPOJO(fields) && Object.keys(fields).length > 0) { + if (utils.isPOJO(fields) && utils.hasOwnKeys(fields)) { exclude = isExclusive(fields); this.$__.selected = fields; this.$__.exclude = exclude; @@ -1701,7 +1701,7 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa if (shouldModify) { if (this.$__.primitiveAtomics && this.$__.primitiveAtomics[path]) { delete this.$__.primitiveAtomics[path]; - if (utils.hasNoOwnKeys(this.$__.primitiveAtomics)) { + if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } @@ -3005,7 +3005,7 @@ Document.prototype.$__validate = async function $__validate(pathsToValidate, opt delete validationError.errors[errPath]; } } - if (utils.hasNoOwnKeys(validationError.errors)) { + if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } @@ -3406,7 +3406,7 @@ Document.prototype.$markValid = function(path) { } delete this.$__.validationError.errors[path]; - if (utils.hasNoOwnKeys(this.$__.validationError.errors)) { + if (utils.hasOwnKeys(this.$__.validationError.errors) === false) { this.$__.validationError = null; } }; @@ -3426,7 +3426,7 @@ function _markValidSubpaths(doc, path) { delete doc.$__.validationError.errors[key]; } } - if (utils.hasNoOwnKeys(doc.$__.validationError.errors)) { + if (utils.hasOwnKeys(doc.$__.validationError.errors) === false) { doc.$__.validationError = null; } } @@ -3501,7 +3501,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) { */ Document.prototype.$isValid = function(path) { - if (this.$__.validationError == null || utils.hasNoOwnKeys(this.$__.validationError.errors)) { + if (this.$__.validationError == null || utils.hasOwnKeys(this.$__.validationError.errors) === false) { return true; } if (path == null) { @@ -4379,7 +4379,7 @@ function omitDeselectedFields(self, json) { selected = {}; queryhelpers.applyPaths(selected, schema); } - if (selected == null || utils.hasNoOwnKeys(selected)) { + if (selected == null || utils.hasOwnKeys(selected) === false) { return json; } @@ -5061,7 +5061,7 @@ Document.prototype.$__delta = function $__delta() { this.$__version(where, delta); } - if (utils.hasNoOwnKeys(delta)) { + if (utils.hasOwnKeys(delta) === false) { return [where, null]; } @@ -5113,7 +5113,7 @@ function checkDivergentArray(doc, path, array) { if (check) { const atomics = array[arrayAtomicsSymbol]; - if (utils.hasNoOwnKeys(atomics) || atomics.$set || atomics.$pop) { + if (utils.hasOwnKeys(atomics) === false || atomics.$set || atomics.$pop) { return path; } } diff --git a/lib/drivers/node-mongodb-native/connection.js b/lib/drivers/node-mongodb-native/connection.js index f00a90224d8..6ff3e03d91b 100644 --- a/lib/drivers/node-mongodb-native/connection.js +++ b/lib/drivers/node-mongodb-native/connection.js @@ -316,15 +316,15 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio const { schemaMap, encryptedFieldsMap } = this._buildEncryptionSchemas(); - if ((Object.keys(schemaMap).length > 0 || Object.keys(encryptedFieldsMap).length) && !options.autoEncryption) { + if ((utils.hasOwnKeys(schemaMap) || utils.hasOwnKeys(encryptedFieldsMap)) && !options.autoEncryption) { throw new Error('Must provide `autoEncryption` when connecting with encrypted schemas.'); } - if (Object.keys(schemaMap).length > 0) { + if (utils.hasOwnKeys(schemaMap)) { options.autoEncryption.schemaMap = schemaMap; } - if (Object.keys(encryptedFieldsMap).length > 0) { + if (utils.hasOwnKeys(encryptedFieldsMap)) { options.autoEncryption.encryptedFieldsMap = encryptedFieldsMap; } diff --git a/lib/helpers/document/applyTimestamps.js b/lib/helpers/document/applyTimestamps.js index 425e144c867..c26146bfa1c 100644 --- a/lib/helpers/document/applyTimestamps.js +++ b/lib/helpers/document/applyTimestamps.js @@ -2,6 +2,7 @@ const handleTimestampOption = require('../schema/handleTimestampOption'); const mpath = require('mpath'); +const utils = require('../../utils'); module.exports = applyTimestamps; @@ -71,7 +72,7 @@ function applyTimestampsToDoc(schema, obj, options) { return; } - if (schema.discriminators && Object.keys(schema.discriminators).length > 0) { + if (schema.discriminators && utils.hasOwnKeys(schema.discriminators)) { for (const discriminatorKey of Object.keys(schema.discriminators)) { const discriminator = schema.discriminators[discriminatorKey]; const key = discriminator.discriminatorMapping.key; diff --git a/lib/helpers/document/applyVirtuals.js b/lib/helpers/document/applyVirtuals.js index 5fbe7ca82ba..09a61f8a2c5 100644 --- a/lib/helpers/document/applyVirtuals.js +++ b/lib/helpers/document/applyVirtuals.js @@ -1,6 +1,7 @@ 'use strict'; const mpath = require('mpath'); +const utils = require('../../utils'); module.exports = applyVirtuals; @@ -101,7 +102,7 @@ function applyVirtualsToDoc(schema, obj, virtuals) { return; } - if (schema.discriminators && Object.keys(schema.discriminators).length > 0) { + if (schema.discriminators && utils.hasOwnKeys(schema.discriminators)) { for (const discriminatorKey of Object.keys(schema.discriminators)) { const discriminator = schema.discriminators[discriminatorKey]; const key = discriminator.discriminatorMapping.key; diff --git a/lib/helpers/document/compile.js b/lib/helpers/document/compile.js index c5d2aa3977f..4b4350f4c2e 100644 --- a/lib/helpers/document/compile.js +++ b/lib/helpers/document/compile.js @@ -46,7 +46,7 @@ function compile(tree, proto, prefix, options) { const limb = tree[key]; const hasSubprops = isPOJO(limb) && - Object.keys(limb).length > 0 && + utils.hasOwnKeys(limb) && (!limb[typeKey] || (typeKey === 'type' && isPOJO(limb.type) && limb.type.type)); const subprops = hasSubprops ? limb : null; diff --git a/lib/helpers/populate/getModelsMapForPopulate.js b/lib/helpers/populate/getModelsMapForPopulate.js index b706bf5a92c..56a4d690d7b 100644 --- a/lib/helpers/populate/getModelsMapForPopulate.js +++ b/lib/helpers/populate/getModelsMapForPopulate.js @@ -724,7 +724,7 @@ function _findRefPathForDiscriminators(doc, modelSchema, data, options, normaliz if (schematype != null && schematype.$isMongooseDocumentArray && schematype.Constructor.discriminators != null && - utils.hasNoOwnKeys(schematype.Constructor.discriminators) === false) { + utils.hasOwnKeys(schematype.Constructor.discriminators)) { const subdocs = utils.getValue(cur, doc); const remnant = options.path.substring(cur.length + 1); const discriminatorKey = schematype.Constructor.schema.options.discriminatorKey; diff --git a/lib/helpers/query/castUpdate.js b/lib/helpers/query/castUpdate.js index 114d19a9e06..75b22b6f2ff 100644 --- a/lib/helpers/query/castUpdate.js +++ b/lib/helpers/query/castUpdate.js @@ -153,9 +153,9 @@ module.exports = function castUpdate(schema, obj, options, context, filter) { } } - if (utils.hasNoOwnKeys(ret) && + if (utils.hasOwnKeys(ret) === false && options.upsert && - utils.hasNoOwnKeys(filter) === false) { + utils.hasOwnKeys(filter)) { // Trick the driver into allowing empty upserts to work around // https://github.com/mongodb/node-mongodb-native/pull/2490 // Shallow clone to avoid passing defaults in re: gh-13962 @@ -376,7 +376,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, prefix) { // we should be able to set a schema-less field // to an empty object literal hasKeys |= walkUpdatePath(schema, val, op, options, context, filter, prefix + key) || - (utils.isObject(val) && utils.hasNoOwnKeys(val)); + (utils.isObject(val) && utils.hasOwnKeys(val) === false); } } else { const isModifier = (key === '$each' || key === '$or' || key === '$and' || key === '$in'); diff --git a/lib/helpers/schema/applyWriteConcern.js b/lib/helpers/schema/applyWriteConcern.js index d54d4803b70..2b7413713a3 100644 --- a/lib/helpers/schema/applyWriteConcern.js +++ b/lib/helpers/schema/applyWriteConcern.js @@ -13,7 +13,7 @@ module.exports = function applyWriteConcern(schema, options) { return; } const writeConcern = schema.options.writeConcern ?? {}; - if (utils.hasNoOwnKeys(writeConcern) === false) { + if (utils.hasOwnKeys(writeConcern)) { options.writeConcern = {}; if (!('w' in options) && writeConcern.w != null) { options.writeConcern.w = writeConcern.w; diff --git a/lib/helpers/update/applyTimestampsToUpdate.js b/lib/helpers/update/applyTimestampsToUpdate.js index 23a06d002af..95f95c06641 100644 --- a/lib/helpers/update/applyTimestampsToUpdate.js +++ b/lib/helpers/update/applyTimestampsToUpdate.js @@ -111,7 +111,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio } } - if (utils.hasNoOwnKeys(updates.$set)) { + if (utils.hasOwnKeys(updates.$set) === false) { delete updates.$set; } return updates; diff --git a/lib/helpers/update/castArrayFilters.js b/lib/helpers/update/castArrayFilters.js index 1a4e3233b03..405c4c06704 100644 --- a/lib/helpers/update/castArrayFilters.js +++ b/lib/helpers/update/castArrayFilters.js @@ -75,7 +75,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt if (updatedPathsByFilter[key] === null) { continue; } - if (utils.hasNoOwnKeys(updatedPathsByFilter)) { + if (utils.hasOwnKeys(updatedPathsByFilter) === false) { continue; } const dot = key.indexOf('.'); diff --git a/lib/model.js b/lib/model.js index 2a36c255639..2f7935cd173 100644 --- a/lib/model.js +++ b/lib/model.js @@ -434,7 +434,7 @@ Model.prototype.$__save = async function $__save(options) { continue; } minimize(updateOp[key]); - if (utils.hasNoOwnKeys(updateOp[key])) { + if (utils.hasOwnKeys(updateOp[key]) === false) { delete updateOp[key]; update.$unset = update.$unset || {}; update.$unset[key] = 1; @@ -4968,7 +4968,7 @@ Model._applyQueryMiddleware = function _applyQueryMiddleware() { return !!contexts.query; } if (hook.name === 'deleteOne' || hook.name === 'updateOne') { - return !!contexts.query || utils.hasNoOwnKeys(contexts); + return !!contexts.query || utils.hasOwnKeys(contexts) === false; } if (hook.query != null || hook.document != null) { return !!hook.query; diff --git a/lib/mongoose.js b/lib/mongoose.js index 9febdf4fc8a..c20ae51ffb4 100644 --- a/lib/mongoose.js +++ b/lib/mongoose.js @@ -309,7 +309,7 @@ Mongoose.prototype.set = function getsetOptions(key, value) { if (optionValue && !_mongoose.connection) { _createDefaultConnection(_mongoose); } else if (optionValue === false && _mongoose.connection && _mongoose.connection[defaultConnectionSymbol]) { - if (_mongoose.connection.readyState === STATES.disconnected && utils.hasNoOwnKeys(_mongoose.connection.models)) { + if (_mongoose.connection.readyState === STATES.disconnected && utils.hasOwnKeys(_mongoose.connection.models) === false) { _mongoose.connections.shift(); } } diff --git a/lib/plugins/trackTransaction.js b/lib/plugins/trackTransaction.js index d7018d03cb1..1a45b572b1a 100644 --- a/lib/plugins/trackTransaction.js +++ b/lib/plugins/trackTransaction.js @@ -44,7 +44,7 @@ function _getAtomics(doc, previous) { utils.isMongooseDocumentArray(val) && val.length && val[arrayAtomicsSymbol] != null && - utils.hasNoOwnKeys(val[arrayAtomicsSymbol]) === false) { + utils.hasOwnKeys(val[arrayAtomicsSymbol])) { const existing = previous.get(path) || {}; pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol])); } @@ -55,7 +55,7 @@ function _getAtomics(doc, previous) { const path = dirt.path; const val = dirt.value; - if (val != null && val[arrayAtomicsSymbol] != null && utils.hasNoOwnKeys(val[arrayAtomicsSymbol]) === false) { + if (val != null && val[arrayAtomicsSymbol] != null && utils.hasOwnKeys(val[arrayAtomicsSymbol])) { const existing = previous.get(path) || {}; pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol])); } diff --git a/lib/query.js b/lib/query.js index f4eacfdb221..9910a8c16cc 100644 --- a/lib/query.js +++ b/lib/query.js @@ -166,7 +166,7 @@ function Query(conditions, options, model, collection) { function isEmptyFilter(obj) { if (obj == null) return true; if (typeof obj !== 'object') return true; - if (utils.hasNoOwnKeys(obj)) return true; + if (utils.hasOwnKeys(obj) === false) return true; // Check $and, $or, $nor arrays for (const key of ['$and', '$or', '$nor']) { @@ -2003,7 +2003,7 @@ Query.prototype._fieldsForExec = function() { if (this._fields == null) { return null; } - if (utils.hasNoOwnKeys(this._fields)) { + if (utils.hasOwnKeys(this._fields) === false) { return null; } return clone(this._fields); @@ -2836,7 +2836,7 @@ Query.prototype._applyTranslateAliases = function _applyTranslateAliases() { return; } - if (this.model?.schema?.aliases && Object.keys(this.model.schema.aliases).length > 0) { + if (this.model?.schema?.aliases && utils.hasOwnKeys(this.model.schema.aliases)) { this.model.translateAliases(this._conditions, true); this.model.translateAliases(this._fields, true); this.model.translateAliases(this._update, true); @@ -3513,7 +3513,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { this._update = setDefaultsOnInsert(this._conditions, this.model.schema, this._update, _opts); - if (!this._update || utils.hasNoOwnKeys(this._update)) { + if (!this._update || utils.hasOwnKeys(this._update) === false) { if (options.upsert) { // still need to do the upsert to empty doc const $set = clone(this._update); @@ -3530,7 +3530,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { // In order to make MongoDB 2.6 happy (see // https://jira.mongodb.org/browse/SERVER-12266 and related issues) // if we have an actual update document but $set is empty, junk the $set. - if (this._update.$set && utils.hasNoOwnKeys(this._update.$set)) { + if (this._update.$set && utils.hasOwnKeys(this._update.$set) === false) { delete this._update.$set; } } @@ -4019,7 +4019,7 @@ Query.prototype._mergeUpdate = function(update) { this._update = Array.isArray(update) ? [] : {}; } - if (update == null || (typeof update === 'object' && utils.hasNoOwnKeys(update))) { + if (update == null || (typeof update === 'object' && utils.hasOwnKeys(update) === false)) { return; } @@ -4071,7 +4071,7 @@ async function _updateThunk(op) { } else { this._update = this._castUpdate(this._update); - if (this._update == null || utils.hasNoOwnKeys(this._update)) { + if (this._update == null || utils.hasOwnKeys(this._update) === false) { return { acknowledged: false }; } diff --git a/lib/schema.js b/lib/schema.js index 03734997077..8a4ddddf64c 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -845,7 +845,7 @@ Schema.prototype.add = function add(obj, prefix) { schemaType.discriminator(key, val[0].discriminators[key]); } } - } else if (Object.keys(val).length < 1) { + } else if (utils.hasOwnKeys(val) === false) { // Special-case: {} always interpreted as Mixed path so leaf at this node if (prefix) { this.nested[prefix.substring(0, prefix.length - 1)] = true; @@ -860,7 +860,7 @@ Schema.prototype.add = function add(obj, prefix) { } else { // There IS a bona-fide type key that may also be a POJO const _typeDef = val[typeKey]; - if (isPOJO(_typeDef) && Object.keys(_typeDef).length > 0) { + if (isPOJO(_typeDef) && utils.hasOwnKeys(_typeDef)) { // If a POJO is the value of a type key, make it a subdocument if (prefix) { this.nested[prefix.substring(0, prefix.length - 1)] = true; @@ -963,7 +963,7 @@ Schema.prototype._removeEncryptedField = function _removeEncryptedField(path) { * @returns {boolean} */ Schema.prototype._hasEncryptedFields = function _hasEncryptedFields() { - return Object.keys(this.encryptedFields).length > 0; + return utils.hasOwnKeys(this.encryptedFields); }; /** @@ -1672,7 +1672,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) { if (typeof cast === 'string') { cast = MongooseTypes[cast.charAt(0).toUpperCase() + cast.substring(1)]; } else if (utils.isPOJO(castFromTypeKey)) { - if (Object.keys(castFromTypeKey).length) { + if (utils.hasOwnKeys(castFromTypeKey)) { // The `minimize` and `typeKey` options propagate to child schemas // declared inline, like `{ arr: [{ val: { $type: String } }] }`. // See gh-3560 diff --git a/lib/schema/map.js b/lib/schema/map.js index 2ac2a3371a8..e7bf1363f87 100644 --- a/lib/schema/map.js +++ b/lib/schema/map.js @@ -172,7 +172,7 @@ SchemaMap.prototype._createNestedSchemaType = function _createNestedSchemaType(s let _mapType = { type: {} }; if (utils.hasUserDefinedProperty(obj, 'of')) { const isInlineSchema = utils.isPOJO(obj.of) && - Object.keys(obj.of).length > 0 && + utils.hasOwnKeys(obj.of) && !utils.hasUserDefinedProperty(obj.of, schema.options.typeKey); if (isInlineSchema) { _mapType = { [schema.options.typeKey]: new Schema(obj.of) }; diff --git a/lib/schema/mixed.js b/lib/schema/mixed.js index c4673d5df29..c95a373a9e9 100644 --- a/lib/schema/mixed.js +++ b/lib/schema/mixed.js @@ -26,7 +26,7 @@ function SchemaMixed(path, options, _schemaOptions, parentSchema) { if (Array.isArray(def) && def.length === 0) { // make sure empty array defaults are handled options.default = Array; - } else if (!options.shared && isObject(def) && utils.hasNoOwnKeys(def)) { + } else if (!options.shared && isObject(def) && utils.hasOwnKeys(def) === false) { // prevent odd "shared" objects between documents options.default = function() { return {}; diff --git a/lib/schema/subdocument.js b/lib/schema/subdocument.js index 183bc68484f..64975f724d6 100644 --- a/lib/schema/subdocument.js +++ b/lib/schema/subdocument.js @@ -215,7 +215,7 @@ SchemaSubdocument.prototype.cast = function(val, doc, init, priorVal, options) { applyDefaults(subdoc, selected, exclude); } else { options = Object.assign({}, options, { priorDoc: priorVal }); - if (utils.hasNoOwnKeys(val)) { + if (utils.hasOwnKeys(val) === false) { return new Constructor({}, selected, doc, options); } diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index 9ec1f9d50ee..a14cf16fcf3 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -338,7 +338,7 @@ const methods = { // check for impossible $atomic combos (Mongo denies more than one // $atomic op on a single path - if (atomics.$set || Object.keys(atomics).length && !(op in atomics)) { + if (atomics.$set || utils.hasOwnKeys(atomics) && !(op in atomics)) { // a different op was previously registered. // save the entire thing. this[arrayAtomicsSymbol] = { $set: this }; @@ -995,7 +995,7 @@ function _minimizePath(obj, parts, i) { } _minimizePath(obj[parts[0]], parts, i + 1); - if (obj[parts[0]] != null && typeof obj[parts[0]] === 'object' && utils.hasNoOwnKeys(obj[parts[0]])) { + if (obj[parts[0]] != null && typeof obj[parts[0]] === 'object' && utils.hasOwnKeys(obj[parts[0]]) === false) { delete obj[parts[0]]; } } diff --git a/lib/types/subdocument.js b/lib/types/subdocument.js index bdc913d3df9..89d06fcaaf3 100644 --- a/lib/types/subdocument.js +++ b/lib/types/subdocument.js @@ -422,7 +422,7 @@ Subdocument.prototype.$toObject = function $toObject(options, json) { // If `$toObject()` was called recursively, respect the minimize option, including schematype level minimize. // If minimize is set, then we can minimize out the whole object. - if (utils.hasNoOwnKeys(ret) && options?._calledWithOptions != null) { + if (utils.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; diff --git a/lib/utils.js b/lib/utils.js index f2deaa09fe7..217c63906bc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -440,22 +440,22 @@ exports.isEmptyObject = function isEmptyObject(val) { if (val == null || typeof val !== 'object') { return false; } - return exports.hasNoOwnKeys(val); + return exports.hasOwnKeys(val) === false; }; /** - * Determines if `obj` has no own keys. Assumes obj is already an object. - * Faster than Object.keys(obj).length === 0. + * Determines if `obj` has any own keys. Assumes obj is already an object. + * Faster than Object.keys(obj).length > 0. * @param {Object} obj */ -exports.hasNoOwnKeys = function hasNoOwnKeys(obj) { +exports.hasOwnKeys = function hasOwnKeys(obj) { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { - return false; + return true; } } - return true; + return false; }; /** From 920a99dfef0ae5a9ffc08867c09c0c3a7dc27c71 Mon Sep 17 00:00:00 2001 From: Hafez Date: Thu, 4 Dec 2025 04:52:38 +0100 Subject: [PATCH 3/4] refactor: use Object.hasOwn instead of Object.prototype.hasOwnProperty(...) --- lib/aggregate.js | 2 +- lib/cast.js | 2 +- lib/connection.js | 18 ++++---- lib/document.js | 39 +++++++++-------- lib/drivers/node-mongodb-native/connection.js | 2 +- lib/helpers/common.js | 2 +- lib/helpers/indexes/applySchemaCollation.js | 2 +- lib/helpers/indexes/isDefaultIdIndex.js | 2 +- lib/helpers/model/applyMethods.js | 2 +- lib/helpers/model/castBulkWrite.js | 2 +- .../populate/getModelsMapForPopulate.js | 6 +-- lib/helpers/populate/modelNamesFromRefPath.js | 2 +- .../populate/removeDeselectedForeignField.js | 2 +- lib/helpers/projection/applyProjection.js | 2 +- .../query/getEmbeddedDiscriminatorPath.js | 2 +- lib/helpers/setDefaultsOnInsert.js | 4 +- lib/helpers/timestamps/setupTimestamps.js | 2 +- lib/helpers/update/applyTimestampsToUpdate.js | 2 +- lib/model.js | 14 +++---- lib/mongoose.js | 4 +- lib/query.js | 6 +-- lib/schema.js | 42 +++++++++---------- lib/schema/array.js | 2 +- lib/schemaType.js | 16 +++---- lib/types/array/index.js | 4 +- lib/types/documentArray/index.js | 4 +- lib/types/objectid.js | 2 +- lib/virtualType.js | 2 +- 28 files changed, 98 insertions(+), 93 deletions(-) diff --git a/lib/aggregate.js b/lib/aggregate.js index 560c9e228c8..2b72f6ffb61 100644 --- a/lib/aggregate.js +++ b/lib/aggregate.js @@ -103,7 +103,7 @@ Aggregate.prototype._optionsForExec = function() { const options = this.options || {}; const asyncLocalStorage = this.model()?.db?.base.transactionAsyncLocalStorage?.getStore(); - if (!options.hasOwnProperty('session') && asyncLocalStorage?.session != null) { + if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) { options.session = asyncLocalStorage.session; } diff --git a/lib/cast.js b/lib/cast.js index f91cdb007dc..070234d4d2b 100644 --- a/lib/cast.js +++ b/lib/cast.js @@ -107,7 +107,7 @@ module.exports = function cast(schema, obj, options, context) { val = cast(schema, val, options, context); } else if (path === '$text') { val = castTextSearch(val, path); - } else if (path === '$comment' && !schema.paths.hasOwnProperty('$comment')) { + } else if (path === '$comment' && !Object.hasOwn(schema.paths, '$comment')) { val = castString(val, path); obj[path] = val; } else { diff --git a/lib/connection.js b/lib/connection.js index d31138e63cb..a8b6e2da0a0 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -164,7 +164,7 @@ Object.defineProperty(Connection.prototype, 'readyState', { */ Connection.prototype.get = function getOption(key) { - if (this.config.hasOwnProperty(key)) { + if (Object.hasOwn(this.config, key)) { return this.config[key]; } @@ -192,7 +192,7 @@ Connection.prototype.get = function getOption(key) { */ Connection.prototype.set = function setOption(key, val) { - if (this.config.hasOwnProperty(key)) { + if (Object.hasOwn(this.config, key)) { this.config[key] = val; return val; } @@ -459,7 +459,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) { const ordered = options.ordered == null ? true : options.ordered; const asyncLocalStorage = this.base.transactionAsyncLocalStorage?.getStore(); - if ((!options || !options.hasOwnProperty('session')) && asyncLocalStorage?.session != null) { + if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) { options = { ...options, session: asyncLocalStorage.session }; } @@ -477,7 +477,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) { if (op.name == null) { throw new MongooseError('Must specify operation name in Connection.prototype.bulkWrite()'); } - if (!castBulkWrite.cast.hasOwnProperty(op.name)) { + if (!Object.hasOwn(castBulkWrite.cast, op.name)) { throw new MongooseError(`Unrecognized bulkWrite() operation name ${op.name}`); } @@ -513,7 +513,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) { results[i] = error; continue; } - if (!castBulkWrite.cast.hasOwnProperty(op.name)) { + if (!Object.hasOwn(castBulkWrite.cast, op.name)) { const error = new MongooseError(`Unrecognized bulkWrite() operation name ${op.name}`); validationErrors.push({ index: i, error: error }); results[i] = error; @@ -772,10 +772,10 @@ async function _wrapUserTransaction(fn, session, mongoose) { function _resetSessionDocuments(session) { for (const doc of session[sessionNewDocuments].keys()) { const state = session[sessionNewDocuments].get(doc); - if (state.hasOwnProperty('isNew')) { + if (Object.hasOwn(state, 'isNew')) { doc.$isNew = state.isNew; } - if (state.hasOwnProperty('versionKey')) { + if (Object.hasOwn(state, 'versionKey')) { doc.set(doc.schema.options.versionKey, state.versionKey); } @@ -1013,7 +1013,7 @@ Connection.prototype.onOpen = function() { // avoid having the collection subscribe to our event emitter // to prevent 0.3 warning for (const i in this.collections) { - if (utils.object.hasOwnProperty(this.collections, i)) { + if (Object.hasOwn(this.collections, i)) { this.collections[i].onOpen(); } } @@ -1327,7 +1327,7 @@ Connection.prototype.onClose = function onClose(force) { // avoid having the collection subscribe to our event emitter // to prevent 0.3 warning for (const i in this.collections) { - if (utils.object.hasOwnProperty(this.collections, i)) { + if (Object.hasOwn(this.collections, i)) { this.collections[i].onClose(force); } } diff --git a/lib/document.js b/lib/document.js index 2a5f97b1219..075936dc6c3 100644 --- a/lib/document.js +++ b/lib/document.js @@ -784,7 +784,7 @@ function init(self, obj, doc, opts, prefix) { } } else { // Retain order when overwriting defaults - if (doc.hasOwnProperty(i) && value !== void 0 && !opts.hydratedPopulatedDocs) { + if (Object.hasOwn(doc, i) && value !== void 0 && !opts.hydratedPopulatedDocs) { delete doc[i]; } if (value === null) { @@ -1171,7 +1171,7 @@ Document.prototype.$set = function $set(path, val, type, options) { const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && - (this._doc.hasOwnProperty(key)) && + (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); @@ -1221,8 +1221,8 @@ Document.prototype.$set = function $set(path, val, type, options) { return this; } const wasModified = this.$isModified(path); - const hasInitialVal = this.$__.savedState != null && this.$__.savedState.hasOwnProperty(path); - if (this.$__.savedState != null && !this.$isNew && !this.$__.savedState.hasOwnProperty(path)) { + const hasInitialVal = this.$__.savedState != null && Object.hasOwn(this.$__.savedState, path); + if (this.$__.savedState != null && !this.$isNew && !Object.hasOwn(this.$__.savedState, path)) { const initialVal = this.$__getValue(path); this.$__.savedState[path] = initialVal; @@ -1527,7 +1527,7 @@ Document.prototype.$set = function $set(path, val, type, options) { this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && - savedState.hasOwnProperty(savedStatePath) && + Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); @@ -2009,7 +2009,7 @@ Document.prototype.$get = Document.prototype.get; Document.prototype.$__path = function(path) { const adhocs = this.$__.adhocPaths; - const adhocType = adhocs && adhocs.hasOwnProperty(path) ? adhocs[path] : null; + const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; @@ -2053,7 +2053,7 @@ Document.prototype.$__saveInitialState = function $__saveInitialState(path) { if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); - if (!savedState.hasOwnProperty(topLevelPath)) { + if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } @@ -2364,7 +2364,7 @@ Document.prototype.$isDefault = function(path) { } if (typeof path === 'string' && path.indexOf(' ') === -1) { - return this.$__.activePaths.getStatePaths('default').hasOwnProperty(path); + return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; @@ -2372,7 +2372,7 @@ Document.prototype.$isDefault = function(path) { paths = paths.split(' '); } - return paths.some(path => this.$__.activePaths.getStatePaths('default').hasOwnProperty(path)); + return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }; /** @@ -2426,7 +2426,7 @@ Document.prototype.isDirectModified = function(path) { } if (typeof path === 'string' && path.indexOf(' ') === -1) { - const res = this.$__.activePaths.getStatePaths('modify').hasOwnProperty(path); + const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } @@ -2465,7 +2465,7 @@ Document.prototype.isInit = function(path) { } if (typeof path === 'string' && path.indexOf(' ') === -1) { - return this.$__.activePaths.getStatePaths('init').hasOwnProperty(path); + return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; @@ -2473,7 +2473,7 @@ Document.prototype.isInit = function(path) { paths = paths.split(' '); } - return paths.some(path => this.$__.activePaths.getStatePaths('init').hasOwnProperty(path)); + return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }; /** @@ -2611,7 +2611,7 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) { return true; } - if (this.$__.selected.hasOwnProperty(path)) { + if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } @@ -2783,7 +2783,7 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) && // Avoid using isDirectModified() here because that does additional checks on whether the parent path // is direct modified, which can cause performance issues re: gh-14897 - !subdocParent.$__.activePaths.getStatePaths('modify').hasOwnProperty(fullPathToSubdoc) && + !Object.hasOwn(subdocParent.$__.activePaths.getStatePaths('modify'), fullPathToSubdoc) && !subdocParent.$isDefault(fullPathToSubdoc)) { paths.add(fullPathToSubdoc); @@ -2854,7 +2854,12 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate // Single nested paths (paths embedded under single nested subdocs) will // be validated on their own when we call `validate()` on the subdoc itself. // Re: gh-8468 - Object.keys(flat).filter(path => !doc.$__schema.singleNestedPaths.hasOwnProperty(path)).forEach(addToPaths); + const singleNestedPaths = doc.$__schema.singleNestedPaths; + for (const path of Object.keys(flat)) { + if (!Object.hasOwn(singleNestedPaths, path)) { + addToPaths(path); + } + } } } @@ -4192,7 +4197,7 @@ function applyVirtuals(self, json, options, toObjectOptions) { } // Allow skipping aliases with `toObject({ virtuals: true, aliases: false })` - if (!aliases && schema.aliases.hasOwnProperty(path)) { + if (!aliases && Object.hasOwn(schema.aliases, path)) { continue; } @@ -5105,7 +5110,7 @@ function checkDivergentArray(doc, path, array) { // would be similarly destructive as we never received all // elements of the array and potentially would overwrite data. const check = pop.options.match || - pop.options.options && utils.object.hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted + pop.options.options && Object.hasOwn(pop.options.options, 'limit') || // 0 is not permitted pop.options.options && pop.options.options.skip || // 0 is permitted pop.options.select && // deselected _id? (pop.options.select._id === 0 || diff --git a/lib/drivers/node-mongodb-native/connection.js b/lib/drivers/node-mongodb-native/connection.js index 6ff3e03d91b..098dffc3c5d 100644 --- a/lib/drivers/node-mongodb-native/connection.js +++ b/lib/drivers/node-mongodb-native/connection.js @@ -495,7 +495,7 @@ function _setClient(conn, client, options, dbName) { conn.onOpen(); for (const i in conn.collections) { - if (utils.object.hasOwnProperty(conn.collections, i)) { + if (Object.hasOwn(conn.collections, i)) { conn.collections[i].onOpen(); } } diff --git a/lib/helpers/common.js b/lib/helpers/common.js index a9c45d50470..391abf8973e 100644 --- a/lib/helpers/common.js +++ b/lib/helpers/common.js @@ -55,7 +55,7 @@ function flatten(update, path, options, schema) { if (isNested) { const paths = Object.keys(schema.paths); for (const p of paths) { - if (p.startsWith(path + key + '.') && !result.hasOwnProperty(p)) { + if (p.startsWith(path + key + '.') && !Object.hasOwn(result, p)) { result[p] = void 0; } } diff --git a/lib/helpers/indexes/applySchemaCollation.js b/lib/helpers/indexes/applySchemaCollation.js index 93a97a48bda..464210e10cf 100644 --- a/lib/helpers/indexes/applySchemaCollation.js +++ b/lib/helpers/indexes/applySchemaCollation.js @@ -7,7 +7,7 @@ module.exports = function applySchemaCollation(indexKeys, indexOptions, schemaOp return; } - if (schemaOptions.hasOwnProperty('collation') && !indexOptions.hasOwnProperty('collation')) { + if (Object.hasOwn(schemaOptions, 'collation') && !Object.hasOwn(indexOptions, 'collation')) { indexOptions.collation = schemaOptions.collation; } }; diff --git a/lib/helpers/indexes/isDefaultIdIndex.js b/lib/helpers/indexes/isDefaultIdIndex.js index 56d74346c6b..8123dfc7d3f 100644 --- a/lib/helpers/indexes/isDefaultIdIndex.js +++ b/lib/helpers/indexes/isDefaultIdIndex.js @@ -14,5 +14,5 @@ module.exports = function isDefaultIdIndex(index) { } const key = get(index, 'key', {}); - return Object.keys(key).length === 1 && key.hasOwnProperty('_id'); + return Object.keys(key).length === 1 && Object.hasOwn(key, '_id'); }; diff --git a/lib/helpers/model/applyMethods.js b/lib/helpers/model/applyMethods.js index a75beceb218..748315aed57 100644 --- a/lib/helpers/model/applyMethods.js +++ b/lib/helpers/model/applyMethods.js @@ -28,7 +28,7 @@ module.exports = function applyMethods(model, schema) { } for (const method of Object.keys(schema.methods)) { const fn = schema.methods[method]; - if (schema.tree.hasOwnProperty(method)) { + if (Object.hasOwn(schema.tree, method)) { throw new Error('You have a method and a property in your schema both ' + 'named "' + method + '"'); } diff --git a/lib/helpers/model/castBulkWrite.js b/lib/helpers/model/castBulkWrite.js index 3f61c4e6ad3..9a5acc28288 100644 --- a/lib/helpers/model/castBulkWrite.js +++ b/lib/helpers/model/castBulkWrite.js @@ -295,7 +295,7 @@ function _addDiscriminatorToObject(schema, obj) { function decideModelByObject(model, object) { const discriminatorKey = model.schema.options.discriminatorKey; - if (object != null && object.hasOwnProperty(discriminatorKey)) { + if (object != null && Object.hasOwn(object, discriminatorKey)) { model = getDiscriminatorByValue(model.discriminators, object[discriminatorKey]) || model; } return model; diff --git a/lib/helpers/populate/getModelsMapForPopulate.js b/lib/helpers/populate/getModelsMapForPopulate.js index 56a4d690d7b..9056f419937 100644 --- a/lib/helpers/populate/getModelsMapForPopulate.js +++ b/lib/helpers/populate/getModelsMapForPopulate.js @@ -389,13 +389,13 @@ function _virtualPopulate(model, docs, options, _virtualRes) { } data.count = virtual.options.count; - if (virtual.options.skip != null && !options.hasOwnProperty('skip')) { + if (virtual.options.skip != null && !Object.hasOwn(options, 'skip')) { options.skip = virtual.options.skip; } - if (virtual.options.limit != null && !options.hasOwnProperty('limit')) { + if (virtual.options.limit != null && !Object.hasOwn(options, 'limit')) { options.limit = virtual.options.limit; } - if (virtual.options.perDocumentLimit != null && !options.hasOwnProperty('perDocumentLimit')) { + if (virtual.options.perDocumentLimit != null && !Object.hasOwn(options, 'perDocumentLimit')) { options.perDocumentLimit = virtual.options.perDocumentLimit; } let foreignField = virtual.options.foreignField; diff --git a/lib/helpers/populate/modelNamesFromRefPath.js b/lib/helpers/populate/modelNamesFromRefPath.js index a5b02859346..875010dccb1 100644 --- a/lib/helpers/populate/modelNamesFromRefPath.js +++ b/lib/helpers/populate/modelNamesFromRefPath.js @@ -56,7 +56,7 @@ module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, mod const refValue = mpath.get(refPath, doc, lookupLocalFields); let modelNames; - if (modelSchema != null && modelSchema.virtuals.hasOwnProperty(refPath)) { + if (modelSchema != null && Object.hasOwn(modelSchema.virtuals, refPath)) { modelNames = [modelSchema.virtuals[refPath].applyGetters(void 0, doc)]; } else { modelNames = Array.isArray(refValue) ? refValue : [refValue]; diff --git a/lib/helpers/populate/removeDeselectedForeignField.js b/lib/helpers/populate/removeDeselectedForeignField.js index a86e6e3e9f1..069adec7e81 100644 --- a/lib/helpers/populate/removeDeselectedForeignField.js +++ b/lib/helpers/populate/removeDeselectedForeignField.js @@ -16,7 +16,7 @@ module.exports = function removeDeselectedForeignField(foreignFields, options, d return; } for (const foreignField of foreignFields) { - if (!projection.hasOwnProperty('-' + foreignField)) { + if (!Object.hasOwn(projection, '-' + foreignField)) { continue; } diff --git a/lib/helpers/projection/applyProjection.js b/lib/helpers/projection/applyProjection.js index 7a35b128b24..6ac27c73629 100644 --- a/lib/helpers/projection/applyProjection.js +++ b/lib/helpers/projection/applyProjection.js @@ -68,7 +68,7 @@ function applyInclusiveProjection(doc, projection, hasIncludedChildren, projecti for (const key of Object.keys(ret)) { const fullPath = prefix ? prefix + '.' + key : key; - if (projection.hasOwnProperty(fullPath) || projectionLimb.hasOwnProperty(key)) { + if (Object.hasOwn(projection, fullPath) || Object.hasOwn(projectionLimb, key)) { if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) { ret[key] = applyInclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath); } diff --git a/lib/helpers/query/getEmbeddedDiscriminatorPath.js b/lib/helpers/query/getEmbeddedDiscriminatorPath.js index c8b9be7ffeb..2247ea3b445 100644 --- a/lib/helpers/query/getEmbeddedDiscriminatorPath.js +++ b/lib/helpers/query/getEmbeddedDiscriminatorPath.js @@ -71,7 +71,7 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p const schemaKey = updatedPathsByFilter[filterKey] + '.' + key; const arrayFilterKey = filterKey + '.' + key; if (schemaKey === discriminatorFilterPath) { - const filter = arrayFilters.find(filter => filter.hasOwnProperty(arrayFilterKey)); + const filter = arrayFilters.find(filter => Object.hasOwn(filter, arrayFilterKey)); if (filter != null) { discriminatorKey = filter[arrayFilterKey]; } diff --git a/lib/helpers/setDefaultsOnInsert.js b/lib/helpers/setDefaultsOnInsert.js index 6c963bc9b69..643ba968f99 100644 --- a/lib/helpers/setDefaultsOnInsert.js +++ b/lib/helpers/setDefaultsOnInsert.js @@ -136,14 +136,14 @@ function pathExistsInUpdate(update, targetPath, pathPieces) { } // Check exact match - if (update.hasOwnProperty(targetPath)) { + if (Object.hasOwn(update, targetPath)) { return true; } // Check if any parent path exists let cur = pathPieces[0]; for (let i = 1; i < pathPieces.length; ++i) { - if (update.hasOwnProperty(cur)) { + if (Object.hasOwn(update, cur)) { return true; } cur += '.' + pathPieces[i]; diff --git a/lib/helpers/timestamps/setupTimestamps.js b/lib/helpers/timestamps/setupTimestamps.js index cdeca8a2296..b7327ffac5f 100644 --- a/lib/helpers/timestamps/setupTimestamps.js +++ b/lib/helpers/timestamps/setupTimestamps.js @@ -23,7 +23,7 @@ module.exports = function setupTimestamps(schema, timestamps) { } const createdAt = handleTimestampOption(timestamps, 'createdAt'); const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); - const currentTime = timestamps != null && timestamps.hasOwnProperty('currentTime') ? + const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ? timestamps.currentTime : null; const schemaAdditions = {}; diff --git a/lib/helpers/update/applyTimestampsToUpdate.js b/lib/helpers/update/applyTimestampsToUpdate.js index 95f95c06641..7be02feb862 100644 --- a/lib/helpers/update/applyTimestampsToUpdate.js +++ b/lib/helpers/update/applyTimestampsToUpdate.js @@ -75,7 +75,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio updates.$set[updatedAt] = now; } - if (updates.hasOwnProperty(updatedAt)) { + if (Object.hasOwn(updates, updatedAt)) { delete updates[updatedAt]; } } diff --git a/lib/model.js b/lib/model.js index 2f7935cd173..ab21137bcf0 100644 --- a/lib/model.js +++ b/lib/model.js @@ -352,7 +352,7 @@ function _createSaveOptions(doc, options) { const asyncLocalStorage = doc[modelDbSymbol].base.transactionAsyncLocalStorage?.getStore(); if (session != null) { saveOptions.session = session; - } else if (!options.hasOwnProperty('session') && asyncLocalStorage?.session != null) { + } else if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) { // Only set session from asyncLocalStorage if `session` option wasn't originally passed in options saveOptions.session = asyncLocalStorage.session; } @@ -586,7 +586,7 @@ Model.prototype.save = async function save(options) { } options = new SaveOptions(options); - if (options.hasOwnProperty('session')) { + if (Object.hasOwn(options, 'session')) { this.$session(options.session); } if (this.$__.timestamps != null) { @@ -748,7 +748,7 @@ Model.prototype.deleteOne = function deleteOne(options) { options = {}; } - if (options.hasOwnProperty('session')) { + if (Object.hasOwn(options, 'session')) { this.$session(options.session); } @@ -2929,7 +2929,7 @@ Model.insertMany = async function insertMany(arr, options) { const lean = !!options.lean; const asyncLocalStorage = this.db.base.transactionAsyncLocalStorage?.getStore(); - if ((!options || !options.hasOwnProperty('session')) && asyncLocalStorage?.session != null) { + if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) { options = { ...options, session: asyncLocalStorage.session }; } @@ -3294,7 +3294,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) { const validations = options?._skipCastBulkWrite ? [] : ops.map(op => castBulkWrite(this, op, options)); const asyncLocalStorage = this.db.base.transactionAsyncLocalStorage?.getStore(); - if ((!options || !options.hasOwnProperty('session')) && asyncLocalStorage?.session != null) { + if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) { options = { ...options, session: asyncLocalStorage.session }; } @@ -4981,10 +4981,10 @@ Model._applyQueryMiddleware = function _applyQueryMiddleware() { function _getContexts(hook) { const ret = {}; - if (hook.hasOwnProperty('query')) { + if (Object.hasOwn(hook, 'query')) { ret.query = hook.query; } - if (hook.hasOwnProperty('document')) { + if (Object.hasOwn(hook, 'document')) { ret.document = hook.document; } return ret; diff --git a/lib/mongoose.js b/lib/mongoose.js index c20ae51ffb4..d098ccddace 100644 --- a/lib/mongoose.js +++ b/lib/mongoose.js @@ -601,10 +601,10 @@ Mongoose.prototype.model = function model(name, schema, collection, options) { // connection.model() may be passing a different schema for // an existing model name. in this case don't read from cache. - const overwriteModels = _mongoose.options.hasOwnProperty('overwriteModels') ? + const overwriteModels = Object.hasOwn(_mongoose.options, 'overwriteModels') ? _mongoose.options.overwriteModels : options.overwriteModels; - if (_mongoose.models.hasOwnProperty(name) && options.cache !== false && overwriteModels !== true) { + if (Object.hasOwn(_mongoose.models, name) && options.cache !== false && overwriteModels !== true) { if (originalSchema?.instanceOfSchema && originalSchema !== _mongoose.models[name].schema) { throw new _mongoose.Error.OverwriteModelError(name); diff --git a/lib/query.js b/lib/query.js index 9910a8c16cc..71de67d5d74 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2097,7 +2097,7 @@ Query.prototype._optionsForExec = function(model) { applyWriteConcern(model.schema, options); const asyncLocalStorage = this.model?.db?.base.transactionAsyncLocalStorage?.getStore(); - if (!this.options.hasOwnProperty('session') && asyncLocalStorage?.session != null) { + if (!Object.hasOwn(this.options, 'session') && asyncLocalStorage?.session != null) { options.session = asyncLocalStorage.session; } @@ -4599,7 +4599,7 @@ Query.prototype.exec = async function exec(op) { throw new MongooseError('Query has invalid `op`: "' + this.op + '"'); } - if (this.options && this.options.sort && typeof this.options.sort === 'object' && this.options.sort.hasOwnProperty('')) { + if (this.options && this.options.sort && typeof this.options.sort === 'object' && Object.hasOwn(this.options.sort, '')) { throw new Error('Invalid field "" passed to sort()'); } @@ -4998,7 +4998,7 @@ Query.prototype.cast = function(model, obj) { model = model || this.model; const discriminatorKey = model.schema.options.discriminatorKey; if (obj != null && - obj.hasOwnProperty(discriminatorKey)) { + Object.hasOwn(obj, discriminatorKey)) { model = getDiscriminatorByValue(model.discriminators, obj[discriminatorKey]) || model; } diff --git a/lib/schema.js b/lib/schema.js index 8a4ddddf64c..5fbf0807f25 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -1483,17 +1483,17 @@ Schema.prototype._gatherChildSchemas = function _gatherChildSchemas() { */ function _getPath(schema, path, cleanPath) { - if (schema.paths.hasOwnProperty(path)) { + if (Object.hasOwn(schema.paths, path)) { return schema.paths[path]; } - if (schema.subpaths.hasOwnProperty(cleanPath)) { + if (Object.hasOwn(schema.subpaths, cleanPath)) { const subpath = schema.subpaths[cleanPath]; if (subpath === 'nested') { return undefined; } return subpath; } - if (schema.singleNestedPaths.hasOwnProperty(cleanPath) && typeof schema.singleNestedPaths[cleanPath] === 'object') { + if (Object.hasOwn(schema.singleNestedPaths, cleanPath) && typeof schema.singleNestedPaths[cleanPath] === 'object') { const singleNestedPath = schema.singleNestedPaths[cleanPath]; if (singleNestedPath === 'nested') { return undefined; @@ -1681,20 +1681,20 @@ Schema.prototype.interpretAsType = function(path, obj, options) { childSchemaOptions.typeKey = options.typeKey; } // propagate 'strict' option to child schema - if (options.hasOwnProperty('strict')) { + if (Object.hasOwn(options, 'strict')) { childSchemaOptions.strict = options.strict; } - if (options.hasOwnProperty('strictQuery')) { + if (Object.hasOwn(options, 'strictQuery')) { childSchemaOptions.strictQuery = options.strictQuery; } - if (options.hasOwnProperty('toObject')) { + if (Object.hasOwn(options, 'toObject')) { childSchemaOptions.toObject = utils.omit(options.toObject, ['transform']); } - if (options.hasOwnProperty('toJSON')) { + if (Object.hasOwn(options, 'toJSON')) { childSchemaOptions.toJSON = utils.omit(options.toJSON, ['transform']); } - if (this._userProvidedOptions.hasOwnProperty('_id')) { + if (Object.hasOwn(this._userProvidedOptions, '_id')) { childSchemaOptions._id = this._userProvidedOptions._id; } else if (Schema.Types.DocumentArray.defaultOptions._id != null) { childSchemaOptions._id = Schema.Types.DocumentArray.defaultOptions._id; @@ -1731,7 +1731,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) { `Could not determine the embedded type for array \`${path}\`. ` + 'See https://mongoosejs.com/docs/guide.html#definition for more info on supported schema syntaxes.'); } - if (!MongooseTypes.hasOwnProperty(name)) { + if (!Object.hasOwn(MongooseTypes, name)) { throw new TypeError('Invalid schema configuration: ' + `\`${name}\` is not a valid type within the array \`${path}\`.` + 'See https://bit.ly/mongoose-schematypes for a list of valid schema types.'); @@ -1892,24 +1892,24 @@ Schema.prototype.indexedPaths = function indexedPaths() { */ Schema.prototype.pathType = function(path) { - if (this.paths.hasOwnProperty(path)) { + if (Object.hasOwn(this.paths, path)) { return 'real'; } - if (this.virtuals.hasOwnProperty(path)) { + if (Object.hasOwn(this.virtuals, path)) { return 'virtual'; } - if (this.nested.hasOwnProperty(path)) { + if (Object.hasOwn(this.nested, path)) { return 'nested'; } // Convert to '.$' to check subpaths re: gh-6405 const cleanPath = _pathToPositionalSyntax(path); - if (this.subpaths.hasOwnProperty(cleanPath) || this.subpaths.hasOwnProperty(path)) { + if (Object.hasOwn(this.subpaths, cleanPath) || Object.hasOwn(this.subpaths, path)) { return 'real'; } - const singleNestedPath = this.singleNestedPaths.hasOwnProperty(cleanPath) || this.singleNestedPaths.hasOwnProperty(path); + const singleNestedPath = Object.hasOwn(this.singleNestedPaths, cleanPath) || Object.hasOwn(this.singleNestedPaths, path); if (singleNestedPath) { return singleNestedPath === 'nested' ? 'nested' : 'real'; } @@ -1939,7 +1939,7 @@ Schema.prototype.hasMixedParent = function(path) { path = ''; for (let i = 0; i < subpaths.length; ++i) { path = i > 0 ? path + '.' + subpaths[i] : subpaths[i]; - if (this.paths.hasOwnProperty(path) && + if (Object.hasOwn(this.paths, path) && this.paths[path] instanceof MongooseTypes.Mixed) { return this.paths[path]; } @@ -1968,7 +1968,7 @@ Schema.prototype.setupTimestamp = function(timestamps) { function getPositionalPathType(self, path, cleanPath) { const subpaths = path.split(/\.(\d+)\.|\.(\d+)$/).filter(Boolean); if (subpaths.length < 2) { - return self.paths.hasOwnProperty(subpaths[0]) ? + return Object.hasOwn(self.paths, subpaths[0]) ? self.paths[subpaths[0]] : 'adhocOrUndefined'; } @@ -2673,7 +2673,7 @@ Schema.prototype.virtual = function(name, options) { */ Schema.prototype.virtualpath = function(name) { - return this.virtuals.hasOwnProperty(name) ? this.virtuals[name] : null; + return Object.hasOwn(this.virtuals, name) ? this.virtuals[name] : null; }; /** @@ -2821,8 +2821,8 @@ Schema.prototype.loadClass = function(model, virtualsOnly) { // Stop copying when hit certain base classes if (model === Object.prototype || model === Function.prototype || - model.prototype.hasOwnProperty('$isMongooseModelPrototype') || - model.prototype.hasOwnProperty('$isMongooseDocumentPrototype')) { + Object.hasOwn(model.prototype, '$isMongooseModelPrototype') || + Object.hasOwn(model.prototype, '$isMongooseDocumentPrototype')) { return this; } @@ -2835,7 +2835,7 @@ Schema.prototype.loadClass = function(model, virtualsOnly) { return; } const prop = Object.getOwnPropertyDescriptor(model, name); - if (prop.hasOwnProperty('value')) { + if (Object.hasOwn(prop, 'value')) { this.static(name, prop.value); } }, this); @@ -3061,7 +3061,7 @@ Schema.prototype._transformDuplicateKeyError = function _transformDuplicateKeyEr return error; } const firstKey = keys[0]; - if (!this._duplicateKeyErrorMessagesByPath.hasOwnProperty(firstKey)) { + if (!Object.hasOwn(this._duplicateKeyErrorMessagesByPath, firstKey)) { return error; } return new MongooseError(this._duplicateKeyErrorMessagesByPath[firstKey], { cause: error }); diff --git a/lib/schema/array.js b/lib/schema/array.js index 945cc1a90eb..d3ba2896a80 100644 --- a/lib/schema/array.js +++ b/lib/schema/array.js @@ -80,7 +80,7 @@ function SchemaArray(key, cast, options, schemaOptions, parentSchema) { : utils.getFunctionName(cast); const Types = require('./index.js'); - const schemaTypeDefinition = Types.hasOwnProperty(name) ? Types[name] : cast; + const schemaTypeDefinition = Object.hasOwn(Types, name) ? Types[name] : cast; if (typeof schemaTypeDefinition === 'function') { if (schemaTypeDefinition === SchemaArray) { diff --git a/lib/schemaType.js b/lib/schemaType.js index a30f90cdeca..222e2be213e 100644 --- a/lib/schemaType.js +++ b/lib/schemaType.js @@ -46,10 +46,10 @@ function SchemaType(path, options, instance, parentSchema) { this.instance = instance; this.schemaName = this.constructor.schemaName; this.validators = []; - this.getters = this.constructor.hasOwnProperty('getters') ? + this.getters = Object.hasOwn(this.constructor, 'getters') ? this.constructor.getters.slice() : []; - this.setters = this.constructor.hasOwnProperty('setters') ? + this.setters = Object.hasOwn(this.constructor, 'setters') ? this.constructor.setters.slice() : []; @@ -62,7 +62,7 @@ function SchemaType(path, options, instance, parentSchema) { for (const option of defaultOptionsKeys) { if (option === 'validate') { this.validate(defaultOptions.validate); - } else if (defaultOptions.hasOwnProperty(option) && !Object.prototype.hasOwnProperty.call(options, option)) { + } else if (Object.hasOwn(defaultOptions, option) && !Object.hasOwn(options, option)) { options[option] = defaultOptions[option]; } } @@ -339,7 +339,7 @@ SchemaType.prototype.cast = function cast() { */ SchemaType.set = function set(option, value) { - if (!this.hasOwnProperty('defaultOptions')) { + if (!Object.hasOwn(this, 'defaultOptions')) { this.defaultOptions = Object.assign({}, this.defaultOptions); } this.defaultOptions[option] = value; @@ -362,7 +362,7 @@ SchemaType.set = function set(option, value) { */ SchemaType.get = function(getter) { - this.getters = this.hasOwnProperty('getters') ? this.getters : []; + this.getters = Object.hasOwn(this, 'getters') ? this.getters : []; this.getters.push(getter); }; @@ -496,7 +496,7 @@ SchemaType.prototype.unique = function unique(value, message) { 'false and `unique` set to true'); } - if (!this.options.hasOwnProperty('index') && value === false) { + if (!Object.hasOwn(this.options, 'index') && value === false) { return this; } @@ -535,7 +535,7 @@ SchemaType.prototype.text = function(bool) { 'false and `text` set to true'); } - if (!this.options.hasOwnProperty('index') && bool === false) { + if (!Object.hasOwn(this.options, 'index') && bool === false) { return this; } @@ -572,7 +572,7 @@ SchemaType.prototype.sparse = function(bool) { 'false and `sparse` set to true'); } - if (!this.options.hasOwnProperty('index') && bool === false) { + if (!Object.hasOwn(this.options, 'index') && bool === false) { return this; } diff --git a/lib/types/array/index.js b/lib/types/array/index.js index 19f37cfd039..81eb2d0ef6c 100644 --- a/lib/types/array/index.js +++ b/lib/types/array/index.js @@ -97,9 +97,9 @@ function MongooseArray(values, path, doc, schematype) { set: function(target, prop, value) { if (typeof prop === 'string' && numberRE.test(prop)) { mongooseArrayMethods.set.call(proxy, prop, value, false); - } else if (internals.hasOwnProperty(prop)) { + } else if (Object.hasOwn(internals, prop)) { internals[prop] = value; - } else if (schematype?.virtuals?.hasOwnProperty(prop)) { + } else if (schematype?.virtuals && Object.hasOwn(schematype.virtuals, prop)) { schematype.virtuals[prop].applySetters(value, target); } else { __array[prop] = value; diff --git a/lib/types/documentArray/index.js b/lib/types/documentArray/index.js index 0fd138757db..b8a14d90815 100644 --- a/lib/types/documentArray/index.js +++ b/lib/types/documentArray/index.js @@ -91,9 +91,9 @@ function MongooseDocumentArray(values, path, doc, schematype) { set: function(target, prop, value) { if (typeof prop === 'string' && numberRE.test(prop)) { DocumentArrayMethods.set.call(proxy, prop, value, false); - } else if (internals.hasOwnProperty(prop)) { + } else if (Object.hasOwn(internals, prop)) { internals[prop] = value; - } else if (schematype?.virtuals?.hasOwnProperty(prop)) { + } else if (schematype?.virtuals && Object.hasOwn(schematype.virtuals, prop)) { schematype.virtuals[prop].applySetters(value, target); } else { __array[prop] = value; diff --git a/lib/types/objectid.js b/lib/types/objectid.js index 5544c243f6e..f0a9b259036 100644 --- a/lib/types/objectid.js +++ b/lib/types/objectid.js @@ -30,7 +30,7 @@ Object.defineProperty(ObjectId.prototype, '_id', { * Convenience `valueOf()` to allow comparing ObjectIds using double equals re: gh-7299 */ -if (!ObjectId.prototype.hasOwnProperty('valueOf')) { +if (!Object.hasOwn(ObjectId.prototype, 'valueOf')) { ObjectId.prototype.valueOf = function objectIdValueOf() { return this.toString(); }; diff --git a/lib/virtualType.js b/lib/virtualType.js index 2008ebf8bb4..30cc35d5a8a 100644 --- a/lib/virtualType.js +++ b/lib/virtualType.js @@ -143,7 +143,7 @@ VirtualType.prototype.set = function(fn) { VirtualType.prototype.applyGetters = function(value, doc) { if (utils.hasUserDefinedProperty(this.options, ['ref', 'refPath']) && doc.$$populatedVirtuals && - doc.$$populatedVirtuals.hasOwnProperty(this.path)) { + Object.hasOwn(doc.$$populatedVirtuals, this.path)) { value = doc.$$populatedVirtuals[this.path]; } From 5869d8c3a0fb00751fb8f9c2e302ec59fba2b2fa Mon Sep 17 00:00:00 2001 From: Hafez Date: Fri, 5 Dec 2025 04:01:47 +0100 Subject: [PATCH 4/4] chore: fix lint --- docs/js/theme-toggle.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/js/theme-toggle.js b/docs/js/theme-toggle.js index 1fb347e0458..6671d792053 100644 --- a/docs/js/theme-toggle.js +++ b/docs/js/theme-toggle.js @@ -1,6 +1,5 @@ +'use strict'; (function() { - 'use strict'; - const STORAGE_KEY = 'mongoose-theme'; const CODE_THEME_CLASS = 'code-theme-dark'; const supportsMatchMedia = typeof window !== 'undefined' && typeof window.matchMedia === 'function'; @@ -20,7 +19,8 @@ if (!skipSetStorage) { try { localStorage.setItem(STORAGE_KEY, theme); - } catch (e) { + // eslint-disable-next-line no-unused-vars + } catch (err) { // Silently fail - theme will still work for current session } }