mongoose 6.3.9 → 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/.eslintrc.json CHANGED
@@ -4,43 +4,70 @@
4
4
  ],
5
5
  "ignorePatterns": [
6
6
  "docs",
7
+ "tools",
7
8
  "dist",
8
- "test/files/*"
9
+ "website.js",
10
+ "test/files/*",
11
+ "benchmarks"
9
12
  ],
10
- "overrides": [{
11
- "files": [
12
- "**/*.{ts,tsx}"
13
- ],
14
- "extends": [
15
- "plugin:@typescript-eslint/eslint-recommended",
16
- "plugin:@typescript-eslint/recommended"
17
- ],
18
- "plugins": [
19
- "@typescript-eslint"
20
- ],
21
- "rules": {
22
- "@typescript-eslint/triple-slash-reference": "off",
23
- "spaced-comment": ["error", "always", {
24
- "markers": ["/"]
25
- }],
26
- "@typescript-eslint/no-explicit-any": "off",
27
- "@typescript-eslint/ban-types": "off",
28
- "@typescript-eslint/no-unused-vars": "off",
29
- "@typescript-eslint/explicit-module-boundary-types": "off",
30
- "@typescript-eslint/indent": ["error", 2, {
31
- "SwitchCase": 1
32
- }],
33
- "@typescript-eslint/prefer-optional-chain": "error",
34
- "@typescript-eslint/brace-style": "error",
35
- "@typescript-eslint/no-dupe-class-members": "error",
36
- "@typescript-eslint/no-redeclare": "error",
37
- "@typescript-eslint/type-annotation-spacing": "error",
38
- "@typescript-eslint/object-curly-spacing": ["error", "always"],
39
- "@typescript-eslint/semi": "error",
40
- "@typescript-eslint/space-before-function-paren": ["error", "never"],
41
- "@typescript-eslint/space-infix-ops": "off"
13
+ "overrides": [
14
+ {
15
+ "files": [
16
+ "**/*.{ts,tsx}"
17
+ ],
18
+ "extends": [
19
+ "plugin:@typescript-eslint/eslint-recommended",
20
+ "plugin:@typescript-eslint/recommended"
21
+ ],
22
+ "plugins": [
23
+ "@typescript-eslint"
24
+ ],
25
+ "rules": {
26
+ "@typescript-eslint/triple-slash-reference": "off",
27
+ "spaced-comment": [
28
+ "error",
29
+ "always",
30
+ {
31
+ "block": {
32
+ "markers": [
33
+ "!"
34
+ ],
35
+ "balanced": true
36
+ },
37
+ "markers": [
38
+ "/"
39
+ ]
40
+ }
41
+ ],
42
+ "@typescript-eslint/no-explicit-any": "off",
43
+ "@typescript-eslint/ban-types": "off",
44
+ "@typescript-eslint/no-unused-vars": "off",
45
+ "@typescript-eslint/explicit-module-boundary-types": "off",
46
+ "@typescript-eslint/indent": [
47
+ "error",
48
+ 2,
49
+ {
50
+ "SwitchCase": 1
51
+ }
52
+ ],
53
+ "@typescript-eslint/prefer-optional-chain": "error",
54
+ "@typescript-eslint/brace-style": "error",
55
+ "@typescript-eslint/no-dupe-class-members": "error",
56
+ "@typescript-eslint/no-redeclare": "error",
57
+ "@typescript-eslint/type-annotation-spacing": "error",
58
+ "@typescript-eslint/object-curly-spacing": [
59
+ "error",
60
+ "always"
61
+ ],
62
+ "@typescript-eslint/semi": "error",
63
+ "@typescript-eslint/space-before-function-paren": [
64
+ "error",
65
+ "never"
66
+ ],
67
+ "@typescript-eslint/space-infix-ops": "off"
68
+ }
42
69
  }
43
- }],
70
+ ],
44
71
  "plugins": [
45
72
  "mocha-no-only"
46
73
  ],
package/lib/connection.js CHANGED
@@ -697,6 +697,18 @@ Connection.prototype.openUri = function(uri, options, callback) {
697
697
  typeof callback + '"');
698
698
  }
699
699
 
700
+ if (this._destroyCalled) {
701
+ const error = 'Connection has been closed and destroyed, and cannot be used for re-opening the connection. ' +
702
+ 'Please create a new connection with `mongoose.createConnection()` or `mongoose.connect()`.';
703
+ if (typeof callback === 'function') {
704
+ callback(error);
705
+ return;
706
+ }
707
+ else {
708
+ throw new MongooseError(error);
709
+ }
710
+ }
711
+
700
712
  if (this.readyState === STATES.connecting || this.readyState === STATES.connected) {
701
713
  if (this._connectionString !== uri) {
702
714
  throw new MongooseError('Can\'t call `openUri()` on an active connection with ' +
@@ -901,6 +913,23 @@ function _setClient(conn, client, options, dbName) {
901
913
  }
902
914
  }
903
915
 
916
+ Connection.prototype.destroy = function(force, callback) {
917
+ if (typeof force === 'function') {
918
+ callback = force;
919
+ force = false;
920
+ }
921
+
922
+ if (force != null && typeof force === 'object') {
923
+ this.$wasForceClosed = !!force.force;
924
+ } else {
925
+ this.$wasForceClosed = !!force;
926
+ }
927
+
928
+ return promiseOrCallback(callback, cb => {
929
+ this._close(force, true, cb);
930
+ });
931
+ };
932
+
904
933
  /**
905
934
  * Closes the connection
906
935
  *
@@ -923,7 +952,7 @@ Connection.prototype.close = function(force, callback) {
923
952
  }
924
953
 
925
954
  return promiseOrCallback(callback, cb => {
926
- this._close(force, cb);
955
+ this._close(force, false, cb);
927
956
  });
928
957
  };
929
958
 
@@ -931,19 +960,26 @@ Connection.prototype.close = function(force, callback) {
931
960
  * Handles closing the connection
932
961
  *
933
962
  * @param {Boolean} force
963
+ * @param {Boolean} destroy
934
964
  * @param {Function} callback
935
965
  * @api private
936
966
  */
937
- Connection.prototype._close = function(force, callback) {
967
+ Connection.prototype._close = function(force, destroy, callback) {
938
968
  const _this = this;
939
969
  const closeCalled = this._closeCalled;
940
970
  this._closeCalled = true;
971
+ this._destroyCalled = destroy;
941
972
  if (this.client != null) {
942
973
  this.client._closeCalled = true;
974
+ this.client._destroyCalled = destroy;
943
975
  }
944
976
 
977
+ const conn = this;
945
978
  switch (this.readyState) {
946
979
  case STATES.disconnected:
980
+ if (destroy && this.base.connections.indexOf(conn) !== -1) {
981
+ this.base.connections.splice(this.base.connections.indexOf(conn), 1);
982
+ }
947
983
  if (closeCalled) {
948
984
  callback();
949
985
  } else {
@@ -963,6 +999,9 @@ Connection.prototype._close = function(force, callback) {
963
999
  if (err) {
964
1000
  return callback(err);
965
1001
  }
1002
+ if (destroy && _this.base.connections.indexOf(conn) !== -1) {
1003
+ _this.base.connections.splice(_this.base.connections.indexOf(conn), 1);
1004
+ }
966
1005
  _this.onClose(force);
967
1006
  callback(null);
968
1007
  });
@@ -970,12 +1009,15 @@ Connection.prototype._close = function(force, callback) {
970
1009
  break;
971
1010
  case STATES.connecting:
972
1011
  this.once('open', function() {
973
- _this.close(callback);
1012
+ destroy ? _this.destroy(force, callback) : _this.close(force, callback);
974
1013
  });
975
1014
  break;
976
1015
 
977
1016
  case STATES.disconnecting:
978
1017
  this.once('close', function() {
1018
+ if (destroy && _this.base.connections.indexOf(conn) !== -1) {
1019
+ _this.base.connections.splice(_this.base.connections.indexOf(conn), 1);
1020
+ }
979
1021
  callback();
980
1022
  });
981
1023
  break;
@@ -1004,7 +1046,7 @@ Connection.prototype.onClose = function(force) {
1004
1046
  this.emit('close', force);
1005
1047
 
1006
1048
  for (const db of this.otherDbs) {
1007
- db.close({ force: force, skipCloseClient: true });
1049
+ this._destroyCalled ? db.destroy({ force: force, skipCloseClient: true }) : db.close({ force: force, skipCloseClient: true });
1008
1050
  }
1009
1051
  };
1010
1052
 
@@ -1026,7 +1068,7 @@ Connection.prototype.collection = function(name, options) {
1026
1068
  };
1027
1069
  options = Object.assign({}, defaultOptions, options ? utils.clone(options) : {});
1028
1070
  options.$wasForceClosed = this.$wasForceClosed;
1029
- const Collection = driver.get().Collection;
1071
+ const Collection = this.base && this.base.__driver && this.base.__driver.Collection || driver.get().Collection;
1030
1072
  if (!(name in this.collections)) {
1031
1073
  this.collections[name] = new Collection(name, this, options);
1032
1074
  }
package/lib/document.js CHANGED
@@ -2486,7 +2486,7 @@ function _getPathsToValidate(doc) {
2486
2486
  const fullPathToSubdoc = subdoc.$__fullPathWithIndexes();
2487
2487
 
2488
2488
  for (const p of paths) {
2489
- if (p === null || p.startsWith(fullPathToSubdoc + '.')) {
2489
+ if (p == null || p.startsWith(fullPathToSubdoc + '.')) {
2490
2490
  paths.delete(p);
2491
2491
  }
2492
2492
  }
@@ -2507,6 +2507,14 @@ function _getPathsToValidate(doc) {
2507
2507
  continue;
2508
2508
  }
2509
2509
 
2510
+ if (_pathType.$isMongooseDocumentArray) {
2511
+ for (const p of paths) {
2512
+ if (p == null || p.startsWith(_pathType.path + '.')) {
2513
+ paths.delete(p);
2514
+ }
2515
+ }
2516
+ }
2517
+
2510
2518
  // Optimization: if primitive path with no validators, or array of primitives
2511
2519
  // with no validators, skip validating this path entirely.
2512
2520
  if (!_pathType.caster && _pathType.validators.length === 0) {
@@ -3145,8 +3153,7 @@ Document.prototype.$__reset = function reset() {
3145
3153
  if (subdoc.$isDocumentArrayElement) {
3146
3154
  if (!resetArrays.has(subdoc.parentArray())) {
3147
3155
  const array = subdoc.parentArray();
3148
- // Mark path to array as init for gh-6818
3149
- this.$__.activePaths.init(fullPathWithIndexes.replace(/\.\d+$/, '').slice(-subdoc.$basePath - 1));
3156
+ this.$__.activePaths.clearPath(fullPathWithIndexes.replace(/\.\d+$/, '').slice(-subdoc.$basePath - 1));
3150
3157
  array[arrayAtomicsBackupSymbol] = array[arrayAtomicsSymbol];
3151
3158
  array[arrayAtomicsSymbol] = {};
3152
3159
 
@@ -3154,7 +3161,7 @@ Document.prototype.$__reset = function reset() {
3154
3161
  }
3155
3162
  } else {
3156
3163
  if (subdoc.$parent() === this) {
3157
- this.$__.activePaths.init(subdoc.$basePath);
3164
+ this.$__.activePaths.clearPath(subdoc.$basePath);
3158
3165
  } else if (subdoc.$parent() != null && subdoc.$parent().$isSubdocument) {
3159
3166
  // If map path underneath subdocument, may end up with a case where
3160
3167
  // map path is modified but parent still needs to be reset. See gh-10295
@@ -4262,10 +4269,10 @@ Document.prototype.$getPopulatedDocs = function $getPopulatedDocs() {
4262
4269
  *
4263
4270
  * #### Example:
4264
4271
  *
4265
- * Model.findOne().populate('author').exec(function (err, doc) {
4266
- * console.log(doc.author.name) // Dr.Seuss
4267
- * console.log(doc.populated('author')) // '5144cf8050f071d979c118a7'
4268
- * })
4272
+ * const doc = await Model.findOne().populate('author');
4273
+ *
4274
+ * console.log(doc.author.name); // Dr.Seuss
4275
+ * console.log(doc.populated('author')); // '5144cf8050f071d979c118a7'
4269
4276
  *
4270
4277
  * If the path was not populated, returns `undefined`.
4271
4278
  *
@@ -4319,6 +4326,37 @@ Document.prototype.populated = function(path, val, options) {
4319
4326
 
4320
4327
  Document.prototype.$populated = Document.prototype.populated;
4321
4328
 
4329
+ /**
4330
+ * Throws an error if a given path is not populated
4331
+ *
4332
+ * #### Example:
4333
+ *
4334
+ * const doc = await Model.findOne().populate('author');
4335
+ *
4336
+ * doc.$assertPopulated('author'); // does not throw
4337
+ * doc.$assertPopulated('other path'); // throws an error
4338
+ *
4339
+ *
4340
+ * @param {String | Array<String>} path
4341
+ * @return {Document} this
4342
+ * @memberOf Document
4343
+ * @instance
4344
+ * @api public
4345
+ */
4346
+
4347
+ Document.prototype.$assertPopulated = function $assertPopulated(paths) {
4348
+ if (Array.isArray(paths)) {
4349
+ paths.forEach(path => this.$assertPopulated(path));
4350
+ return this;
4351
+ }
4352
+
4353
+ if (!this.$populated(paths)) {
4354
+ throw new MongooseError(`Expected path "${paths}" to be populated`);
4355
+ }
4356
+
4357
+ return this;
4358
+ };
4359
+
4322
4360
  /**
4323
4361
  * Takes a populated field and returns it to its unpopulated state.
4324
4362
  *
@@ -31,8 +31,11 @@ module.exports = function setupTimestamps(schema, timestamps) {
31
31
  }
32
32
 
33
33
  if (createdAt && !schema.paths[createdAt]) {
34
- schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable: true };
34
+ const baseImmutableCreatedAt = schema.base.get('timestamps.createdAt.immutable');
35
+ const immutable = baseImmutableCreatedAt != null ? baseImmutableCreatedAt : true;
36
+ schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable };
35
37
  }
38
+
36
39
  schema.add(schemaAdditions);
37
40
 
38
41
  schema.pre('save', function(next) {
@@ -125,26 +125,19 @@ module.exports = function(query, schema, castedDoc, options, callback) {
125
125
  validatorsToExecute.push(function(callback) {
126
126
  schemaPath.doValidate(v, function(err) {
127
127
  if (err) {
128
- err.path = updates[i];
129
- validationErrors.push(err);
130
- return callback(null);
131
- }
132
-
133
- v.validate(function(err) {
134
- if (err) {
135
- if (err.errors) {
136
- for (const key of Object.keys(err.errors)) {
137
- const _err = err.errors[key];
138
- _err.path = updates[i] + '.' + key;
139
- validationErrors.push(_err);
140
- }
141
- } else {
142
- err.path = updates[i];
143
- validationErrors.push(err);
128
+ if (err.errors) {
129
+ for (const key of Object.keys(err.errors)) {
130
+ const _err = err.errors[key];
131
+ _err.path = updates[i] + '.' + key;
132
+ validationErrors.push(_err);
144
133
  }
134
+ } else {
135
+ err.path = updates[i];
136
+ validationErrors.push(err);
145
137
  }
146
- callback(null);
147
- });
138
+ }
139
+
140
+ return callback(null);
148
141
  }, context, { updateValidator: true });
149
142
  });
150
143
  } else {
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
@@ -273,7 +310,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
273
310
  Mongoose.prototype.createConnection = function(uri, options, callback) {
274
311
  const _mongoose = this instanceof Mongoose ? this : mongoose;
275
312
 
276
- const Connection = driver.get().getConnection();
313
+ const Connection = _mongoose.__driver.getConnection();
277
314
  const conn = new Connection(_mongoose);
278
315
  if (typeof options === 'function') {
279
316
  callback = options;
@@ -467,7 +504,6 @@ Mongoose.prototype.pluralize = function(fn) {
467
504
  */
468
505
 
469
506
  Mongoose.prototype.model = function(name, schema, collection, options) {
470
-
471
507
  const _mongoose = this instanceof Mongoose ? this : mongoose;
472
508
 
473
509
  if (typeof schema === 'string') {
@@ -684,7 +720,7 @@ Mongoose.prototype.__defineGetter__('connection', function() {
684
720
  });
685
721
 
686
722
  Mongoose.prototype.__defineSetter__('connection', function(v) {
687
- if (v instanceof Connection) {
723
+ if (v instanceof this.__driver.getConnection()) {
688
724
  this.connections[0] = v;
689
725
  this.models = v.models;
690
726
  }
@@ -713,18 +749,6 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {
713
749
 
714
750
  Mongoose.prototype.connections;
715
751
 
716
- /*!
717
- * Connection
718
- */
719
-
720
- const Connection = driver.get().getConnection();
721
-
722
- /*!
723
- * Collection
724
- */
725
-
726
- const Collection = driver.get().Collection;
727
-
728
752
  /**
729
753
  * The Mongoose Aggregate constructor
730
754
  *
@@ -741,7 +765,14 @@ Mongoose.prototype.Aggregate = Aggregate;
741
765
  * @api public
742
766
  */
743
767
 
744
- 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
+ });
745
776
 
746
777
  /**
747
778
  * The Mongoose [Connection](#connection_Connection) constructor
@@ -752,7 +783,18 @@ Mongoose.prototype.Collection = Collection;
752
783
  * @api public
753
784
  */
754
785
 
755
- 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
+ });
756
798
 
757
799
  /**
758
800
  * The Mongoose version
@@ -1178,6 +1220,43 @@ Mongoose.prototype._promiseOrCallback = function(callback, fn, ee) {
1178
1220
  return promiseOrCallback(callback, fn, ee, this.Promise);
1179
1221
  };
1180
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
+
1181
1260
  /*!
1182
1261
  * The exports object is an instance of Mongoose.
1183
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