mongoose 6.3.7 → 6.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/index.js CHANGED
@@ -8,6 +8,7 @@ require('./driver').set(require('./drivers/node-mongodb-native'));
8
8
 
9
9
  const Document = require('./document');
10
10
  const EventEmitter = require('events').EventEmitter;
11
+ const Kareem = require('kareem');
11
12
  const Schema = require('./schema');
12
13
  const SchemaType = require('./schematype');
13
14
  const SchemaTypes = require('./schema/index');
@@ -35,6 +36,7 @@ const shardingPlugin = require('./plugins/sharding');
35
36
  const trusted = require('./helpers/query/trusted').trusted;
36
37
  const sanitizeFilter = require('./helpers/query/sanitizeFilter');
37
38
  const isBsonType = require('./helpers/isBsonType');
39
+ const MongooseError = require('./error/mongooseError');
38
40
 
39
41
  const defaultMongooseSymbol = Symbol.for('mongoose:default');
40
42
 
@@ -62,6 +64,7 @@ function Mongoose(options) {
62
64
  this.connections = [];
63
65
  this.models = {};
64
66
  this.events = new EventEmitter();
67
+ this.__driver = driver.get();
65
68
  // default global options
66
69
  this.options = Object.assign({
67
70
  pluralization: true,
@@ -135,6 +138,7 @@ Mongoose.prototype.ConnectionStates = STATES;
135
138
  * uses to communicate with the database. A driver is a Mongoose-specific interface that defines functions
136
139
  * like `find()`.
137
140
  *
141
+ * @deprecated
138
142
  * @memberOf Mongoose
139
143
  * @property driver
140
144
  * @api public
@@ -142,6 +146,36 @@ Mongoose.prototype.ConnectionStates = STATES;
142
146
 
143
147
  Mongoose.prototype.driver = driver;
144
148
 
149
+ /**
150
+ * Overwrites the current driver used by this Mongoose instance. A driver is a
151
+ * Mongoose-specific interface that defines functions like `find()`.
152
+ *
153
+ * @memberOf Mongoose
154
+ * @method setDriver
155
+ * @api public
156
+ */
157
+
158
+ Mongoose.prototype.setDriver = function setDriver(driver) {
159
+ const _mongoose = this instanceof Mongoose ? this : mongoose;
160
+
161
+ if (_mongoose.__driver === driver) {
162
+ return _mongoose;
163
+ }
164
+
165
+ const openConnection = _mongoose.connections && _mongoose.connections.find(conn => conn.readyState !== STATES.disconnected);
166
+ if (openConnection) {
167
+ const msg = 'Cannot modify Mongoose driver if a connection is already open. ' +
168
+ 'Call `mongoose.disconnect()` before modifying the driver';
169
+ throw new MongooseError(msg);
170
+ }
171
+ _mongoose.__driver = driver;
172
+
173
+ const Connection = driver.getConnection();
174
+ _mongoose.connections = [new Connection(_mongoose)];
175
+
176
+ return _mongoose;
177
+ };
178
+
145
179
  /**
146
180
  * Sets mongoose options
147
181
  *
@@ -154,23 +188,26 @@ Mongoose.prototype.driver = driver;
154
188
  * mongoose.set('debug', function(collectionName, methodName, ...methodArgs) {}); // use custom function to log collection methods + arguments
155
189
  *
156
190
  * Currently supported options are:
191
+ * - 'applyPluginsToChildSchemas': `true` by default. Set to false to skip applying global plugins to child schemas
192
+ * - 'applyPluginsToDiscriminators': `false` by default. Set to true to apply global plugins to discriminator schemas. This typically isn't necessary because plugins are applied to the base schema and discriminators copy all middleware, methods, statics, and properties from the base schema.
193
+ * - 'autoCreate': Set to `true` to make Mongoose call [`Model.createCollection()`](/docs/api/model.html#model_Model.createCollection) automatically when you create a model with `mongoose.model()` or `conn.model()`. This is useful for testing transactions, change streams, and other features that require the collection to exist.
194
+ * - 'autoIndex': `true` by default. Set to false to disable automatic index creation for all models associated with this Mongoose instance.
157
195
  * - 'debug': If `true`, prints the operations mongoose sends to MongoDB to the console. If a writable stream is passed, it will log to that stream, without colorization. If a callback function is passed, it will receive the collection name, the method name, then all arguments passed to the method. For example, if you wanted to replicate the default logging, you could output from the callback `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`.
158
196
  * - 'returnOriginal': If `false`, changes the default `returnOriginal` option to `findOneAndUpdate()`, `findByIdAndUpdate`, and `findOneAndReplace()` to false. This is equivalent to setting the `new` option to `true` for `findOneAndX()` calls by default. Read our [`findOneAndUpdate()` tutorial](/docs/tutorials/findoneandupdate.html) for more information.
159
197
  * - 'bufferCommands': enable/disable mongoose's buffering mechanism for all connections and models
160
- * - 'cloneSchemas': false by default. Set to `true` to `clone()` all schemas before compiling into a model.
161
- * - 'applyPluginsToDiscriminators': false by default. Set to true to apply global plugins to discriminator schemas. This typically isn't necessary because plugins are applied to the base schema and discriminators copy all middleware, methods, statics, and properties from the base schema.
162
- * - 'applyPluginsToChildSchemas': true by default. Set to false to skip applying global plugins to child schemas
163
- * - 'objectIdGetter': true by default. Mongoose adds a getter to MongoDB ObjectId's called `_id` that returns `this` for convenience with populate. Set this to false to remove the getter.
164
- * - 'runValidators': false by default. Set to true to enable [update validators](/docs/validation.html#update-validators) for all validators by default.
165
- * - 'toObject': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toObject()`](/docs/api.html#document_Document-toObject)
166
- * - 'toJSON': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toJSON()`](/docs/api.html#document_Document-toJSON), for determining how Mongoose documents get serialized by `JSON.stringify()`
167
- * - 'strict': true by default, may be `false`, `true`, or `'throw'`. Sets the default strict mode for schemas.
168
- * - 'strictQuery': same value as 'strict' by default (`true`), may be `false`, `true`, or `'throw'`. Sets the default [strictQuery](/docs/guide.html#strictQuery) mode for schemas.
169
- * - 'selectPopulatedPaths': true by default. Set to false to opt out of Mongoose adding all fields that you `populate()` to your `select()`. The schema-level option `selectPopulatedPaths` overwrites this one.
198
+ * - 'cloneSchemas': `false` by default. Set to `true` to `clone()` all schemas before compiling into a model.
199
+ * - 'debug': If `true`, prints the operations mongoose sends to MongoDB to the console. If a writable stream is passed, it will log to that stream, without colorization. If a callback function is passed, it will receive the collection name, the method name, then all arugments passed to the method. For example, if you wanted to replicate the default logging, you could output from the callback `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`.
200
+ * - 'timestamps.createdAt.immutable': `true` by default. If `false`, it will change the `createdAt` field to be [`immutable: false`](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-immutable) which means you can update the `createdAt`
170
201
  * - 'maxTimeMS': If set, attaches [maxTimeMS](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/) to every query
171
- * - 'autoIndex': true by default. Set to false to disable automatic index creation for all models associated with this Mongoose instance.
172
- * - 'autoCreate': Set to `true` to make Mongoose call [`Model.createCollection()`](/docs/api/model.html#model_Model.createCollection) automatically when you create a model with `mongoose.model()` or `conn.model()`. This is useful for testing transactions, change streams, and other features that require the collection to exist.
202
+ * - 'objectIdGetter': `true` by default. Mongoose adds a getter to MongoDB ObjectId's called `_id` that returns `this` for convenience with populate. Set this to false to remove the getter.
173
203
  * - 'overwriteModels': Set to `true` to default to overwriting models with the same name when calling `mongoose.model()`, as opposed to throwing an `OverwriteModelError`.
204
+ * - 'returnOriginal': If `false`, changes the default `returnOriginal` option to `findOneAndUpdate()`, `findByIdAndUpdate`, and `findOneAndReplace()` to false. This is equivalent to setting the `new` option to `true` for `findOneAndX()` calls by default. Read our [`findOneAndUpdate()` tutorial](/docs/tutorials/findoneandupdate.html) for more information.
205
+ * - 'runValidators': `false` by default. Set to true to enable [update validators](/docs/validation.html#update-validators) for all validators by default.
206
+ * - 'selectPopulatedPaths': `true` by default. Set to false to opt out of Mongoose adding all fields that you `populate()` to your `select()`. The schema-level option `selectPopulatedPaths` overwrites this one.
207
+ * - 'strict': `true` by default, may be `false`, `true`, or `'throw'`. Sets the default strict mode for schemas.
208
+ * - 'strictQuery': same value as 'strict' by default (`true`), may be `false`, `true`, or `'throw'`. Sets the default [strictQuery](/docs/guide.html#strictQuery) mode for schemas.
209
+ * - 'toJSON': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toJSON()`](/docs/api.html#document_Document-toJSON), for determining how Mongoose documents get serialized by `JSON.stringify()`
210
+ * - 'toObject': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toObject()`](/docs/api.html#document_Document-toObject)
174
211
  *
175
212
  * @param {String} key
176
213
  * @param {String|Function|Boolean} value
@@ -260,8 +297,6 @@ Mongoose.prototype.get = Mongoose.prototype.set;
260
297
  * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility.
261
298
  * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
262
299
  * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
263
- * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections.
264
- * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above.
265
300
  * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](https://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html).
266
301
  * @param {Number} [options.maxPoolSize=5] 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).
267
302
  * @param {Number} [options.minPoolSize=1] 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).
@@ -275,7 +310,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
275
310
  Mongoose.prototype.createConnection = function(uri, options, callback) {
276
311
  const _mongoose = this instanceof Mongoose ? this : mongoose;
277
312
 
278
- const Connection = driver.get().getConnection();
313
+ const Connection = _mongoose.__driver.getConnection();
279
314
  const conn = new Connection(_mongoose);
280
315
  if (typeof options === 'function') {
281
316
  callback = options;
@@ -323,8 +358,6 @@ Mongoose.prototype.createConnection = function(uri, options, callback) {
323
358
  * @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).
324
359
  * @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.
325
360
  * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
326
- * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections.
327
- * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above.
328
361
  * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](https://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html).
329
362
  * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
330
363
  * @param {Number} [options.socketTimeoutMS=30000] 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. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. 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.
@@ -471,7 +504,6 @@ Mongoose.prototype.pluralize = function(fn) {
471
504
  */
472
505
 
473
506
  Mongoose.prototype.model = function(name, schema, collection, options) {
474
-
475
507
  const _mongoose = this instanceof Mongoose ? this : mongoose;
476
508
 
477
509
  if (typeof schema === 'string') {
@@ -688,7 +720,7 @@ Mongoose.prototype.__defineGetter__('connection', function() {
688
720
  });
689
721
 
690
722
  Mongoose.prototype.__defineSetter__('connection', function(v) {
691
- if (v instanceof Connection) {
723
+ if (v instanceof this.__driver.getConnection()) {
692
724
  this.connections[0] = v;
693
725
  this.models = v.models;
694
726
  }
@@ -717,18 +749,6 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {
717
749
 
718
750
  Mongoose.prototype.connections;
719
751
 
720
- /*!
721
- * Connection
722
- */
723
-
724
- const Connection = driver.get().getConnection();
725
-
726
- /*!
727
- * Collection
728
- */
729
-
730
- const Collection = driver.get().Collection;
731
-
732
752
  /**
733
753
  * The Mongoose Aggregate constructor
734
754
  *
@@ -745,7 +765,14 @@ Mongoose.prototype.Aggregate = Aggregate;
745
765
  * @api public
746
766
  */
747
767
 
748
- Mongoose.prototype.Collection = Collection;
768
+ Object.defineProperty(Mongoose.prototype, 'Collection', {
769
+ get: function() {
770
+ return this.__driver.Collection;
771
+ },
772
+ set: function(Collection) {
773
+ this.__driver.Collection = Collection;
774
+ }
775
+ });
749
776
 
750
777
  /**
751
778
  * The Mongoose [Connection](#connection_Connection) constructor
@@ -756,7 +783,18 @@ Mongoose.prototype.Collection = Collection;
756
783
  * @api public
757
784
  */
758
785
 
759
- Mongoose.prototype.Connection = Connection;
786
+ Object.defineProperty(Mongoose.prototype, 'Connection', {
787
+ get: function() {
788
+ return this.__driver.getConnection();
789
+ },
790
+ set: function(Connection) {
791
+ if (Connection === this.__driver.getConnection()) {
792
+ return;
793
+ }
794
+
795
+ this.__driver.getConnection = () => Connection;
796
+ }
797
+ });
760
798
 
761
799
  /**
762
800
  * The Mongoose version
@@ -1182,6 +1220,43 @@ Mongoose.prototype._promiseOrCallback = function(callback, fn, ee) {
1182
1220
  return promiseOrCallback(callback, fn, ee, this.Promise);
1183
1221
  };
1184
1222
 
1223
+ /**
1224
+ * Use this function in `pre()` middleware to skip calling the wrapped function.
1225
+ *
1226
+ * ####Example:
1227
+ *
1228
+ * schema.pre('save', function() {
1229
+ * // Will skip executing `save()`, but will execute post hooks as if
1230
+ * // `save()` had executed with the result `{ matchedCount: 0 }`
1231
+ * return mongoose.skipMiddlewareFunction({ matchedCount: 0 });
1232
+ * });
1233
+ *
1234
+ * @method skipMiddlewareFunction
1235
+ * @param {any} result
1236
+ * @api public
1237
+ */
1238
+
1239
+ Mongoose.prototype.skipMiddlewareFunction = Kareem.skipWrappedFunction;
1240
+
1241
+ /**
1242
+ * Use this function in `post()` middleware to replace the result
1243
+ *
1244
+ * ####Example:
1245
+ *
1246
+ * schema.post('find', function(res) {
1247
+ * // Normally you have to modify `res` in place. But with
1248
+ * // `overwriteMiddlewarResult()`, you can make `find()` return a
1249
+ * // completely different value.
1250
+ * return mongoose.overwriteMiddlewareResult(res.filter(doc => !doc.isDeleted));
1251
+ * });
1252
+ *
1253
+ * @method overwriteMiddlewareResult
1254
+ * @param {any} result
1255
+ * @api public
1256
+ */
1257
+
1258
+ Mongoose.prototype.overwriteMiddlewareResult = Kareem.overwriteResult;
1259
+
1185
1260
  /*!
1186
1261
  * The exports object is an instance of Mongoose.
1187
1262
  *
package/lib/model.js CHANGED
@@ -4281,7 +4281,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
4281
4281
 
4282
4282
  for (const path of paths) {
4283
4283
  const schemaType = schema.path(path);
4284
- if (!schemaType || !schemaType.$isMongooseArray) {
4284
+ if (!schemaType || !schemaType.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4285
4285
  continue;
4286
4286
  }
4287
4287
 
package/lib/query.js CHANGED
@@ -27,6 +27,7 @@ const immediate = require('./helpers/immediate');
27
27
  const isExclusive = require('./helpers/projection/isExclusive');
28
28
  const isInclusive = require('./helpers/projection/isInclusive');
29
29
  const isSubpath = require('./helpers/projection/isSubpath');
30
+ const mpath = require('mpath');
30
31
  const mquery = require('mquery');
31
32
  const parseProjection = require('./helpers/projection/parseProjection');
32
33
  const removeUnusedArrayFilters = require('./helpers/update/removeUnusedArrayFilters');
@@ -2196,7 +2197,6 @@ function _castArrayFilters(query) {
2196
2197
  * @api private
2197
2198
  */
2198
2199
  Query.prototype._find = wrapThunk(function(callback) {
2199
-
2200
2200
  this._castConditions();
2201
2201
 
2202
2202
  if (this.error() != null) {
@@ -2220,7 +2220,8 @@ Query.prototype._find = wrapThunk(function(callback) {
2220
2220
  // Separate options to pass down to `completeMany()` in case we need to
2221
2221
  // set a session on the document
2222
2222
  const completeManyOptions = Object.assign({}, {
2223
- session: this && this.options && this.options.session || null
2223
+ session: this && this.options && this.options.session || null,
2224
+ lean: mongooseOptions.lean || null
2224
2225
  });
2225
2226
 
2226
2227
  const cb = (err, docs) => {
@@ -2244,7 +2245,9 @@ Query.prototype._find = wrapThunk(function(callback) {
2244
2245
  });
2245
2246
  }
2246
2247
  return mongooseOptions.lean ?
2247
- callback(null, docs) :
2248
+ // call _completeManyLean here?
2249
+ _completeManyLean(_this.model.schema, docs, null, completeManyOptions, callback) :
2250
+ // callback(null, docs) :
2248
2251
  completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
2249
2252
  }
2250
2253
 
@@ -2418,6 +2421,9 @@ Query.prototype._completeOne = function(doc, res, callback) {
2418
2421
  const mongooseOptions = this._mongooseOptions;
2419
2422
  // `rawResult`
2420
2423
  const options = this.options;
2424
+ if (!options.lean && mongooseOptions.lean) {
2425
+ options.lean = mongooseOptions.lean;
2426
+ }
2421
2427
 
2422
2428
  if (options.explain) {
2423
2429
  return callback(null, doc);
@@ -2431,7 +2437,7 @@ Query.prototype._completeOne = function(doc, res, callback) {
2431
2437
  }
2432
2438
  }
2433
2439
  return mongooseOptions.lean ?
2434
- _completeOneLean(doc, res, options, callback) :
2440
+ _completeOneLean(model.schema, doc, null, res, options, callback) :
2435
2441
  completeOne(model, doc, res, options, projection, userProvidedFields,
2436
2442
  null, callback);
2437
2443
  }
@@ -2442,7 +2448,7 @@ Query.prototype._completeOne = function(doc, res, callback) {
2442
2448
  if (err != null) {
2443
2449
  return callback(err);
2444
2450
  }
2445
- _completeOneLean(doc, res, options, callback);
2451
+ _completeOneLean(model.schema, doc, null, res, options, callback);
2446
2452
  });
2447
2453
  }
2448
2454
 
@@ -4009,13 +4015,62 @@ Query.prototype._findAndModify = function(type, callback) {
4009
4015
  * ignore
4010
4016
  */
4011
4017
 
4012
- function _completeOneLean(doc, res, opts, callback) {
4018
+ function _completeOneLean(schema, doc, path, res, opts, callback) {
4019
+ if (opts.lean && opts.lean.transform) {
4020
+ for (let i = 0; i < schema.childSchemas.length; i++) {
4021
+ const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path;
4022
+ const _schema = schema.childSchemas[i].schema;
4023
+ const obj = mpath.get(childPath, doc);
4024
+ if (obj == null) {
4025
+ continue;
4026
+ }
4027
+ if (Array.isArray(obj)) {
4028
+ for (let i = 0; i < obj.length; i++) {
4029
+ opts.lean.transform(obj[i]);
4030
+ }
4031
+ } else {
4032
+ opts.lean.transform(obj);
4033
+ }
4034
+ _completeOneLean(_schema, obj, childPath, res, opts);
4035
+ }
4036
+ if (callback) {
4037
+ return callback(null, doc);
4038
+ } else {
4039
+ return;
4040
+ }
4041
+ }
4013
4042
  if (opts.rawResult) {
4014
4043
  return callback(null, res);
4015
4044
  }
4016
4045
  return callback(null, doc);
4017
4046
  }
4018
4047
 
4048
+ /*!
4049
+ * ignore
4050
+ */
4051
+
4052
+ function _completeManyLean(schema, docs, path, opts, callback) {
4053
+ if (opts.lean && opts.lean.transform) {
4054
+ for (let i = 0; i < schema.childSchemas.length; i++) {
4055
+ const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path;
4056
+ const _schema = schema.childSchemas[i].schema;
4057
+ let doc = mpath.get(childPath, docs);
4058
+ if (doc == null) {
4059
+ continue;
4060
+ }
4061
+ doc = doc.flat();
4062
+ for (let i = 0; i < doc.length; i++) {
4063
+ opts.lean.transform(doc[i]);
4064
+ }
4065
+ _completeManyLean(_schema, doc, childPath, opts);
4066
+ }
4067
+ }
4068
+
4069
+ if (!callback) {
4070
+ return;
4071
+ }
4072
+ return callback(null, docs);
4073
+ }
4019
4074
  /*!
4020
4075
  * Override mquery.prototype._mergeUpdate to handle mongoose objects in
4021
4076
  * updates.
@@ -167,7 +167,7 @@ SubdocumentPath.prototype.cast = function(val, doc, init, priorVal, options) {
167
167
  }
168
168
  return obj;
169
169
  }, null);
170
- options = priorVal != null ? Object.assign({}, options, { priorDoc: priorVal }) : options;
170
+ options = Object.assign({}, options, { priorDoc: priorVal });
171
171
  if (init) {
172
172
  subdoc = new Constructor(void 0, selected, doc);
173
173
  subdoc.$init(val);
@@ -10,6 +10,7 @@ const EventEmitter = require('events').EventEmitter;
10
10
  const SchemaDocumentArrayOptions =
11
11
  require('../options/SchemaDocumentArrayOptions');
12
12
  const SchemaType = require('../schematype');
13
+ const SubdocumentPath = require('./SubdocumentPath');
13
14
  const discriminator = require('../helpers/model/discriminator');
14
15
  const handleIdOption = require('../helpers/schema/handleIdOption');
15
16
  const handleSpreadDoc = require('../helpers/document/handleSpreadDoc');
@@ -75,6 +76,15 @@ function DocumentArrayPath(key, schema, options, schemaOptions) {
75
76
  this.$embeddedSchemaType.cast = function(value, doc, init) {
76
77
  return parentSchemaType.cast(value, doc, init)[0];
77
78
  };
79
+ this.$embeddedSchemaType.doValidate = function(value, fn, scope, options) {
80
+ const Constructor = getConstructor(this.caster, value);
81
+
82
+ if (value && !(value instanceof Constructor)) {
83
+ value = new Constructor(value, scope, null, null, options && options.index != null ? options.index : null);
84
+ }
85
+
86
+ return SubdocumentPath.prototype.doValidate.call(this, value, fn, scope, options);
87
+ };
78
88
  this.$embeddedSchemaType.$isMongooseDocumentArrayElement = true;
79
89
  this.$embeddedSchemaType.caster = this.Constructor;
80
90
  this.$embeddedSchemaType.schema = this.schema;
@@ -391,6 +401,8 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
391
401
  let selected;
392
402
  let subdoc;
393
403
 
404
+ options = options || {};
405
+
394
406
  if (!Array.isArray(value)) {
395
407
  if (!init && !DocumentArrayPath.options.castNonArrays) {
396
408
  throw new CastError('DocumentArray', value, this.path, null, this);
@@ -405,7 +417,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
405
417
 
406
418
  // We need to create a new array, otherwise change tracking will
407
419
  // update the old doc (gh-4449)
408
- if (!options || !options.skipDocumentArrayCast || utils.isMongooseDocumentArray(value)) {
420
+ if (!options.skipDocumentArrayCast || utils.isMongooseDocumentArray(value)) {
409
421
  value = new MongooseDocumentArray(value, this.path, doc);
410
422
  }
411
423
 
@@ -413,7 +425,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
413
425
  value[arrayAtomicsSymbol] = prev[arrayAtomicsSymbol] || {};
414
426
  }
415
427
 
416
- if (options && options.arrayPathIndex != null) {
428
+ if (options.arrayPathIndex != null) {
417
429
  value[arrayPathSymbol] = this.path + '.' + options.arrayPathIndex;
418
430
  }
419
431
 
package/lib/schema.js CHANGED
@@ -107,11 +107,11 @@ function Schema(obj, options) {
107
107
  this.inherits = {};
108
108
  this.callQueue = [];
109
109
  this._indexes = [];
110
- this.methods = {};
110
+ this.methods = (options && options.methods) || {};
111
111
  this.methodOptions = {};
112
- this.statics = {};
112
+ this.statics = (options && options.statics) || {};
113
113
  this.tree = {};
114
- this.query = {};
114
+ this.query = (options && options.query) || {};
115
115
  this.childSchemas = [];
116
116
  this.plugins = [];
117
117
  // For internal debugging. Do not use this to try to save a schema in MDB.
@@ -766,7 +766,6 @@ reserved['prototype'] =
766
766
  // EventEmitter
767
767
  reserved.emit =
768
768
  reserved.listeners =
769
- reserved.on =
770
769
  reserved.removeListener =
771
770
 
772
771
  // document properties and functions
package/lib/schematype.js CHANGED
@@ -1181,7 +1181,6 @@ SchemaType.prototype._castNullish = function _castNullish(v) {
1181
1181
 
1182
1182
  SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) {
1183
1183
  let v = this._applySetters(value, scope, init, priorVal, options);
1184
-
1185
1184
  if (v == null) {
1186
1185
  return this._castNullish(v);
1187
1186
  }
@@ -95,6 +95,19 @@ StateMachine.prototype.clear = function clear(state) {
95
95
  }
96
96
  };
97
97
 
98
+ /*!
99
+ * ignore
100
+ */
101
+
102
+ StateMachine.prototype.clearPath = function clearPath(path) {
103
+ const state = this.paths[path];
104
+ if (!state) {
105
+ return;
106
+ }
107
+ delete this.paths[path];
108
+ delete this.states[state][path];
109
+ };
110
+
98
111
  /*!
99
112
  * Checks to see if at least one path is in the states passed in via `arguments`
100
113
  * e.g., this.some('required', 'inited')
package/lib/utils.js CHANGED
@@ -935,6 +935,9 @@ exports.getOption = function(name) {
935
935
  const sources = Array.prototype.slice.call(arguments, 1);
936
936
 
937
937
  for (const source of sources) {
938
+ if (source == null) {
939
+ continue;
940
+ }
938
941
  if (source[name] != null) {
939
942
  return source[name];
940
943
  }
@@ -15,6 +15,7 @@ const VALID_OPTIONS = Object.freeze([
15
15
  'bufferTimeoutMS',
16
16
  'cloneSchemas',
17
17
  'debug',
18
+ 'timestamps.createdAt.immutable',
18
19
  'maxTimeMS',
19
20
  'objectIdGetter',
20
21
  'overwriteModels',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "6.3.7",
4
+ "version": "6.4.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -20,8 +20,8 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "bson": "^4.6.2",
23
- "kareem": "2.3.5",
24
- "mongodb": "4.5.0",
23
+ "kareem": "2.4.1",
24
+ "mongodb": "4.7.0",
25
25
  "mpath": "0.9.0",
26
26
  "mquery": "4.0.3",
27
27
  "ms": "2.1.3",
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "strict": true,
3
4
  "strictNullChecks": true,
4
5
  "paths": {
5
6
  "mongoose" : ["./types/index.d.ts"]
@@ -1,6 +1,9 @@
1
1
  declare module 'mongoose' {
2
2
  import mongodb = require('mongodb');
3
3
 
4
+ /** Extract generic type from Aggregate class */
5
+ type AggregateExtract<P> = P extends Aggregate<infer T> ? T : never;
6
+
4
7
  interface AggregateOptions extends
5
8
  SessionOption {
6
9
  /**
@@ -66,6 +66,11 @@ declare module 'mongoose' {
66
66
  close(callback: CallbackWithoutResult): void;
67
67
  close(force?: boolean): Promise<void>;
68
68
 
69
+ /** Closes and destroys the connection. Connection once destroyed cannot be reopened */
70
+ destroy(force: boolean, callback: CallbackWithoutResult): void;
71
+ destroy(callback: CallbackWithoutResult): void;
72
+ destroy(force?: boolean): Promise<void>;
73
+
69
74
  /** Retrieves a collection, creating it if not cached. */
70
75
  collection<T extends AnyObject = AnyObject>(name: string, options?: mongodb.CreateCollectionOptions): Collection<T>;
71
76
 
@@ -19,6 +19,9 @@ declare module 'mongoose' {
19
19
  /** This documents __v. */
20
20
  __v?: any;
21
21
 
22
+ /** Assert that a given path or paths is populated. Throws an error if not populated. */
23
+ $assertPopulated<Paths = {}>(paths: string | string[]): Omit<this, keyof Paths> & Paths;
24
+
22
25
  /* Get all subdocs (by bfs) */
23
26
  $getAllSubdocs(): Document[];
24
27
 
@@ -193,8 +196,8 @@ declare module 'mongoose' {
193
196
  /** Populates document references. */
194
197
  populate<Paths = {}>(path: string | PopulateOptions | (string | PopulateOptions)[]): Promise<this & Paths>;
195
198
  populate<Paths = {}>(path: string | PopulateOptions | (string | PopulateOptions)[], callback: Callback<this & Paths>): void;
196
- populate<Paths = {}>(path: string, select?: string | AnyObject, model?: Model<unknown>, match?: AnyObject, options?: PopulateOptions): Promise<this & Paths>;
197
- populate<Paths = {}>(path: string, select?: string | AnyObject, model?: Model<unknown>, match?: AnyObject, options?: PopulateOptions, callback?: Callback<this & Paths>): void;
199
+ populate<Paths = {}>(path: string, select?: string | AnyObject, model?: Model<any>, match?: AnyObject, options?: PopulateOptions): Promise<this & Paths>;
200
+ populate<Paths = {}>(path: string, select?: string | AnyObject, model?: Model<any>, match?: AnyObject, options?: PopulateOptions, callback?: Callback<this & Paths>): void;
198
201
 
199
202
  /** Gets _id(s) used during population of the given `path`. If the path was not populated, returns `undefined`. */
200
203
  populated(path: string): any;