2026 EV 1036km Flagship

Liftback-u kryesor luksoz i Denza-s me deri në 1036km autonomi EV, performancë 850kW me tre motorë dhe opsione hibride plug-in me 401km autonomi elektrike.

Kthehu te Denza Z9 GT
Llogaritës Leasing
Pagesa Mujore e Vlerësuar
0
25% pagesa fillestare (25%) · 8% norma e interesit · 5 vite (60 muaj)
Vendosni çmimin e makines
€0€80,000
Pagesa Fillestare (25%)€0
Shuma e Financuar€0

Ky është vetëm një vlerësim dhe nuk pasqyron çmimin aktual të makina. Kërkoni një ofertë për çmimin e saktë.

Telefono Tani

Specifikimet e 2026 EV 1036km Flagship

Performanca

5.8s
0-100 km/h

Fuqia & Bateria

503 hp
(370 kW)

122.5 kWh
Kapaciteti i Baterisë

Ngarkimi

0.15h
30-80%

Automjeti

5 seats
Ulëse

Rear-engine rear-wheel drive
Sistemi i Transmetimit

Specifikimet e Plota

Basic
Range Cltc1036
Max Power Kw370
Max Power Hp503
Battery Kwh122.5
Acceleration 0 1005.8
Top Speed240
DrivetrainRear-engine rear-wheel drive
Parametrat Bazë
$IsNew
Prodhuesi腾势汽车
Niveli i Automjetit中大型车
Lloji i EnergjisëPure electric
Data e Lancimit2026-03-05
Autonomia Elektrike CLTC (km)1036
Koha e Ngarkimit të Shpejtë (orë)0.15
Diapazoni i Ngarkimit të Shpejtë (%)10-97
Fuqia Maksimale (kW)370
Momenti Maksimal (N·m)500
TransmisioniElectric vehicle single-speed transmission
Struktura e Trupit5-door 5-seat liftback
Motori Elektrik (Ps)503
Gjatësi×Gjerësi×Lartësi (mm)5180*1990*1490
Përshpejtimi Zyrtar 0-100 km/h (s)5.8
Shpejtësia Maksimale (km/h)240
Konsumi Ekuivalent i Energjisë Elektrike1.54
Garancia e AutomjetitSix years or 150,000 km
Pesha e Zbrazët (kg)2700
Pesha Maksimale me Ngarkesë (kg)3075
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathbasicParameters
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Karrroceria
$IsNew
Gjatësia (mm)5180
Gjerësia (mm)1990
Lartësia (mm)1490
Ndërdistanca e Boshteve (mm)3125
Gjerësia e Boshtit të Përparmë (mm)1710
Gjerësia e Boshtit të Pasëm (mm)1715
Këndi i Afërimit (°)13
Këndi i Largimit (°)16
Rrezja Minimale e Kthesës (m)5.4
Struktura e TrupitLiftback
Mënyra e Hapjes së DerësSwing door
Numri i Dyerve5
Numri i Vendeve5
Vëllimi i Bagazhit (L)495
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathbody
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Motori
$IsNew
Lloji i EnergjisëPure electric
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathengine
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Motori Elektrik
$IsNew
Lloji i Motorit永磁
Fuqia Totale e Motorit (kW)370
Kuajfuqia Totale e Motorit (Ps)503
Momenti Total i Motorit (N·m)500
Marka e Motorit të Pasëm比亚迪
Modeli i Motorit të PasëmTZ226QYD
Fuqia Maksimale e Motorit të Pasëm (kW)370
Momenti Maksimal i Motorit të Pasëm (N·m)500
Numri i Motorëve të TërheqjesSingle motor
Paraqitja e MotoritRear
Politika e Garancisë për Pronarin e ParëLifetime warranty/non-commercial (exclusion clause subject to official terms)
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathelectricMotor
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Bateria & Ngarkimi
$IsNew
Lloji i BaterisëLithium iron phosphate battery
Marka e Qelizave të BaterisëFinDreams
Teknologjia Specifike e Baterisë第二代刀片电池
Metoda e Ftohjes së Baterisë直冷
Autonomia Elektrike CLTC (km)1036
Energjia e Baterisë (kWh)122.5
Konsumi për 100km (kWh)13.3
Funksioni i Ngarkimit të ShpejtëSupported
Koha e Ngarkimit të Shpejtë (orë)0.15
Diapazoni i Ngarkimit të Shpejtë (%)10-97
Vendndodhja e Portës së Ngarkimit të NgadaltëRight rear side
Vendndodhja e Portës së Ngarkimit të ShpejtëRight rear side
Fuqia e Shkarkimit AC të Jashtëm (kW)6
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathbatteryCharging
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Battery
Capacity Kwh122.5
Charging Time 30 800.15h (10-97)
Charging
Charging Time 30 800.15h
Dimensions
Seats5
Gjatësia (mm)5180
Gjerësia (mm)1990
Lartësia (mm)1490
Ndërdistanca e Boshteve (mm)3125
Kutia e Shpejtësive
$IsNew
ShkurtimElectric vehicle single-speed transmission
Numri i Marsheve1
Lloji i TransmetimitFixed gear ratio transmission
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathgearbox
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Shasia & Timoni
$IsNew
Mënyra e TërheqjesRear-engine rear-wheel drive
Lloji i Pezullimit të PërparmëDouble wishbone independent suspension
Lloji i Pezullimit të PasëmFive-link independent suspension
Lloji i Drejtimit të FuqishëmElectric power steering
Struktura e TrupitUnibody
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathchassisSteering
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Rrotat & Frenave
$IsNew
Lloji i Frenave të PërparmëVentilated disc
Lloji i Frenave të PasmeVentilated disc
Lloji i Frenës së ParkimitElectronic parking
Specifikat e Gomave të Përparme255/45 R20
Specifikat e Gomave të Pasme255/45 R20
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathwheelBrakes
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Siguria Pasive
$IsNew
Jastëkë Ajri Shoferi/PasagjeriDriver +/Passenger +
Jastëkë Ajri Anësore Përpara/PasFront +/Rear +
Jastëkë Ajri Koke Përpara/Pas (Perde)Front +/Rear +
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathpassiveSafety
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Siguria Aktive
$IsNew
Monitorim Presioni GomashTire pressure display
Kujtues Rrip Sigurimi të HapurFull vehicle
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathactiveSafety
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Kontrolli i Drejtimit
$IsNew
Ndërrimi i Mënyrës së DrejtimitSnow
Funksion Pezullimi të Ndryshueshëm悬架高低调节
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathdrivingControl
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Harduer i Drejtimit
$IsNew
Radar Parkimi Përpara/PasFront +/Rear +
Imazhe Asistence Drejtimi360-degree panoramic camera
Kamera e Perceptimit të Përparmë双目
Numri i Kamerave11
Numri i Kamerave Brenda Makinës1
Numri i Radarëve Ultrasonik12
Numri i Radarëve me Valë Milimetrike3
Numri i LiDAR-ëve1
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathdrivingHardware
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Veçoritë e Drejtimit
$IsNew
Sistemi i Kontrollit të KursitFull-speed adaptive cruise
Sistemi i Drejtimit të AsistuarDiPilot
Niveli i Asistencës së DrejtimitL2
Marka e Hartës高德
Seksione Drejtimi të AsistuarHighway sections
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathdrivingFeatures
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Pamja & Anti-vjedhje
$IsNew
Materiali i RrrotësAluminum alloy
Lloji i ÇelësitNFC, RFID钥匙
Funksion Hyrje Pa ÇelësFull vehicle
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathappearanceAntiTheft
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Dritat e Jashtme
$IsNew
Burimi i Dritës së UlëtLED
Burimi i Dritës së LartëLED
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathexteriorLights
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Çati Dielli & Xhami
$IsNew
Lloji i Çatisë së Xhamit分段式不可开启天窗
Xhama Elektrike Përpara/PasFront +/Rear +
Funksion Ngritje Xhami me Një PrekjeFull vehicle
Xham Izolues Anësor Shumështresor后排
Pasqyrë Grimi主驾驶+照明灯
Funksion Fshese me Sensor ShiuRain-sensing
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathskylightGlass
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Pasqyra e Jashtme e Përpasme
$IsNew
Funksioni i Pasqyrës së Jashtme të PasmeMirror heating
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathexteriorRearviewMirror
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Sistemi i Ekranit
$IsNew
Ekran Ngjyrash Kontrolli QendrorTouch LCD screen
Madhësia e Ekranit Qendror17.3英寸
Rezolucioni i Ekranit Qendror2.5K
Ndërlidhje/Hartëzim Telefoni原厂互联/映射
Sistem Kontrolli me Njohje ZëriAir conditioning
Fjala e Zgjimit të Asistentit ZanorHi, Denza
Njohje Zgjimi me Zë sipas RajonitFour zones
Sistem Inteligjent Brenda AutomjetitDiLink
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathscreenSystem
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Konfigurimi Inteligjent
$IsNew
Rrjeti 4G/5G5G
Funksion Aplikacioni nga DistancaCharging management
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathintelligentConfiguration
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Timoni & Pasqyra e Brendshme e Përpasme
$IsNew
Materiali i TimonitLeather
Rregullimi i Pozicionit të TimonitElectric up/down + front/rear adjustment
Ndërrimi i MarsheveElectronic column-mounted shifting
Ekran Kompjuteri i UdhëtimitColor
Madhësia e Instrumenteve LCD13.2英寸
Madhësia e HUD50英寸
Funksioni i Pasqyrës së Brendshme të Pasme流媒体
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathsteeringWheelInteriorRearviewMirror
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Ngarkimi Brenda Makinës
$IsNew
Portë Multimedia/NgarkimiType-C
Sasia e Portave USB/Type-C前排2个/后排2个
Funksion Ngarkimi Pa Tel i Telefonit后排
Fuqia e Ngarkimit Pa Tel (W)50
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathinCarCharging
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Konfigurimi i Ulëseve
$IsNew
Materiali i NdenjësesGenuine leather
Metoda e Rregullimit të Ndenjëses Kryesore腿托调节
Metoda e Rregullimit të Ndenjëses së Pasagjerit腿托调节
Rregullim Elektrik Ndenjësesh Shoferi/PasagjeriDriver +/Passenger +
Funksionet e Ndenjëseve të Përparme头枕扬声器(仅驾驶位)
Funksion Memorie Ndenjëseje Elektrike副驾驶位
Rregullimi i Ndenjëseve të Rreshtit të Dytë靠背调节
Funksioni i Ndenjëseve të Rreshtit të DytëVentilation
Palosje Ndenjësesh të PasmeProportional folding
Mbështetëse Qendrore Përpara/PasFront +/Rear +
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathseatConfiguration
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Audio & Ndriçimi i Brendshëm
$IsNew
Marka e AltoparlantitDEVIALET帝瓦雷
Numri i Altoparlantëve24喇叭
Ndriçim Ambiental Brendshëm128色
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathaudioInteriorLighting
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Kondicionimi i Ajrit & Frigoriferi
$IsNew
Metoda e Kontrollit të Temperaturës së KondicionimitAutomatic air conditioning
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathairConditioningRefrigerator
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }
Ngjyrat
$IsNew
Ngjyrat e Jashtme冬日青
Ngjyrat e Brendshme¥10000
function Object() { [native code] }function SingleNested(value, path, parent) { this.$__parent = parent; SubdocumentType.apply(this, arguments); if (parent == null) { return; } this.$session(parent.$session()); }
ToBSONfunction() { return this.toObject(internalToObjectOptions); }
$BasePathcolors
$ Initfunction syncWrapper() { const modifiedArgs = _this.execPreSync(name, this, Array.from(arguments)); const toReturn = fn.apply(this, modifiedArgs); const result = _this.execPostSync(name, this, [toReturn]); return result[0]; }
Saveasync function save(options) { options = options || {}; if (!options.suppressWarning) { utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + 'the document to MongoDB, it only runs save middleware. ' + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + 'if you\'re sure this behavior is right for your app.'); } return await this.$__save(); }
$ FullPathfunction(path) { if (!this.$__.fullPath) { this.ownerDocument(); } return path ? this.$__.fullPath + '.' + path : this.$__.fullPath; }
$ PathRelativeToParentfunction(p) { // If this subdocument has a stored relative path (set by map when subdoc is created), // use it directly to avoid string operations if (this.$pathRelativeToParent != null) { return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p; } if (p == null) { return this.$basePath; } if (!this.$basePath) { return p; } return [this.$basePath, p].join('.'); }
$ Saveasync function $__save() { try { await this._execDocumentPreHooks('save'); } catch (error) { await this._execDocumentPostHooks('save', error); return; } await this._execDocumentPostHooks('save'); }
$IsValidfunction(path) { const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { return parent.$isValid(fullPath); } return Document.prototype.$isValid.call(this, path); }
MarkModifiedfunction(path) { Document.prototype.markModified.call(this, path); const parent = this.$parent(); if (parent == null) { return; } const pathToMark = this.$__pathRelativeToParent(path); if (pathToMark == null) { return; } const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); if (parent.isDirectModified(myPath) || this.isNew) { return; } this.$__parent.markModified(pathToMark, this); }
IsModifiedfunction(paths, options, modifiedPaths) { const parent = this.$parent(); if (parent != null) { if (Array.isArray(paths) || typeof paths === 'string') { paths = (Array.isArray(paths) ? paths : paths.split(' ')); paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); } else if (!paths) { paths = this.$__pathRelativeToParent(); } return parent.$isModified(paths, options, modifiedPaths); } return Document.prototype.isModified.call(this, paths, options, modifiedPaths); }
$MarkValidfunction(path) { Document.prototype.$markValid.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$markValid(fullPath); } }
Invalidatefunction(path, err, val) { Document.prototype.invalidate.call(this, path, err, val); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.invalidate(fullPath, err, val); } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { throw err; } return this.ownerDocument().$__.validationError; }
$Ignorefunction(path) { Document.prototype.$ignore.call(this, path); const parent = this.$parent(); const fullPath = this.$__pathRelativeToParent(path); if (parent != null && fullPath != null) { parent.$ignore(fullPath); } }
OwnerDocumentfunction() { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, true)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } this.$__.fullPath = paths.join('.'); this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
$ FullPathWithIndexesfunction() { let parent = this; const paths = []; const seenDocs = new Set([parent]); while (true) { if (typeof parent.$__pathRelativeToParent !== 'function') { break; } paths.unshift(parent.$__pathRelativeToParent(void 0, false)); const _parent = parent.$parent(); if (_parent == null) { break; } parent = _parent; if (seenDocs.has(parent)) { throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); } seenDocs.add(parent); } return paths.join('.'); }
Parentfunction() { return this.$__parent; }
$Parentfunction() { return this.$__parent; }
$ SetParentfunction $__setParent(parent) { this.$__parent = parent; }
$ RemoveFromParentfunction() { this.$__parent.set(this.$basePath, null); }
DeleteOnefunction deleteOne(options) { registerRemoveListener(this); // If removing entire doc, no need to remove subdoc if (!options?.noop) { this.$__removeFromParent(); const owner = this.ownerDocument(); owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; owner.$__.removedSubdocs.push(this); } }
Populatefunction() { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
Inspectfunction() { return this.toObject(); }
$ToObjectfunction $toObject(options, json) { const ret = Document.prototype.$toObject.call(this, 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.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; if (minimize && !this.constructor.$__required) { return undefined; } } return ret; }
$IsMongooseDocumentPrototype
Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Onfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Oncefunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Emitfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$Listenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$SetMaxListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$RemoveAllListenersfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$AddListenerfunction() { // Delay creating emitter until necessary because emitters take up a lot of memory, // especially for subdocuments. if (!this.$__.emitter) { if (emitterFn === 'emit') { return; } this.$__.emitter = new EventEmitter(); this.$__.emitter.setMaxListeners(0); } return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
$ BuildDocfunction(obj, fields, skipId, exclude, hasIncludedChildren) { const doc = {}; const paths = Object.keys(this.$__schema.paths). // Don't build up any paths that are underneath a map, we don't know // what the keys will be filter(p => !p.includes('$*')); const plen = paths.length; let ii = 0; for (; ii < plen; ++ii) { const p = paths[ii]; if (p === '_id') { if (skipId) { continue; } if (obj && '_id' in obj) { continue; } } const path = this.$__schema.paths[p].splitPath(); const len = path.length; const last = len - 1; let curPath = ''; let doc_ = doc; let included = false; for (let i = 0; i < len; ++i) { const piece = path[i]; if (!curPath.length) { curPath = piece; } else { curPath += '.' + piece; } // support excluding intermediary levels if (exclude === true) { if (curPath in fields) { break; } } else if (exclude === false && fields && !included) { if (curPath in fields) { included = true; } else if (!hasIncludedChildren[curPath]) { break; } } if (i < last) { doc_ = doc_[piece] || (doc_[piece] = {}); } } } this._doc = doc; }
Initfunction(doc, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = null; } if (doc == null) { throw new ObjectParameterError(doc, 'doc', 'init'); } this.$__init(doc, opts); if (fn) { fn(null, this); } return this; }
$Initfunction() { return this.constructor.prototype.init.apply(this, arguments); }
UpdateOnefunction updateOne(update, options) { const query = this.constructor.updateOne(); const self = this; query.pre(async function queryPreUpdateOne() { const res = await self._execDocumentPreHooks('updateOne', self, update, options); // `self` is passed to pre hooks as argument for backwards compatibility, but that // isn't the actual arguments passed to the wrapped function. if (res[0] !== self || res[1] !== update || res[2] !== options) { throw new Error('Document updateOne pre hooks cannot overwrite arguments'); } query.updateOne({ _id: self._doc._id }, update, options); // Apply custom where conditions _after_ document updateOne middleware for // consistency with save() - sharding plugin needs to set $where if (self.$where != null) { this.where(self.$where); } if (self.$session() != null) { if (!('session' in query.options)) { query.options.session = self.$session(); } } return res; }); query.post(function queryPostUpdateOne() { return self._execDocumentPostHooks('updateOne'); }); return query; }
ReplaceOnefunction replaceOne() { const args = [...arguments]; args.unshift({ _id: this._doc._id }); return this.constructor.replaceOne.apply(this.constructor, args); }
$Sessionfunction $session(session) { if (arguments.length === 0) { if (this.$__.session?.hasEnded) { this.$__.session = null; return null; } return this.$__.session; } if (session?.hasEnded) { throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + 'called `endSession()` on the session you are passing to `$session()`.'); } if (session == null && this.$__.session == null) { return; } this.$__.session = session; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$session(session); } } return session; }
$Timestampsfunction $timestamps(value) { if (arguments.length === 0) { if (this.$__.timestamps != null) { return this.$__.timestamps; } if (this.$__schema) { return this.$__schema.options.timestamps; } return undefined; } const currentValue = this.$timestamps(); if (value !== currentValue) { this.$__.timestamps = value; } return this; }
Overwritefunction overwrite(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 (schemaOptions.versionKey && key === schemaOptions.versionKey) { continue; } if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { continue; } this.$set(key, obj[key]); } return this; }
$Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
Setfunction $set(path, val, type, options) { if (utils.isPOJO(type)) { options = type; type = undefined; } const merge = options?.merge; const adhoc = type && type !== true; const constructing = type === true; let adhocs; let keys; let i = 0; let pathtype; let key; let prefix; const userSpecifiedStrict = options && 'strict' in options; let strict = userSpecifiedStrict ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); } if (path == null) { [path, val] = [val, path]; } else if (typeof path !== 'string') { // new Document({ key: val }) if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { // This ternary is to support gh-7898 (copying virtuals if same schema) // while not breaking gh-10819, which for some reason breaks if we use toObject() path = path.$__schema === this.$__schema ? applyVirtuals(path, { ...path._doc }) : path._doc; } } if (path == null) { [path, val] = [val, path]; } prefix = val ? val + '.' : ''; keys = getKeysInSchemaOrder(this.$__schema, path); const len = keys.length; // `_skipMinimizeTopLevel` is because we may have deleted the top-level // nested key to ensure key order. const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; if (len === 0 && _skipMinimizeTopLevel) { delete options._skipMinimizeTopLevel; if (val) { this.$set(val, {}); } return this; } options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); for (let i = 0; i < len; ++i) { key = keys[i]; const pathName = prefix ? prefix + key : key; pathtype = this.$__schema.pathType(pathName); const valForKey = path[key]; // On initial set, delete any nested keys if we're going to overwrite // them to ensure we keep the user's key order. if (type === true && !prefix && valForKey != null && pathtype === 'nested' && this._doc[key] != null) { delete this._doc[key]; } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { this.$set(pathName, valForKey, constructing, options); $applyDefaultsToNested(this.$get(pathName), pathName, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) if (constructing && valForKey === void 0 && this.$get(pathName) !== void 0) { continue; } if (pathtype === 'adhocOrUndefined') { pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); } if (pathtype === 'real' || pathtype === 'virtual') { this.$set(pathName, valForKey, constructing, options); } else if (pathtype === 'nested' && valForKey instanceof Document) { this.$set(pathName, valForKey.toObject({ transform: false }), constructing, options); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, valForKey); } else { throw new StrictModeError(key); } } else if (pathtype === 'nested' && valForKey == null) { this.$set(pathName, valForKey, constructing, options); } } else { this.$set(pathName, valForKey, constructing, options); } } // Ensure all properties are in correct order const orderedDoc = {}; const orderedKeys = Object.keys(this.$__schema.tree); for (let i = 0, len = orderedKeys.length; i < len; ++i) { (key = orderedKeys[i]) && (Object.hasOwn(this._doc, key)) && (orderedDoc[key] = undefined); } this._doc = Object.assign(orderedDoc, this._doc); return this; } let pathType = this.$__schema.pathType(path); let parts = null; if (pathType === 'adhocOrUndefined') { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); } if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { // May be path underneath non-strict schema if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); if (subdocStrict !== undefined) { strict = subdocStrict; } } // Assume this is a Mongoose document that was copied into a POJO using // `Object.assign()` or `{...doc}` val = handleSpreadDoc(val, true); // if this doc is being constructed we should not trigger getters const priorVal = (() => { if (this.$__.priorDoc != null) { return this.$__.priorDoc.$__getValue(path); } if (constructing) { return void 0; } return this.$__getValue(path); })(); if (pathType === 'nested' && val) { if (typeof val === 'object' && val != null) { if (val.$__ != null) { val = val.toObject(internalToObjectOptions); } if (val == null) { this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } const wasModified = this.$isModified(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; const keys = Object.keys(initialVal || {}); for (const key of keys) { this.$__.savedState[path + '.' + key] = initialVal[key]; } } if (!merge) { this.$__setValue(path, null); cleanModifiedSubpaths(this, path); } else { return this.$set(val, path, constructing, options); } const keys = getKeysInSchemaOrder(this.$__schema, val, path); this.$__setValue(path, {}); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); } if (priorVal != null && (!wasModified || hasInitialVal) && utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { this.unmarkModified(path); } else { this.markModified(path); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } let schema; if (parts == null) { parts = path.indexOf('.') === -1 ? [path] : path.split('.'); } // Might need to change path for top-level alias if (typeof this.$__schema.aliases[parts[0]] === 'string') { parts[0] = this.$__schema.aliases[parts[0]]; } if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types let mixed; for (i = 0; i < parts.length; ++i) { const subpath = parts.slice(0, i + 1).join('.'); // If path is underneath a virtual, bypass everything and just set it. if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { mpath.set(path, val, this); return this; } schema = this.$__schema.path(subpath); if (schema == null) { continue; } if (schema instanceof MixedSchema) { // allow changes to sub paths of mixed types mixed = true; break; } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { // Map of mixed and not the last element in the path resolves to mixed mixed = true; schema = schema.$__schemaType; break; } } if (schema == null) { // Check for embedded discriminators schema = getEmbeddedDiscriminatorPath(this, path); } if (!mixed && !schema) { if (strict === 'throw') { throw new StrictModeError(path); } return this; } } else if (pathType === 'virtual') { schema = this.$__schema.virtualpath(path); schema.applySetters(val, this); return this; } else { schema = this.$__path(path); } // gh-4578, if setting a deeply nested path that doesn't exist yet, create it let cur = this._doc; let curPath = ''; for (i = 0; i < parts.length - 1; ++i) { cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; if (!cur) { this.$set(curPath, {}); // Hack re: gh-5800. If nested field is not selected, it probably exists // so `MongoServerError: cannot use the part (nested of nested.num) to // traverse the element ({nested: null})` is not likely. If user gets // that error, its their fault for now. We should reconsider disallowing // modifying not selected paths for 6.x if (!this.$__isSelected(curPath)) { this.unmarkModified(curPath); } cur = this.$__getValue(curPath); } } let pathToMark; // When using the $set operator the path to the field must already exist. // Else mongodb throws: "LEFT_SUBFIELD only supports Object" if (parts.length <= 1) { pathToMark = path; } else { const len = parts.length; for (i = 0; i < len; ++i) { const subpath = parts.slice(0, i + 1).join('.'); if (this.$get(subpath, null, { getters: false }) === null) { pathToMark = subpath; break; } } if (!pathToMark) { pathToMark = path; } } if (!schema) { this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (pathType === 'nested' && val == null) { cleanModifiedSubpaths(this, path); } return this; } // If overwriting a subdocument path, make sure to clear out // any errors _before_ setting, so new errors that happen // get persisted. Re: #9080 if (schema.$isSingleNested || schema.$isMongooseArray) { _markValidSubpaths(this, path); } if (val != null && merge && schema.$isSingleNested) { if (val instanceof Document) { val = val.toObject({ virtuals: false, transform: false }); } const keys = Object.keys(val); for (const key of keys) { this.$set(path + '.' + key, val[key], constructing, options); } return this; } let shouldSet = true; try { // If the user is trying to set a ref path to a document with // the correct model name, treat it as populated const refMatches = (() => { if (schema.options == null) { return false; } if (!(val instanceof Document)) { return false; } const model = val.constructor; // Check ref const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; const ref = refOpt?.modelName || refOpt; if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { return true; } // Check refPath const refPath = schema.options.refPath; if (refPath == null) { return false; } const modelName = val.get(refPath); return modelName === model.modelName || modelName === model.baseModelName; })(); let didPopulate = false; if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); val.$__.wasPopulated = { value: unpopulatedValue }; didPopulate = true; } let popOpts; const typeKey = this.$__schema.options.typeKey; if (schema.options && Array.isArray(schema.options[typeKey]) && schema.options[typeKey].length && schema.options[typeKey][0] && schema.options[typeKey][0].ref && _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { popOpts = { [populateModelSymbol]: val[0].constructor }; this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); for (const doc of val) { doc.$__.wasPopulated = { value: doc._doc._id }; } didPopulate = true; } if (!refMatches || !schema.$isSingleNested || !val.$__) { // If this path is underneath a single nested schema, we'll call the setter // later in `$__set()` because we don't take `_doc` when we iterate through // a single nested doc. That's to make sure we get the correct context. // Otherwise we would double-call the setter, see gh-7196. let setterContext = this; if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); } if (options?.overwriteImmutable) { val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); } else { val = schema.applySetters(val, setterContext, false, priorVal, { path }); } } if (Array.isArray(val) && !Array.isArray(schema) && schema.$isMongooseDocumentArray && val.length !== 0 && val[0]?.$__?.populated != null) { const populatedPaths = Object.keys(val[0].$__.populated); for (const populatedPath of populatedPaths) { this.$populated(path + '.' + populatedPath, val.map(v => v.$populated(populatedPath)), val[0].$__.populated[populatedPath].options); } didPopulate = true; } if (!didPopulate && this.$__.populated) { // If this array partially contains populated documents, convert them // all to ObjectIds re: #8443 if (Array.isArray(val) && this.$__.populated[path]) { for (let i = 0; i < val.length; ++i) { if (val[i] instanceof Document) { val.set(i, val[i]._doc._id, true); } } } delete this.$__.populated[path]; } if (val != null && schema.$isSingleNested) { _checkImmutableSubpaths(val, schema, priorVal); } this.$markValid(path); } catch (e) { if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { this.invalidate(path, e); } else if (e instanceof MongooseError.CastError) { this.invalidate(e.path, e); if (e.$originalErrorPath) { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); } } else { this.invalidate(path, new MongooseError.CastError(schema.instance, val, path, e)); } shouldSet = false; } if (shouldSet) { let savedState = null; let savedStatePath = null; if (!constructing) { const doc = this.$isSubdocument ? this.ownerDocument() : this; savedState = doc.$__.savedState; savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; doc.$__saveInitialState(savedStatePath); } this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); const isInTransaction = !!this.$__.session?.transaction; const isModifiedWithinTransaction = this.$__.session && this.$__.session[sessionNewDocuments] && this.$__.session[sessionNewDocuments].has(this) && this.$__.session[sessionNewDocuments].get(this).modifiedPaths && !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); if (savedState != null && Object.hasOwn(savedState, savedStatePath) && (!isInTransaction || isModifiedWithinTransaction) && utils.deepEqual(val, savedState[savedStatePath])) { this.unmarkModified(path); } } if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { cleanModifiedSubpaths(this, path); } else if (schema.$isSchemaMap && val == null) { cleanModifiedSubpaths(this, path); } return this; }
$ ShouldModifyfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { if (options?._skipMarkModified) { return false; } if (this.$isNew) { return true; } // Is path already modified? If so, always modify. We may unmark modified later. if (path in this.$__.activePaths.getStatePaths('modify')) { return true; } if (val === void 0 && !this.$__isSelected(path)) { // when a path is not selected in a query, its initial // value will be undefined. return true; } if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { // we're just unsetting the default value which was never saved return false; } // gh-3992: if setting a populated field to a doc, don't mark modified // if they have the same _id if (this.$populated(path) && val instanceof Document && deepEqual(val._doc._id, priorVal)) { return false; } if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { return true; } if (!constructing && val !== null && val !== undefined && path in this.$__.activePaths.getStatePaths('default') && deepEqual(val, schema.getDefault(this, constructing))) { // a path with a default was $unset on the server // and the user is setting it to the same value again return true; } return false; }
$ Setfunction(pathToMark, path, options, constructing, parts, schema, val, priorVal) { Embedded = Embedded || require('./types/arraySubdocument'); const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); if (shouldModify) { if (this.$__.primitiveAtomics?.[path]) { delete this.$__.primitiveAtomics[path]; if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { delete this.$__.primitiveAtomics; } } this.markModified(pathToMark); // handle directly setting arrays (gh-1126) MongooseArray || (MongooseArray = require('./types/array')); if (val && utils.isMongooseArray(val)) { val._registerAtomic('$set', val); // Update embedded document parent references (gh-5189) if (utils.isMongooseDocumentArray(val)) { val.forEach(function(item) { item && item.__parentArray && (item.__parentArray = val); }); } } } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; if (utils.isMongooseDocumentArray(val)) { val.forEach(doc => { if (doc != null) { doc.$isNew = false; } }); } } let obj = this._doc; let i = 0; const l = parts.length; let cur = ''; for (; i < l; i++) { const next = i + 1; const last = next === l; cur += (cur ? '.' + parts[i] : parts[i]); if (specialProperties.has(parts[i])) { continue; } if (last) { if (obj instanceof Map) { obj.set(parts[i], val); } else if (obj.$isSingleNested) { if (!(parts[i] in obj)) { obj[parts[i]] = val; obj._doc[parts[i]] = val; } else { obj._doc[parts[i]] = val; } if (shouldModify) { obj.markModified(parts[i]); } } else { obj[parts[i]] = val; } } else { const isMap = obj instanceof Map; let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; if (utils.isPOJO(value)) { obj = value; } else if (value && value instanceof Embedded) { obj = value; } else if (value && !Array.isArray(value) && value.$isSingleNested) { obj = value; } else if (value && Array.isArray(value)) { obj = value; } else if (value == null) { value = {}; if (isMap) { obj.set(parts[i], value); } else { obj[parts[i]] = value; } obj = value; } else { obj = value; } } } }
$ GetValuefunction(path) { if (typeof path !== 'string' && !Array.isArray(path)) { throw new TypeError( `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` ); } return utils.getValue(path, this._doc); }
$Incfunction $inc(path, val) { if (val == null) { val = 1; } if (Array.isArray(path)) { path.forEach((p) => this.$inc(p, val)); return this; } const schemaType = this.$__path(path); if (schemaType == null) { if (this.$__.strictMode === 'throw') { throw new StrictModeError(path); } else if (this.$__.strictMode === true) { return this; } } else if (schemaType.instance !== 'Number') { this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); return this; } const currentValue = this.$__getValue(path) || 0; let shouldSet = false; let valToSet = null; let valToInc = val; try { val = schemaType.cast(val); valToSet = schemaType.applySetters(currentValue + val, this); valToInc = valToSet - currentValue; shouldSet = true; } catch (err) { this.invalidate(path, new MongooseError.CastError('number', val, path, err)); } if (shouldSet) { this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; if (this.$__.primitiveAtomics[path] == null) { this.$__.primitiveAtomics[path] = { $inc: valToInc }; } else { this.$__.primitiveAtomics[path].$inc += valToInc; } this.markModified(path); this.$__setValue(path, valToSet); } return this; }
$ SetValuefunction(path, val) { utils.setValue(path, val, this._doc); return this; }
Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$Getfunction(path, type, options) { let adhoc; if (options == null) { options = {}; } if (type) { adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); } const noDottedPath = options.noDottedPath; // Fast path if we know we're just accessing top-level path on the document: // just get the schema path, avoid `$__path()` because that does string manipulation let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); if (schema == null) { schema = this.$__schema.virtualpath(path); if (schema != null) { return schema.applyGetters(void 0, this); } } if (noDottedPath) { let obj = this._doc[path]; if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { return schema.applyGetters(obj, this); } return obj; } if (schema?.instance === 'Mixed') { const virtual = this.$__schema.virtualpath(path); if (virtual != null) { schema = virtual; } } const hasDot = path.indexOf('.') !== -1; let obj = this._doc; const pieces = hasDot ? path.split('.') : [path]; // Might need to change path for top-level alias if (typeof this.$__schema.aliases[pieces[0]] === 'string') { pieces[0] = this.$__schema.aliases[pieces[0]]; } for (let i = 0, l = pieces.length; i < l; i++) { if (obj?._doc) { obj = obj._doc; } if (obj == null) { obj = void 0; } else if (obj instanceof Map) { obj = obj.get(pieces[i], { getters: false }); } else if (i === l - 1) { obj = utils.getValue(pieces[i], obj); } else { obj = obj[pieces[i]]; } } if (adhoc) { obj = adhoc.cast(obj); } if (schema != null && options.getters !== false) { obj = schema.applyGetters(obj, this); } else if (this.$__schema.nested[path] && options.virtuals) { // Might need to apply virtuals if this is a nested path return applyVirtuals(this, clone(obj) || {}, { path: path }); } return obj; }
$ Pathfunction(path) { const adhocs = this.$__.adhocPaths; const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; if (adhocType) { return adhocType; } return this.$__schema.path(path); }
$ SaveInitialStatefunction $__saveInitialState(path) { const savedState = this.$__.savedState; const savedStatePath = path; if (savedState != null) { const firstDot = savedStatePath.indexOf('.'); const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); if (!Object.hasOwn(savedState, topLevelPath)) { savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); } } }
UnmarkModifiedfunction(path) { this.$__.activePaths.init(path); if (this.$__.pathsToScopes != null) { delete this.$__.pathsToScopes[path]; } }
DirectModifiedPathsfunction() { return Object.keys(this.$__.activePaths.getStatePaths('modify')); }
$IsEmptyfunction(path) { const isEmptyOptions = { minimize: true, virtuals: false, getters: false, transform: false }; if (arguments.length !== 0) { const v = this.$get(path); if (v == null) { return true; } if (typeof v !== 'object') { return false; } if (utils.isPOJO(v)) { return _isEmpty(v); } return Object.keys(v.toObject(isEmptyOptions)).length === 0; } return Object.keys(this.toObject(isEmptyOptions)).length === 0; }
ModifiedPathsfunction(options) { options = options || {}; const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); const result = new Set(); let i = 0; let j = 0; const len = directModifiedPaths.length; for (i = 0; i < len; ++i) { const path = directModifiedPaths[i]; const parts = parentPaths(path); const pLen = parts.length; for (j = 0; j < pLen; ++j) { result.add(parts[j]); } if (!options.includeChildren) { continue; } let ii = 0; let cur = this.$get(path); if (typeof cur === 'object' && cur !== null) { if (cur._doc) { cur = cur._doc; } const len = cur.length; if (Array.isArray(cur)) { for (ii = 0; ii < len; ++ii) { const subPath = path + '.' + ii; if (!result.has(subPath)) { result.add(subPath); if (cur[ii] != null && cur[ii].$__) { const modified = cur[ii].modifiedPaths(); let iii = 0; const iiiLen = modified.length; for (iii = 0; iii < iiiLen; ++iii) { result.add(subPath + '.' + modified[iii]); } } } } } else { const keys = Object.keys(cur); let ii = 0; const len = keys.length; for (ii = 0; ii < len; ++ii) { result.add(path + '.' + keys[ii]); } } } } return Array.from(result); }
$IsModifiedfunction(paths, options, modifiedPaths) { if (paths) { const ignoreAtomics = options?.ignoreAtomics; const directModifiedPathsObj = this.$__.activePaths.states.modify; if (directModifiedPathsObj == null) { return false; } if (typeof paths === 'string') { paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); } for (const path of paths) { if (directModifiedPathsObj[path] != null) { return true; } } const modified = modifiedPaths || this[documentModifiedPaths](); const isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); let directModifiedPaths = Object.keys(directModifiedPathsObj); if (ignoreAtomics) { directModifiedPaths = directModifiedPaths.filter(path => { const value = this.$__getValue(path); if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { return false; } return true; }); } return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.startsWith(mod + '.'); }); }); } return this.$__.activePaths.some('modify'); }
$IsDefaultfunction(path) { if (path == null) { return this.$__.activePaths.some('default'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); }
$IsDeletedfunction(val) { if (arguments.length === 0) { return !!this.$__.isDeleted; } this.$__.isDeleted = !!val; return this; }
IsDirectModifiedfunction(path) { if (path == null) { return this.$__.activePaths.some('modify'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); if (res || path.indexOf('.') === -1) { return res; } const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { return true; } } return false; } let paths = path; if (typeof paths === 'string') { paths = paths.split(' '); } return paths.some(path => this.isDirectModified(path)); }
IsInitfunction(path) { if (path == null) { return this.$__.activePaths.some('init'); } if (typeof path === 'string' && path.indexOf(' ') === -1) { return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); } let paths = path; if (!Array.isArray(paths)) { paths = paths.split(' '); } return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); }
IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
$ IsSelectedfunction isSelected(path) { if (this.$__.selected == null) { return true; } if (!path) { return false; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.$__isSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } const pathDot = path + '.'; for (const cur of paths) { if (cur === '_id') { continue; } if (cur.startsWith(pathDot)) { return inclusive || cur !== pathDot; } if (pathDot.startsWith(cur + '.')) { return inclusive; } } return !inclusive; }
IsDirectSelectedfunction isDirectSelected(path) { if (this.$__.selected == null) { return true; } if (path === '_id') { return this.$__.selected._id !== 0; } if (path.indexOf(' ') !== -1) { path = path.split(' '); } if (Array.isArray(path)) { return path.some(p => this.isDirectSelected(p)); } const paths = Object.keys(this.$__.selected); let inclusive = null; if (paths.length === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } for (const cur of paths) { if (cur === '_id') { continue; } if (!isDefiningProjection(this.$__.selected[cur])) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (Object.hasOwn(this.$__.selected, path)) { return inclusive; } return !inclusive; }
Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
$Validateasync function validate(pathsToValidate, options) { if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); } this.$op = 'validate'; if (arguments.length === 1) { if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } } if (options && typeof options.pathsToSkip === 'string') { const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); } const _skipParallelValidateCheck = options?._skipParallelValidateCheck; if (this.$isSubdocument != null) { // Skip parallel validate check for subdocuments } else if (this.$__.validating && !_skipParallelValidateCheck) { throw new ParallelValidateError(this); } else if (!_skipParallelValidateCheck) { this.$__.validating = true; } try { await this.$__validate(pathsToValidate, options); } finally { this.$op = null; this.$__.validating = null; } }
ExecDocumentPreHooksasync function _execDocumentPreHooks(opName, ...args) { return this.$__middleware.execPre(opName, this, [...args]); }
ExecDocumentPostHooksasync function _execDocumentPostHooks(opName, error) { return this.$__middleware.execPost(opName, this, [this], { error }); }
$ Validateasync function $__validate(pathsToValidate, options) { try { [options] = await this._execDocumentPreHooks('validate', options); } catch (error) { await this._execDocumentPostHooks('validate', error); return; } if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { pathsToValidate = [...this.$__.saveOptions.pathsToSave]; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); const pathsToSkip = options?.pathsToSkip || null; let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); } } const _this = this; const _complete = () => { let validationError = this.$__.validationError; this.$__.validationError = null; this.$__.validating = null; if (shouldValidateModifiedOnly && validationError != null) { // Remove any validation errors that aren't from modified paths const errors = Object.keys(validationError.errors); for (const errPath of errors) { if (!this.$isModified(errPath)) { delete validationError.errors[errPath]; } } if (utils.hasOwnKeys(validationError.errors) === false) { validationError = void 0; } } this.$__.cachedRequired = {}; this.$emit('validate', _this); this.constructor.emit('validate', _this); if (validationError) { for (const key in validationError.errors) { // Make sure cast errors persist if (!this[documentArrayParent] && validationError.errors[key] instanceof MongooseError.CastError) { this.invalidate(key, validationError.errors[key]); } } return validationError; } }; // only validate required fields when necessary let paths; let doValidateOptionsByPath; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; doValidateOptionsByPath = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; doValidateOptionsByPath = pathDetails[1]; } if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } if (paths.length === 0) { const error = _complete(); await this._execDocumentPostHooks('validate', error); return; } const validated = {}; let pathsToSave = this.$__.saveOptions?.pathsToSave; const promises = []; if (Array.isArray(pathsToSave)) { pathsToSave = new Set(pathsToSave); for (const path of paths) { if (!pathsToSave.has(path)) { continue; } promises.push(validatePath(path)); } } else { for (const path of paths) { promises.push(validatePath(path)); } } await Promise.all(promises); const error = _complete(); await this._execDocumentPostHooks('validate', error); async function validatePath(path) { if (path == null || validated[path]) { return; } validated[path] = true; const schemaType = _this.$__schema.path(path); if (!schemaType) { return; } // If user marked as invalid or there was a cast error, don't validate if (!_this.$isValid(path)) { return; } // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { return; } let val = _this.$__getValue(path); // If you `populate()` and get back a null value, required validators // shouldn't fail (gh-8018). We should always fall back to the populated // value. let pop; if ((pop = _this.$populated(path))) { val = pop; } else if (val?.$__?.wasPopulated) { // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, // so in that case pull out the document's id val = val._doc._id; } const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? _this.$__.pathsToScopes[path] : _this; const doValidateOptions = { ...doValidateOptionsByPath[path], path: path, validateAllPaths, _nestedValidate: true }; try { await schemaType.doValidate(val, scope, doValidateOptions); } catch (err) { const isSubdoc = schemaType.$isSingleNested || schemaType.$isArraySubdocument || schemaType.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { return; } _this.invalidate(path, err, undefined, true); } } }
ValidateSyncfunction(pathsToValidate, options) { const _this = this; if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { options = arguments[0]; pathsToValidate = null; } const hasValidateModifiedOnlyOption = options && (typeof options === 'object') && ('validateModifiedOnly' in options); let shouldValidateModifiedOnly; if (hasValidateModifiedOnlyOption) { shouldValidateModifiedOnly = !!options.validateModifiedOnly; } else { shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; } let pathsToSkip = options?.pathsToSkip; const validateAllPaths = options?.validateAllPaths; if (validateAllPaths) { if (pathsToSkip) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); } if (pathsToValidate) { throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); } } if (typeof pathsToValidate === 'string') { const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { pathsToSkip = pathsToSkip.split(' '); } // only validate required fields when necessary let paths; let skipSchemaValidators; if (validateAllPaths) { paths = new Set(Object.keys(this.$__schema.paths)); // gh-661: if a whole array is modified, make sure to run validation on all // the children as well for (const path of paths) { const schemaType = this.$__schema.path(path); if (!schemaType?.$isMongooseArray) { continue; } const val = this.$__getValue(path); if (!val) { continue; } _pushNestedArrayPaths(val, paths, path); } paths = [...paths]; skipSchemaValidators = {}; } else { const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); paths = shouldValidateModifiedOnly ? pathDetails[0].filter((path) => this.$isModified(path)) : pathDetails[0]; skipSchemaValidators = pathDetails[1]; } const validating = {}; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; if (validating[path]) { continue; } validating[path] = true; const p = _this.$__schema.path(path); if (!p) { continue; } if (!_this.$isValid(path)) { continue; } const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], path: path, validateModifiedOnly: shouldValidateModifiedOnly, validateAllPaths }); if (err) { const isSubdoc = p.$isSingleNested || p.$isArraySubdocument || p.$isMongooseDocumentArray; if (isSubdoc && err instanceof ValidationError) { continue; } _this.invalidate(path, err, undefined, true); } } const err = _this.$__.validationError; _this.$__.validationError = undefined; _this.$emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (const key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
$ Resetfunction reset() { let _this = this; // Skip for subdocuments const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; if (subdocs?.length > 0) { for (const subdoc of subdocs) { subdoc.$__reset(); } } // clear atomics this.$__dirty().forEach(function(dirt) { const type = dirt.value; if (type && typeof type.clearAtomics === 'function') { type.clearAtomics(); } else if (type && type[arrayAtomicsSymbol]) { type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; type[arrayAtomicsSymbol] = {}; } }); this.$__.backup = {}; this.$__.backup.activePaths = { modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) }; this.$__.backup.validationError = this.$__.validationError; this.$__.backup.errors = this.$errors; // Clear 'dirty' cache this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('default'); this.$__.validationError = undefined; this.$errors = undefined; _this = this; this.$__schema.requiredPaths().forEach(function(path) { _this.$__.activePaths.require(path); }); return this; }
$ UndoResetfunction $__undoReset() { if (this.$__.backup?.activePaths == null) { return; } this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; this.$__.activePaths.states.default = this.$__.backup.activePaths.default; this.$__.validationError = this.$__.backup.validationError; this.$errors = this.$__.backup.errors; for (const dirt of this.$__dirty()) { const type = dirt.value; if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; } } if (!this.$isSubdocument) { for (const subdoc of this.$getAllSubdocs()) { subdoc.$__undoReset(); } } }
$ Dirtyfunction() { const _this = this; let all = this.$__.activePaths.map('modify', function(path) { return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; }); // gh-2558: if we had to set a default and the value is not undefined, // we have to save as well all = all.concat(this.$__.activePaths.map('default', function(path) { if (path === '_id' || _this.$__getValue(path) == null) { return; } return { path: path, value: _this.$__getValue(path), schema: _this.$__path(path) }; })); const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); // Ignore "foo.a" if "foo" is dirty already. const minimal = []; all.forEach(function(item) { if (!item) { return; } let top = null; const array = parentPaths(item.path); for (let i = 0; i < array.length - 1; i++) { if (allPaths.has(array[i])) { top = allPaths.get(array[i]); break; } } if (top == null) { minimal.push(item); } else if (top != null && top[arrayAtomicsSymbol] != null && top.hasAtomics()) { // special case for top level MongooseArrays // the `top` array itself and a sub path of `top` are being set. // the only way to honor all of both modifications is through a $set // of entire array. top[arrayAtomicsSymbol] = {}; top[arrayAtomicsSymbol].$set = top; } }); return minimal; }
$ SetSchemafunction(schema) { compile(schema.tree, this, undefined, schema.options); // Apply default getters if virtual doesn't have any (gh-6262) for (const key of Object.keys(schema.virtuals)) { schema.virtuals[key]._applyDefaultGetters(); } if (schema.path('schema') == null) { this.schema = schema; } this.$__schema = schema; this.$__middleware = schema._getDocumentMiddleware(); this[documentSchemaSymbol] = schema; }
$ GetArrayPathsToValidatefunction() { DocumentArray || (DocumentArray = require('./types/documentArray')); // validate all document arrays. return this.$__.activePaths .map('init', 'modify', function(i) { return this.$__getValue(i); }.bind(this)) .filter(function(val) { return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; }).reduce(function(seed, array) { return seed.concat(array); }, []) .filter(function(doc) { return doc; }); }
$GetAllSubdocsfunction(options) { if (options?.useCache && this.$__.saveOptions?.__subdocs) { return this.$__.saveOptions.__subdocs; } DocumentArray || (DocumentArray = require('./types/documentArray')); Embedded = Embedded || require('./types/arraySubdocument'); const subDocs = []; function getSubdocs(doc) { const newSubdocs = []; for (const { model } of doc.$__schema.childSchemas) { // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose const val = doc.$__getValue(model.path); if (val == null) { continue; } if (val.$__) { newSubdocs.push(val); } if (Array.isArray(val)) { for (const el of val) { if (el?.$__) { newSubdocs.push(el); } } } if (val instanceof Map) { for (const el of val.values()) { if (el?.$__) { newSubdocs.push(el); } } } } for (const subdoc of newSubdocs) { getSubdocs(subdoc); } subDocs.push(...newSubdocs); } getSubdocs(this); if (this.$__.saveOptions) { this.$__.saveOptions.__subdocs = subDocs; } return subDocs; }
$ HandleRejectfunction handleReject(err) { // emit on the Model if listening if (this.$listeners('error').length) { this.$emit('error', err); } else if (this.constructor.listeners?.('error').length) { this.constructor.emit('error', err); } }
$ ToObjectShallowfunction $__toObjectShallow(schemaFieldsOnly) { const ret = {}; if (this._doc != null) { const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); for (const key of keys) { // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths const value = this._doc[key]; if (value instanceof Date) { ret[key] = new Date(value); } else if (value !== undefined) { ret[key] = value; } } } return ret; }
ToObjectfunction(options) { return this.$toObject(options); }
ToJSONfunction(options) { return this.$toObject(options, true); }
ToStringfunction() { const ret = this.inspect(); if (typeof ret === 'string') { return ret; } return inspect(ret); }
Equalsfunction(doc) { if (!doc) { return false; } const tid = this.$__getValue('_id'); const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
$GetPopulatedDocsfunction $getPopulatedDocs() { let keys = []; if (this.$__.populated != null) { keys = keys.concat(Object.keys(this.$__.populated)); } let result = []; for (const key of keys) { const value = this.$get(key); if (Array.isArray(value)) { result = result.concat(value); } else if (value instanceof Document) { result.push(value); } } return result; }
Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$Populatedfunction(path, val, options) { // val and options are internal if (val == null || val === true) { if (!this.$__.populated) { return undefined; } if (typeof path !== 'string') { return undefined; } // Map paths can be populated with either `path.$*` or just `path` const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; const v = this.$__.populated[_path]; if (v) { return val === true ? v : v.value; } return undefined; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = { value: val, options: options }; // If this was a nested populate, make sure each populated doc knows // about its populated children (gh-7685) const pieces = path.split('.'); for (let i = 0; i < pieces.length - 1; ++i) { const subpath = pieces.slice(0, i + 1).join('.'); const subdoc = this.$get(subpath); if (subdoc?.$__ != null && this.$populated(subpath)) { const rest = pieces.slice(i + 1).join('.'); subdoc.$populated(rest, val, options); // No need to continue because the above recursion should take care of // marking the rest of the docs as populated break; } } return val; }
$AssertPopulatedfunction $assertPopulated(path, values) { if (Array.isArray(path)) { path.forEach(p => this.$assertPopulated(p, values)); return this; } if (arguments.length > 1) { this.$set(values); } if (!this.$populated(path)) { throw new MongooseError(`Expected path "${path}" to be populated`); } return this; }
Depopulatefunction(path) { if (typeof path === 'string') { path = path.indexOf(' ') === -1 ? [path] : path.split(' '); } let populatedIds; const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; const populated = this.$__?.populated || {}; if (arguments.length === 0) { // Depopulate all for (const virtualKey of virtualKeys) { delete this.$$populatedVirtuals[virtualKey]; delete this._doc[virtualKey]; delete populated[virtualKey]; } const keys = Object.keys(populated); for (const key of keys) { populatedIds = this.$populated(key); if (!populatedIds) { continue; } delete populated[key]; if (Array.isArray(populatedIds)) { const arr = utils.getValue(key, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(key, populatedIds, this._doc); } } else { utils.setValue(key, populatedIds, this._doc); } } return this; } for (const singlePath of path) { populatedIds = this.$populated(singlePath); delete populated[singlePath]; if (virtualKeys.indexOf(singlePath) !== -1) { delete this.$$populatedVirtuals[singlePath]; delete this._doc[singlePath]; } else if (populatedIds) { if (Array.isArray(populatedIds)) { const arr = utils.getValue(singlePath, this._doc); if (arr.isMongooseArray) { const rawArray = arr.__array; for (let i = 0; i < rawArray.length; ++i) { const subdoc = rawArray[i]; if (subdoc == null) { continue; } rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; } } else { utils.setValue(singlePath, populatedIds, this._doc); } } else { utils.setValue(singlePath, populatedIds, this._doc); } } } return this; }
GetChangesfunction() { const delta = this.$__delta(); const changes = delta ? delta[1] : {}; return changes; }
$ Deltafunction $__delta() { const dirty = this.$__dirty(); const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; if (optimisticConcurrency) { if (Array.isArray(optimisticConcurrency)) { const optCon = new Set(optimisticConcurrency); const modPaths = this.modifiedPaths(); if (modPaths.some(path => optCon.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else if (Array.isArray(optimisticConcurrency?.exclude)) { const excluded = new Set(optimisticConcurrency.exclude); const modPaths = this.modifiedPaths(); if (modPaths.some(path => !excluded.has(path))) { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } else { this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; } } if (!dirty.length && VERSION_ALL !== this.$__.version) { return; } const where = {}; const delta = {}; const len = dirty.length; const divergent = []; let d = 0; where._id = this._doc._id; // If `_id` is an object, need to depopulate, but also need to be careful // because `_id` can technically be null (see gh-6406) if (where?._id?.$__ != null) { where._id = where._id.toObject({ transform: false, depopulate: true }); } for (; d < len; ++d) { const data = dirty[d]; let value = data.value; const match = checkDivergentArray(this, data.path, value); if (match) { divergent.push(match); continue; } const pop = this.$populated(data.path, true); if (!pop && this.$__.selected) { // If any array was selected using an $elemMatch projection, we alter the path and where clause // NOTE: MongoDB only supports projected $elemMatch on top level array. const pathSplit = data.path.split('.'); const top = pathSplit[0]; if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { // If the selected array entry was modified if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { where[top] = this.$__.selected[top]; pathSplit[1] = '$'; data.path = pathSplit.join('.'); } // if the selected array was modified in any other way throw an error else { divergent.push(data.path); continue; } } } // If this path is set to default, and either this path or one of // its parents is excluded, don't treat this path as dirty. if (this.$isDefault(data.path) && this.$__.selected) { if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { continue; } const pathsToCheck = parentPaths(data.path); if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { continue; } } if (divergent.length) continue; if (value === undefined) { operand(this, where, delta, data, 1, '$unset'); } else if (value === null) { operand(this, where, delta, data, null); } else if (typeof value.getAtomics === 'function') { // arrays and other custom container types handleAtomics(this, where, delta, data, value); } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { // MongooseBuffer value = value.toObject(); operand(this, where, delta, data, value); } else { if (this.$__.primitiveAtomics?.[data.path] != null) { const val = this.$__.primitiveAtomics[data.path]; const op = firstKey(val); operand(this, where, delta, data, val[op], op); } else { value = clone(value, { depopulate: true, transform: false, virtuals: false, getters: false, omitUndefined: true, _isNested: true }); operand(this, where, delta, data, value); } } } if (divergent.length) { throw new DivergentArrayError(divergent); } if (this.$__.version) { this.$__version(where, delta); } if (utils.hasOwnKeys(delta) === false) { return [where, null]; } return [where, delta]; }
$Clonefunction() { const Model = this.constructor; const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); } if (this.$__) { const Cache = this.$__.constructor; const clonedCache = new Cache(); for (const key of Object.getOwnPropertyNames(this.$__)) { if (key === 'activePaths') { continue; } clonedCache[key] = clone(this.$__[key]); } Object.assign( clonedCache.activePaths, clone({ ...this.$__.activePaths }) ); clonedDoc.$__ = clonedCache; } return clonedDoc; }
$CreateModifiedPathsSnapshotfunction $createModifiedPathsSnapshot() { const subdocSnapshot = new WeakMap(); if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { subdocSnapshot.set(child, child.$__.activePaths.clone()); } } return new ModifiedPathsSnapshot( subdocSnapshot, this.$__.activePaths.clone(), this.$__.version ); }
$RestoreModifiedPathsSnapshotfunction $restoreModifiedPathsSnapshot(snapshot) { this.$__.activePaths = snapshot.activePaths.clone(); this.$__.version = snapshot.version; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { if (snapshot.subdocSnapshot.has(child)) { child.$__.activePaths = snapshot.subdocSnapshot.get(child); } } } return this; }
$ClearModifiedPathsfunction $clearModifiedPaths() { this.$__.activePaths.clear('modify'); this.$__.activePaths.clear('init'); this.$__.version = 0; if (!this.$isSubdocument) { const subdocs = this.$getAllSubdocs(); for (const child of subdocs) { child.$clearModifiedPaths(); } } return this; }
$ HasOnlyPrimitiveValuesfunction $__hasOnlyPrimitiveValues() { return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { return v == null || typeof v !== 'object' || (utils.isNativeObject(v) && !Array.isArray(v)) || isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128'); })); }
ApplyVersionIncrementfunction _applyVersionIncrement() { if (!this.$__.version) return; const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); this.$__.version = undefined; if (doIncrement) { const key = this.$__schema.options.versionKey; const version = this.$__getValue(key) || 0; this.$__setValue(key, version + 1); // increment version if was successful } }

Të interesuar për 2026 EV 1036km Flagship?

Merrni çmime të personalizuara dhe rezervoni një test drive me ekipin tonë ekspert

Na Telefononi

+355699414270

Na Shkruani Email

[email protected]

Vizitoni Showroom-in

Rruga Siri Kodra, Tiranë, Albania