mongoose 9.3.3 → 9.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/connection.js CHANGED
@@ -1005,10 +1005,7 @@ Connection.prototype.error = function error(err, callback) {
1005
1005
  Connection.prototype.onOpen = function() {
1006
1006
  this.readyState = STATES.connected;
1007
1007
 
1008
- for (const d of this._queue) {
1009
- d.fn.apply(d.ctx, d.args);
1010
- }
1011
- this._queue = [];
1008
+ this._flushQueue();
1012
1009
 
1013
1010
  // avoid having the collection subscribe to our event emitter
1014
1011
  // to prevent 0.3 warning
@@ -1021,6 +1018,21 @@ Connection.prototype.onOpen = function() {
1021
1018
  this.emit('open');
1022
1019
  };
1023
1020
 
1021
+ /**
1022
+ * Flush all buffered operations in `_queue`. Called by `onOpen()` and
1023
+ * by the heartbeat handler when a previously-stale connection recovers.
1024
+ *
1025
+ * @api private
1026
+ */
1027
+
1028
+ Connection.prototype._flushQueue = function _flushQueue() {
1029
+ const queue = this._queue;
1030
+ this._queue = [];
1031
+ for (const d of queue) {
1032
+ d.fn.apply(d.ctx, d.args);
1033
+ }
1034
+ };
1035
+
1024
1036
  /**
1025
1037
  * Opens the connection with a URI using `MongoClient.connect()`.
1026
1038
  *
@@ -1033,11 +1045,11 @@ Connection.prototype.onOpen = function() {
1033
1045
  * @param {string} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
1034
1046
  * @param {number} [options.maxPoolSize=100] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
1035
1047
  * @param {number} [options.minPoolSize=0] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
1036
- * @param {number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds).
1037
- * @param {number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
1048
+ * @param {number} [options.serverSelectionTimeoutMS] The MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds).
1049
+ * @param {number} [options.heartbeatFrequencyMS] The MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
1038
1050
  * @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
1039
1051
  * @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
1040
- * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
1052
+ * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
1041
1053
  * @param {boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection.
1042
1054
  * @returns {Promise<Connection>}
1043
1055
  * @api public
package/lib/document.js CHANGED
@@ -2981,9 +2981,40 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
2981
2981
  // Skip $* paths - they represent map schemas, not actual document paths
2982
2982
  return;
2983
2983
  }
2984
+
2985
+ const _pathType = doc.$__schema.path(p);
2986
+
2987
+ // Optimization: if primitive path with no validators, or array of primitives
2988
+ // with no validators, skip validating this path entirely.
2989
+ // Note: paths with no _pathType (e.g. sub-paths under Mixed) must still be
2990
+ // added, as they trigger validation of the parent Mixed path.
2991
+ if (_pathType) {
2992
+ if (!_pathType.schema &&
2993
+ !_pathType.embeddedSchemaType &&
2994
+ _pathType.validators.length === 0 &&
2995
+ !_pathType.$parentSchemaDocArray &&
2996
+ // gh-15957: skip this optimization for SchemaMap as maps can contain subdocuments
2997
+ // that need validation even if the map itself has no validators
2998
+ !_pathType.$isSchemaMap &&
2999
+ !_pathType.$isSchemaUnion) {
3000
+ return;
3001
+ } else if (_pathType.$isMongooseArray &&
3002
+ !_pathType.$isMongooseDocumentArray && // Skip document arrays...
3003
+ !_pathType.embeddedSchemaType.$isMongooseArray && // and arrays of arrays
3004
+ _pathType.validators.length === 0 && // and arrays with top-level validators
3005
+ _pathType.embeddedSchemaType.validators.length === 0) {
3006
+ return;
3007
+ }
3008
+ }
3009
+
2984
3010
  paths.add(p);
2985
3011
  }
2986
3012
 
3013
+ const onlyPrimitiveValues = doc.$__hasOnlyPrimitiveValues();
3014
+ if (onlyPrimitiveValues && paths.size === 0) {
3015
+ return [[], doValidateOptions];
3016
+ }
3017
+
2987
3018
  if (!isNestedValidate) {
2988
3019
  // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments.
2989
3020
  // But only run for top-level subdocuments, because we're looking for subdocuments that are not modified at top-level but
@@ -3048,37 +3079,17 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
3048
3079
  }
3049
3080
  }
3050
3081
 
3051
- for (const path of paths) {
3052
- const _pathType = doc.$__schema.path(path);
3053
- if (!_pathType) {
3054
- continue;
3055
- }
3056
-
3057
- if (_pathType.$isMongooseDocumentArray) {
3058
- for (const p of paths) {
3059
- if (p == null || p.startsWith(_pathType.path + '.')) {
3060
- paths.delete(p);
3082
+ if (!onlyPrimitiveValues) {
3083
+ for (const path of paths) {
3084
+ const _pathType = doc.$__schema.path(path);
3085
+ if (_pathType && _pathType.$isMongooseDocumentArray) {
3086
+ for (const p of paths) {
3087
+ if (p == null || p.startsWith(_pathType.path + '.')) {
3088
+ paths.delete(p);
3089
+ }
3061
3090
  }
3062
3091
  }
3063
3092
  }
3064
-
3065
- // Optimization: if primitive path with no validators, or array of primitives
3066
- // with no validators, skip validating this path entirely.
3067
- if (!_pathType.schema &&
3068
- !_pathType.embeddedSchemaType &&
3069
- _pathType.validators.length === 0 &&
3070
- !_pathType.$parentSchemaDocArray &&
3071
- // gh-15957: skip this optimization for SchemaMap as maps can contain subdocuments
3072
- // that need validation even if the map itself has no validators
3073
- !_pathType.$isSchemaMap) {
3074
- paths.delete(path);
3075
- } else if (_pathType.$isMongooseArray &&
3076
- !_pathType.$isMongooseDocumentArray && // Skip document arrays...
3077
- !_pathType.embeddedSchemaType.$isMongooseArray && // and arrays of arrays
3078
- _pathType.validators.length === 0 && // and arrays with top-level validators
3079
- _pathType.embeddedSchemaType.validators.length === 0) {
3080
- paths.delete(path);
3081
- }
3082
3093
  }
3083
3094
 
3084
3095
 
@@ -3595,25 +3606,21 @@ Document.prototype.$isValid = function(path) {
3595
3606
  Document.prototype.$__reset = function reset() {
3596
3607
  let _this = this;
3597
3608
 
3598
- // Skip for subdocuments
3599
- const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null;
3609
+ const onlyPrimitiveValues = this.$__hasOnlyPrimitiveValues();
3610
+
3611
+ // Skip for subdocuments. Also skip if doc only has primitive values,
3612
+ // because primitives can't be subdocs.
3613
+ const subdocs = !this.$isSubdocument && !onlyPrimitiveValues ? this.$getAllSubdocs({ useCache: true }) : null;
3600
3614
  if (subdocs?.length > 0) {
3601
3615
  for (const subdoc of subdocs) {
3602
3616
  subdoc.$__reset();
3603
3617
  }
3604
3618
  }
3605
3619
 
3606
- // clear atomics
3607
- this.$__dirty().forEach(function(dirt) {
3608
- const type = dirt.value;
3609
-
3610
- if (type && typeof type.clearAtomics === 'function') {
3611
- type.clearAtomics();
3612
- } else if (type && type[arrayAtomicsSymbol]) {
3613
- type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol];
3614
- type[arrayAtomicsSymbol] = {};
3615
- }
3616
- });
3620
+ // Clear atomics on dirty paths. Walk the modified and default paths
3621
+ // directly instead of calling $__dirty(), which builds intermediate
3622
+ // arrays, a Map, and does parent-path deduplication we don't need here.
3623
+ this.$__resetAtomics();
3617
3624
 
3618
3625
  this.$__.backup = {};
3619
3626
  this.$__.backup.activePaths = {
@@ -3636,6 +3643,40 @@ Document.prototype.$__reset = function reset() {
3636
3643
  return this;
3637
3644
  };
3638
3645
 
3646
+ /*!
3647
+ * Clear atomics on all dirty (modified + default) paths. This is a lighter
3648
+ * alternative to calling `$__dirty()` when we only need to clear atomics
3649
+ * and don't need the full {path, value, schema} objects or parent-path
3650
+ * deduplication.
3651
+ */
3652
+
3653
+ Document.prototype.$__resetAtomics = function $__resetAtomics() {
3654
+ const activePaths = this.$__.activePaths;
3655
+ const modifyPaths = activePaths.getStatePaths('modify');
3656
+ const defaultPaths = activePaths.getStatePaths('default');
3657
+
3658
+ _clearAtomicsOnPaths(this, modifyPaths);
3659
+ _clearAtomicsOnPaths(this, defaultPaths);
3660
+ };
3661
+
3662
+ function _clearAtomicsOnPaths(doc, paths) {
3663
+ if (paths == null) {
3664
+ return;
3665
+ }
3666
+ const keys = Object.keys(paths);
3667
+ for (let i = 0; i < keys.length; ++i) {
3668
+ const type = doc.$__getValue(keys[i]);
3669
+ if (type != null) {
3670
+ if (typeof type.clearAtomics === 'function') {
3671
+ type.clearAtomics();
3672
+ } else if (type[arrayAtomicsSymbol]) {
3673
+ type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol];
3674
+ type[arrayAtomicsSymbol] = {};
3675
+ }
3676
+ }
3677
+ }
3678
+ }
3679
+
3639
3680
  /*!
3640
3681
  * ignore
3641
3682
  */
@@ -4995,34 +5036,53 @@ Document.prototype.$__fullPath = function(path) {
4995
5036
  * });
4996
5037
  *
4997
5038
  * // returns an empty object, no changes happened yet
4998
- * user.getChanges(); // { }
5039
+ * user.$getChanges(); // { }
4999
5040
  *
5000
5041
  * user.country = undefined;
5001
5042
  * user.age = 26;
5002
5043
  *
5003
- * user.getChanges(); // { $set: { age: 26 }, { $unset: { country: 1 } } }
5044
+ * user.$getChanges(); // { $set: { age: 26 }, { $unset: { country: 1 } } }
5004
5045
  *
5005
5046
  * await user.save();
5006
5047
  *
5007
- * user.getChanges(); // { }
5048
+ * user.$getChanges(); // { }
5008
5049
  *
5009
- * Modifying the object that `getChanges()` returns does not affect the document's
5010
- * change tracking state. Even if you `delete user.getChanges().$set`, Mongoose
5050
+ * Modifying the object that `$getChanges()` returns does not affect the document's
5051
+ * change tracking state. Even if you `delete user.$getChanges().$set`, Mongoose
5011
5052
  * will still send a `$set` to the server.
5012
5053
  *
5013
5054
  * @return {object}
5014
5055
  * @api public
5015
- * @method getChanges
5056
+ * @method $getChanges
5016
5057
  * @memberOf Document
5017
5058
  * @instance
5018
5059
  */
5019
5060
 
5020
- Document.prototype.getChanges = function() {
5061
+ Document.prototype.$getChanges = function() {
5021
5062
  const delta = this.$__delta();
5022
5063
  const changes = delta ? delta[1] : {};
5023
5064
  return changes;
5024
5065
  };
5025
5066
 
5067
+ /**
5068
+ * **Deprecated.** Use `$getChanges()` instead.
5069
+ *
5070
+ * Returns the changes that happened to the document
5071
+ * in the format that will be sent to MongoDB.
5072
+ *
5073
+ * @return {Object}
5074
+ * @deprecated Use `$getChanges()` instead. `getChanges` does not use the `$` prefix convention and may conflict with user-defined schema methods/properties.
5075
+ * @api public
5076
+ * @method getChanges
5077
+ * @memberOf Document
5078
+ * @instance
5079
+ */
5080
+
5081
+ Document.prototype.getChanges = function() {
5082
+ utils.warn('`getChanges()` is deprecated, use `$getChanges()` instead.');
5083
+ return this.$getChanges();
5084
+ };
5085
+
5026
5086
  /**
5027
5087
  * Produces a special query document of the modified properties used in updates.
5028
5088
  *
@@ -89,12 +89,11 @@ NativeCollection.prototype._getCollection = function _getCollection() {
89
89
  function iter(i) {
90
90
  NativeCollection.prototype[i] = function() {
91
91
  const collection = this._getCollection();
92
- const args = Array.from(arguments);
92
+ const args = arguments;
93
93
  const _this = this;
94
94
  const globalDebug = _this?.conn?.base?.options?.debug;
95
95
  const connectionDebug = _this?.conn?.options?.debug;
96
96
  const debug = connectionDebug == null ? globalDebug : connectionDebug;
97
- const opId = new ObjectId();
98
97
 
99
98
  // If user force closed, queueing will hang forever. See #5664
100
99
  if (this.conn.$wasForceClosed) {
@@ -108,9 +107,21 @@ function iter(i) {
108
107
  }
109
108
  }
110
109
 
110
+ // Lazily generate opId and argsArray only if necessary because they have
111
+ // a non-trivial performance cost.
112
+ let opId = null;
113
+ let argsArray = null;
114
+ const hasOperationListeners = this.conn.listenerCount('operation-start') > 0 || this.conn.listenerCount('operation-end') > 0;
115
+ if (hasOperationListeners || debug) {
116
+ opId = new ObjectId();
117
+ }
118
+
111
119
  let timeout = null;
112
120
  let waitForBufferPromise = null;
113
121
  if (this._shouldBufferCommands() && this.buffer) {
122
+ if (opId == null) {
123
+ opId = new ObjectId();
124
+ }
114
125
  this.conn.emit('buffer', {
115
126
  _id: opId,
116
127
  modelName: _this.modelName,
@@ -145,11 +156,14 @@ function iter(i) {
145
156
 
146
157
  if (debug) {
147
158
  if (typeof debug === 'function') {
159
+ if (argsArray == null) {
160
+ argsArray = Array.from(args);
161
+ }
148
162
  let argsToAdd = null;
149
- if (typeof args[args.length - 1] == 'function') {
150
- argsToAdd = args.slice(0, args.length - 1);
163
+ if (typeof argsArray[argsArray.length - 1] == 'function') {
164
+ argsToAdd = argsArray.slice(0, argsArray.length - 1);
151
165
  } else {
152
- argsToAdd = args;
166
+ argsToAdd = argsArray;
153
167
  }
154
168
  debug.apply(_this,
155
169
  [_this.name, i].concat(argsToAdd));
@@ -162,7 +176,12 @@ function iter(i) {
162
176
  }
163
177
  }
164
178
 
165
- this.conn.emit('operation-start', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, params: args });
179
+ if (hasOperationListeners) {
180
+ if (argsArray == null) {
181
+ argsArray = Array.from(args);
182
+ }
183
+ this.conn.emit('operation-start', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, params: argsArray });
184
+ }
166
185
 
167
186
  try {
168
187
  if (collection == null) {
@@ -179,20 +198,26 @@ function iter(i) {
179
198
  if (timeout != null) {
180
199
  clearTimeout(timeout);
181
200
  }
182
- this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result });
201
+ if (hasOperationListeners) {
202
+ this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result });
203
+ }
183
204
  return result;
184
205
  },
185
206
  error => {
186
207
  if (timeout != null) {
187
208
  clearTimeout(timeout);
188
209
  }
189
- this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error });
210
+ if (hasOperationListeners) {
211
+ this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error });
212
+ }
190
213
  throw error;
191
214
  }
192
215
  );
193
216
  }
194
217
 
195
- this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result: ret });
218
+ if (hasOperationListeners) {
219
+ this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result: ret });
220
+ }
196
221
  if (timeout != null) {
197
222
  clearTimeout(timeout);
198
223
  }
@@ -201,7 +226,9 @@ function iter(i) {
201
226
  if (timeout != null) {
202
227
  clearTimeout(timeout);
203
228
  }
204
- this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error: error });
229
+ if (hasOperationListeners) {
230
+ this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error: error });
231
+ }
205
232
  throw error;
206
233
  }
207
234
  };
@@ -14,6 +14,10 @@ const setTimeout = require('../../helpers/timers').setTimeout;
14
14
  const utils = require('../../utils');
15
15
  const Schema = require('../../schema');
16
16
 
17
+ // Snapshot the native Date constructor to ensure Date.now()
18
+ // bypasses timer mocks such as those set up by useFakeTimers().
19
+ const Date = globalThis.Date;
20
+
17
21
  /**
18
22
  * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
19
23
  *
@@ -482,6 +486,15 @@ function _setClient(conn, client, options, dbName) {
482
486
  for (const otherDb of conn.otherDbs) {
483
487
  otherDb._lastHeartbeatAt = conn._lastHeartbeatAt;
484
488
  }
489
+ // Flush buffered operations if the connection is no longer stale (gh-16183)
490
+ if (conn._queue.length > 0 && conn.readyState === STATES.connected) {
491
+ conn._flushQueue();
492
+ }
493
+ for (const otherDb of conn.otherDbs) {
494
+ if (otherDb._queue.length > 0 && otherDb.readyState === STATES.connected) {
495
+ otherDb._flushQueue();
496
+ }
497
+ }
485
498
  });
486
499
 
487
500
  if (options.monitorCommands) {
@@ -32,7 +32,7 @@ const MongooseError = require('./mongooseError');
32
32
  * - `ValidationError`: error returned from [`validate()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) or [`validateSync()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validateSync()). Contains zero or more `ValidatorError` instances in `.errors` property.
33
33
  * - `MissingSchemaError`: You called `mongoose.Document()` without a schema
34
34
  * - `ObjectExpectedError`: Thrown when you set a nested path to a non-object value with [strict mode set](https://mongoosejs.com/docs/guide.html#strict).
35
- * - `ObjectParameterError`: Thrown when you pass a non-object value to a function which expects an object as a paramter
35
+ * - `ObjectParameterError`: Thrown when you pass a non-object value to a function which expects an object as a parameter
36
36
  * - `OverwriteModelError`: Thrown when you call [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.model()) to re-define a model that was already defined.
37
37
  * - `ParallelSaveError`: Thrown when you call [`save()`](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) on a document when the same document instance is already saving.
38
38
  * - `StrictModeError`: Thrown when you set a path that isn't the schema and [strict mode](https://mongoosejs.com/docs/guide.html#strict) is set to `throw`.
@@ -84,7 +84,7 @@ module.exports = function(filter, schema, castedDoc, options, queryMongooseOptio
84
84
  if (schemaType.path === '_id' && schemaType.options.auto) {
85
85
  return;
86
86
  }
87
- const def = schemaType.getDefault(null, true, { context });
87
+ const def = schemaType.getDefault(null, false, { context });
88
88
  if (typeof def === 'undefined') {
89
89
  return;
90
90
  }
package/lib/model.js CHANGED
@@ -189,8 +189,11 @@ Model.prototype.db;
189
189
  */
190
190
 
191
191
  Model.useConnection = function useConnection(connection) {
192
- if (!connection) {
193
- throw new MongooseError('Please provide a connection.');
192
+ if (typeof connection?.model !== 'function' || typeof connection.collection !== 'function' || typeof connection.base?.version !== 'string') {
193
+ throw new MongooseError('`useConnection()` requires a Mongoose connection.');
194
+ }
195
+ if (this.db?.base?.version && this.db?.base?.version !== connection.base?.version) {
196
+ throw new MongooseError(`The connection passed to \`useConnection()\` has a different version of Mongoose (${connection.base?.version}) than the model you are using (${this.db?.base?.version}).`);
194
197
  }
195
198
  if (this.db) {
196
199
  delete this.db.models[this.modelName];
@@ -635,7 +638,7 @@ Model.prototype.save = async function save(options) {
635
638
  if (this.$__.saving) {
636
639
  parallelSave = new ParallelSaveError(this);
637
640
  } else {
638
- this.$__.saving = new ParallelSaveError(this);
641
+ this.$__.saving = true;
639
642
  }
640
643
 
641
644
  options = new SaveOptions(options);
@@ -21,7 +21,6 @@ const isOperator = require('../helpers/query/isOperator');
21
21
  const util = require('util');
22
22
  const utils = require('../utils');
23
23
  const castToNumber = require('./operators/helpers').castToNumber;
24
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
25
24
  const geospatial = require('./operators/geospatial');
26
25
  const getDiscriminatorByValue = require('../helpers/discriminator/getDiscriminatorByValue');
27
26
 
@@ -684,9 +683,8 @@ handle.$in = SchemaType.prototype.$conditionalHandlers.$in;
684
683
 
685
684
  SchemaArray.prototype.toJSONSchema = function toJSONSchema(options) {
686
685
  const embeddedSchemaType = this.getEmbeddedSchemaType();
687
- const isRequired = this.options.required && typeof this.options.required !== 'function';
688
686
  return {
689
- ...createJSONSchemaTypeDefinition('array', 'array', options?.useBsonType, isRequired),
687
+ ...this._createJSONSchemaTypeDefinition('array', 'array', options),
690
688
  items: embeddedSchemaType.toJSONSchema(options)
691
689
  };
692
690
  };
@@ -7,7 +7,6 @@
7
7
  const CastError = require('../error/cast');
8
8
  const SchemaType = require('../schemaType');
9
9
  const castBigInt = require('../cast/bigint');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
 
12
11
  /**
13
12
  * BigInt SchemaType constructor.
@@ -267,8 +266,7 @@ SchemaBigInt.prototype._castNullish = function _castNullish(v) {
267
266
  */
268
267
 
269
268
  SchemaBigInt.prototype.toJSONSchema = function toJSONSchema(options) {
270
- const isRequired = this.options.required && typeof this.options.required !== 'function';
271
- return createJSONSchemaTypeDefinition('string', 'long', options?.useBsonType, isRequired);
269
+ return this._createJSONSchemaTypeDefinition('string', 'long', options);
272
270
  };
273
271
 
274
272
  SchemaBigInt.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -7,7 +7,6 @@
7
7
  const CastError = require('../error/cast');
8
8
  const SchemaType = require('../schemaType');
9
9
  const castBoolean = require('../cast/boolean');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
 
12
11
  /**
13
12
  * Boolean SchemaType constructor.
@@ -317,8 +316,7 @@ SchemaBoolean.prototype._castNullish = function _castNullish(v) {
317
316
  */
318
317
 
319
318
  SchemaBoolean.prototype.toJSONSchema = function toJSONSchema(options) {
320
- const isRequired = this.options.required && typeof this.options.required !== 'function';
321
- return createJSONSchemaTypeDefinition('boolean', 'bool', options?.useBsonType, isRequired);
319
+ return this._createJSONSchemaTypeDefinition('boolean', 'bool', options);
322
320
  };
323
321
 
324
322
  SchemaBoolean.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -7,7 +7,6 @@
7
7
  const MongooseBuffer = require('../types/buffer');
8
8
  const SchemaBufferOptions = require('../options/schemaBufferOptions');
9
9
  const SchemaType = require('../schemaType');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
  const handleBitwiseOperator = require('./operators/bitwise');
12
11
  const utils = require('../utils');
13
12
 
@@ -328,8 +327,7 @@ SchemaBuffer.prototype.castForQuery = function($conditional, val, context) {
328
327
  */
329
328
 
330
329
  SchemaBuffer.prototype.toJSONSchema = function toJSONSchema(options) {
331
- const isRequired = this.options.required && typeof this.options.required !== 'function';
332
- return createJSONSchemaTypeDefinition('string', 'binData', options?.useBsonType, isRequired);
330
+ return this._createJSONSchemaTypeDefinition('string', 'binData', options);
333
331
  };
334
332
 
335
333
  SchemaBuffer.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -8,7 +8,6 @@ const MongooseError = require('../error/index');
8
8
  const SchemaDateOptions = require('../options/schemaDateOptions');
9
9
  const SchemaType = require('../schemaType');
10
10
  const castDate = require('../cast/date');
11
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
12
11
  const getConstructorName = require('../helpers/getConstructorName');
13
12
  const utils = require('../utils');
14
13
 
@@ -452,8 +451,7 @@ SchemaDate.prototype.castForQuery = function($conditional, val, context) {
452
451
  */
453
452
 
454
453
  SchemaDate.prototype.toJSONSchema = function toJSONSchema(options) {
455
- const isRequired = this.options.required && typeof this.options.required !== 'function';
456
- return createJSONSchemaTypeDefinition('string', 'date', options?.useBsonType, isRequired);
454
+ return this._createJSONSchemaTypeDefinition('string', 'date', options);
457
455
  };
458
456
 
459
457
  SchemaDate.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -7,7 +7,6 @@
7
7
  const SchemaType = require('../schemaType');
8
8
  const CastError = SchemaType.CastError;
9
9
  const castDecimal128 = require('../cast/decimal128');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
  const isBsonType = require('../helpers/isBsonType');
12
11
 
13
12
  /**
@@ -248,8 +247,7 @@ Object.defineProperty(SchemaDecimal128.prototype, '$conditionalHandlers', {
248
247
  */
249
248
 
250
249
  SchemaDecimal128.prototype.toJSONSchema = function toJSONSchema(options) {
251
- const isRequired = this.options.required && typeof this.options.required !== 'function';
252
- return createJSONSchemaTypeDefinition('string', 'decimal', options?.useBsonType, isRequired);
250
+ return this._createJSONSchemaTypeDefinition('string', 'decimal', options);
253
251
  };
254
252
 
255
253
  SchemaDecimal128.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -12,7 +12,6 @@ const SchemaDocumentArrayOptions =
12
12
  require('../options/schemaDocumentArrayOptions');
13
13
  const SchemaType = require('../schemaType');
14
14
  const cast = require('../cast');
15
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
16
15
  const discriminator = require('../helpers/model/discriminator');
17
16
  const handleIdOption = require('../helpers/schema/handleIdOption');
18
17
  const handleSpreadDoc = require('../helpers/document/handleSpreadDoc');
@@ -651,10 +650,9 @@ function cast$elemMatch(val, context) {
651
650
  */
652
651
 
653
652
  SchemaDocumentArray.prototype.toJSONSchema = function toJSONSchema(options) {
654
- const itemsTypeDefinition = createJSONSchemaTypeDefinition('object', 'object', options?.useBsonType, false);
655
- const isRequired = this.options.required && typeof this.options.required !== 'function';
653
+ const itemsTypeDefinition = this._createJSONSchemaTypeDefinition('object', 'object', { ...options, _overrideRequired: false });
656
654
  return {
657
- ...createJSONSchemaTypeDefinition('array', 'array', options?.useBsonType, isRequired),
655
+ ...this._createJSONSchemaTypeDefinition('array', 'array', options),
658
656
  items: { ...itemsTypeDefinition, ...this.schema.toJSONSchema(options) }
659
657
  };
660
658
  };
@@ -7,7 +7,6 @@
7
7
  const CastError = require('../error/cast');
8
8
  const SchemaType = require('../schemaType');
9
9
  const castDouble = require('../cast/double');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
 
12
11
  /**
13
12
  * Double SchemaType constructor.
@@ -231,8 +230,7 @@ Object.defineProperty(SchemaDouble.prototype, '$conditionalHandlers', {
231
230
  */
232
231
 
233
232
  SchemaDouble.prototype.toJSONSchema = function toJSONSchema(options) {
234
- const isRequired = this.options.required && typeof this.options.required !== 'function';
235
- return createJSONSchemaTypeDefinition('number', 'double', options?.useBsonType, isRequired);
233
+ return this._createJSONSchemaTypeDefinition('number', 'double', options);
236
234
  };
237
235
 
238
236
  SchemaDouble.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -7,7 +7,6 @@
7
7
  const CastError = require('../error/cast');
8
8
  const SchemaType = require('../schemaType');
9
9
  const castInt32 = require('../cast/int32');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
  const handleBitwiseOperator = require('./operators/bitwise');
12
11
 
13
12
  /**
@@ -273,8 +272,7 @@ SchemaInt32.prototype.castForQuery = function($conditional, val, context) {
273
272
  */
274
273
 
275
274
  SchemaInt32.prototype.toJSONSchema = function toJSONSchema(options) {
276
- const isRequired = this.options.required && typeof this.options.required !== 'function';
277
- return createJSONSchemaTypeDefinition('number', 'int', options?.useBsonType, isRequired);
275
+ return this._createJSONSchemaTypeDefinition('number', 'int', options);
278
276
  };
279
277
 
280
278
  SchemaInt32.prototype.autoEncryptionType = function autoEncryptionType() {
package/lib/schema/map.js CHANGED
@@ -7,7 +7,6 @@
7
7
  const MongooseMap = require('../types/map');
8
8
  const SchemaMapOptions = require('../options/schemaMapOptions');
9
9
  const SchemaType = require('../schemaType');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
  const MongooseError = require('../error/mongooseError');
12
11
  const Schema = require('../schema');
13
12
  const utils = require('../utils');
@@ -125,11 +124,9 @@ class SchemaMap extends SchemaType {
125
124
  */
126
125
 
127
126
  toJSONSchema(options) {
128
- const useBsonType = options?.useBsonType;
129
127
  const embeddedSchemaType = this.getEmbeddedSchemaType();
130
128
 
131
- const isRequired = this.options.required && typeof this.options.required !== 'function';
132
- const result = createJSONSchemaTypeDefinition('object', 'object', useBsonType, isRequired);
129
+ const result = this._createJSONSchemaTypeDefinition('object', 'object', options);
133
130
  result.additionalProperties = embeddedSchemaType.toJSONSchema(options);
134
131
 
135
132
  return result;