mongoose 5.0.6 → 5.0.10

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/History.md CHANGED
@@ -1,3 +1,44 @@
1
+ 5.0.10 / 2018-03-12
2
+ ===================
3
+ * docs(schematype): add notes re: running setters on queries #6209
4
+ * docs: fix typo #6208 [kamagatos](https://github.com/kamagatos)
5
+ * fix(query): only call setters once on query filter props for findOneAndUpdate and findOneAndRemove #6203
6
+ * docs: elaborate on connection string changes in migration guide #6193
7
+ * fix(document): skip applyDefaults if subdoc is null #6187
8
+ * docs: fix schematypes docs and link to them #6176
9
+ * docs(faq): add FAQs re: array defaults and casting aggregation pipelines #6184 #6176 #6170 [lineus](https://github.com/lineus)
10
+ * fix(document): ensure primitive defaults are set and built-in default functions run before setters #6155
11
+ * fix(query): handle single embedded embedded discriminators in castForQuery #6027
12
+
13
+ 5.0.9 / 2018-03-05
14
+ ==================
15
+ * perf: bump mongodb -> 3.0.4 to fix SSL perf issue #6065
16
+
17
+ 5.0.8 / 2018-03-03
18
+ ==================
19
+ * docs: remove obsolete references to `emitIndexErrors` #6186 [isaackwan](https://github.com/isaackwan)
20
+ * fix(query): don't cast findOne() until exec() so setters don't run twice #6157
21
+ * fix: remove document_provider.web.js file #6186
22
+ * fix(discriminator): support custom discriminator model names #6100 [wentout](https://github.com/wentout)
23
+ * fix: support caching calls to `useDb()` #6036 [rocketspacer](https://github.com/rocketspacer)
24
+ * fix(query): add omitUndefined option so setDefaultsOnInsert can kick in on undefined #6034
25
+ * fix: upgrade mongodb -> 3.0.3 for reconnectTries: 0 blocking process exit fix #6028
26
+
27
+ 5.0.7 / 2018-02-23
28
+ ==================
29
+ * fix: support eachAsync options with aggregation cursor #6169 #6168 [vichle](https://github.com/vichle)
30
+ * docs: fix link to MongoDB compound indexes docs #6162 [br0p0p](https://github.com/br0p0p)
31
+ * docs(aggregate): use eachAsync instead of incorrect `each()` #6160 [simllll](https://github.com/simllll)
32
+ * chore: fix benchmarks #6158 [pradel](https://github.com/pradel)
33
+ * docs: remove dead link to old blog post #6154 [markstos](https://github.com/markstos)
34
+ * fix: don't convert dates to numbers when updating mixed path #6146 #6145 [s4rbagamble](https://github.com/s4rbagamble)
35
+ * feat(aggregate): add replaceRoot, count, sortByCount helpers #6142 [jakesjews](https://github.com/jakesjews)
36
+ * fix(document): add includedChildren flag to modifiedPaths() #6134
37
+ * perf: don't create wrapper function if no hooks specified #6126
38
+ * fix(schema): allow indexes on single nested subdocs for geoJSON #6113
39
+ * fix(document): allow depopulating all fields #6073
40
+ * feat(mongoose): add support for `useFindAndModify` option on singleton #5616
41
+
1
42
  5.0.6 / 2018-02-15
2
43
  ==================
3
44
  * refactor(query.castUpdate): avoid creating error until necessary #6137
package/README.md CHANGED
@@ -10,6 +10,8 @@ Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed
10
10
 
11
11
  [mongoosejs.com](http://mongoosejs.com/)
12
12
 
13
+ [Mongoose 5.0.0](https://github.com/Automattic/mongoose/blob/master/History.md#500--2018-01-17) was released on January 17, 2018. You can find more details on backwards breaking changes in 5.0.0 on [GitHub](https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md).
14
+
13
15
  ## Support
14
16
 
15
17
  - [Stack Overflow](http://stackoverflow.com/questions/tagged/mongoose)
@@ -57,7 +59,7 @@ First, we need to define a connection. If your app uses only one database, you s
57
59
  Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`.
58
60
 
59
61
  ```js
60
- var mongoose = require('mongoose');
62
+ const mongoose = require('mongoose');
61
63
 
62
64
  mongoose.connect('mongodb://localhost/my_database');
63
65
  ```
@@ -73,14 +75,14 @@ Once connected, the `open` event is fired on the `Connection` instance. If you'r
73
75
  Models are defined through the `Schema` interface.
74
76
 
75
77
  ```js
76
- var Schema = mongoose.Schema,
78
+ const Schema = mongoose.Schema,
77
79
  ObjectId = Schema.ObjectId;
78
80
 
79
- var BlogPost = new Schema({
80
- author : ObjectId,
81
- title : String,
82
- body : String,
83
- date : Date
81
+ const BlogPost = new Schema({
82
+ author: ObjectId,
83
+ title: String,
84
+ body: String,
85
+  date: Date
84
86
  });
85
87
  ```
86
88
 
@@ -100,7 +102,7 @@ Aside from defining the structure of your documents and the types of data you're
100
102
  The following example shows some of these features:
101
103
 
102
104
  ```js
103
- var Comment = new Schema({
105
+ const Comment = new Schema({
104
106
  name: { type: String, default: 'hahaha' },
105
107
  age: { type: Number, min: 18, index: true },
106
108
  bio: { type: String, match: /[a-z]/ },
@@ -127,19 +129,19 @@ Take a look at the example in `examples/schema.js` for an end-to-end example of
127
129
  Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function
128
130
 
129
131
  ```js
130
- var myModel = mongoose.model('ModelName');
132
+ const myModel = mongoose.model('ModelName');
131
133
  ```
132
134
 
133
135
  Or just do it all at once
134
136
 
135
137
  ```js
136
- var MyModel = mongoose.model('ModelName', mySchema);
138
+ const MyModel = mongoose.model('ModelName', mySchema);
137
139
  ```
138
140
 
139
141
  The first argument is the _singular_ name of the collection your model is for. **Mongoose automatically looks for the _plural_ version of your model name.** For example, if you use
140
142
 
141
143
  ```js
142
- var MyModel = mongoose.model('Ticket', mySchema);
144
+ const MyModel = mongoose.model('Ticket', mySchema);
143
145
  ```
144
146
 
145
147
  Then Mongoose will create the model for your __tickets__ collection, not your __ticket__ collection.
@@ -147,7 +149,7 @@ Then Mongoose will create the model for your __tickets__ collection, not your __
147
149
  Once we have our model, we can then instantiate it, and save it:
148
150
 
149
151
  ```js
150
- var instance = new MyModel();
152
+ const instance = new MyModel();
151
153
  instance.my.key = 'hello';
152
154
  instance.save(function (err) {
153
155
  //
@@ -167,18 +169,18 @@ You can also `findOne`, `findById`, `update`, etc. For more details check out [t
167
169
  **Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created:
168
170
 
169
171
  ```js
170
- var conn = mongoose.createConnection('your connection string'),
171
- MyModel = conn.model('ModelName', schema),
172
- m = new MyModel;
172
+ const conn = mongoose.createConnection('your connection string');
173
+ const MyModel = conn.model('ModelName', schema);
174
+ const m = new MyModel;
173
175
  m.save(); // works
174
176
  ```
175
177
 
176
178
  vs
177
179
 
178
180
  ```js
179
- var conn = mongoose.createConnection('your connection string'),
180
- MyModel = mongoose.model('ModelName', schema),
181
- m = new MyModel;
181
+ const conn = mongoose.createConnection('your connection string');
182
+ const MyModel = mongoose.model('ModelName', schema);
183
+ const m = new MyModel;
182
184
  m.save(); // does not work b/c the default connection object was never connected
183
185
  ```
184
186
 
package/browser.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Export lib/mongoose
3
+ *
4
+ */
5
+
6
+ module.exports = require('./lib/browser');
package/lib/aggregate.js CHANGED
@@ -339,6 +339,78 @@ Aggregate.prototype.unwind = function() {
339
339
  return this.append.apply(this, res);
340
340
  };
341
341
 
342
+ /**
343
+ * Appends a new $replaceRoot operator to this aggregate pipeline.
344
+ *
345
+ * Note that the `$replaceRoot` operator requires the new root to start with '$'.
346
+ * Mongoose will prepend '$' if the specified field doesn't start '$'.
347
+ *
348
+ * ####Examples:
349
+ *
350
+ * aggregate.replaceRoot("user");
351
+ *
352
+ * @see $replaceRoot https://docs.mongodb.org/manual/reference/operator/aggregation/replaceRoot
353
+ * @param {String} the field which will become the new root document
354
+ * @return {Aggregate}
355
+ * @api public
356
+ */
357
+
358
+ Aggregate.prototype.replaceRoot = function(newRoot) {
359
+ return this.append({
360
+ $replaceRoot: {
361
+ newRoot: (newRoot && newRoot.charAt(0) === '$') ? newRoot : '$' + newRoot
362
+ }
363
+ });
364
+ };
365
+
366
+ /**
367
+ * Appends a new $count operator to this aggregate pipeline.
368
+ *
369
+ * ####Examples:
370
+ *
371
+ * aggregate.count("userCount");
372
+ *
373
+ * @see $count https://docs.mongodb.org/manual/reference/operator/aggregation/count
374
+ * @param {String} the name of the count field
375
+ * @return {Aggregate}
376
+ * @api public
377
+ */
378
+
379
+ Aggregate.prototype.count = function(countName) {
380
+ return this.append({ $count: countName });
381
+ };
382
+
383
+ /**
384
+ * Appends a new $sortByCount operator to this aggregate pipeline. Accepts either a string field name
385
+ * or a pipeline object.
386
+ *
387
+ * Note that the `$sortByCount` operator requires the new root to start with '$'.
388
+ * Mongoose will prepend '$' if the specified field name doesn't start with '$'.
389
+ *
390
+ * ####Examples:
391
+ *
392
+ * aggregate.sortByCount('users');
393
+ * aggregate.sortByCount({ $mergeObjects: [ "$employee", "$business" ] })
394
+ *
395
+ * @see $sortByCount https://docs.mongodb.com/manual/reference/operator/aggregation/sortByCount/
396
+ * @param {Object|String} arg
397
+ * @return {Aggregate} this
398
+ * @api public
399
+ */
400
+
401
+ Aggregate.prototype.sortByCount = function(arg) {
402
+ if (arg && typeof arg === 'object') {
403
+ return this.append({ $sortByCount: arg });
404
+ } else if (typeof arg === 'string') {
405
+ return this.append({
406
+ $sortByCount: (arg && arg.charAt(0) === '$') ? arg : '$' + arg
407
+ });
408
+ } else {
409
+ throw new TypeError('Invalid arg "' + arg + '" to sortByCount(), ' +
410
+ 'must be string or object');
411
+ }
412
+ };
413
+
342
414
  /**
343
415
  * Appends new custom $lookup operator(s) to this aggregate pipeline.
344
416
  *
package/lib/cast.js CHANGED
@@ -1,13 +1,16 @@
1
+ 'use strict';
2
+
1
3
  /*!
2
4
  * Module dependencies.
3
5
  */
4
6
 
5
- var StrictModeError = require('./error/strict');
6
- var Types = require('./schema/index');
7
- var util = require('util');
8
- var utils = require('./utils');
7
+ const StrictModeError = require('./error/strict');
8
+ const Types = require('./schema/index');
9
+ const get = require('lodash.get');
10
+ const util = require('util');
11
+ const utils = require('./utils');
9
12
 
10
- var ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
13
+ const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
11
14
 
12
15
  /**
13
16
  * Handles internal casting for query filters.
@@ -67,6 +70,31 @@ module.exports = function cast(schema, obj, options, context) {
67
70
 
68
71
  schematype = schema.path(path);
69
72
 
73
+ // Check for embedded discriminator paths
74
+ if (!schematype) {
75
+ let split = path.split('.');
76
+ let j = split.length;
77
+ while (j--) {
78
+ let pathFirstHalf = split.slice(0, j).join('.');
79
+ let pathLastHalf = split.slice(j).join('.');
80
+ let _schematype = schema.path(pathFirstHalf);
81
+ let discriminatorKey = get(_schematype, 'schema.options.discriminatorKey');
82
+ // gh-6027: if we haven't found the schematype but this path is
83
+ // underneath an embedded discriminator and the embedded discriminator
84
+ // key is in the query, use the embedded discriminator schema
85
+ if (_schematype != null &&
86
+ get(_schematype, 'schema.discriminators') != null &&
87
+ discriminatorKey != null &&
88
+ pathLastHalf !== discriminatorKey) {
89
+ const discriminatorVal = get(obj, pathFirstHalf + '.' + discriminatorKey);
90
+ if (discriminatorVal) {
91
+ schematype = _schematype.schema.discriminators[discriminatorVal].
92
+ path(pathLastHalf);
93
+ }
94
+ }
95
+ }
96
+ }
97
+
70
98
  if (!schematype) {
71
99
  // Handle potential embedded array queries
72
100
  var split = path.split('.'),
@@ -206,7 +234,7 @@ module.exports = function cast(schema, obj, options, context) {
206
234
  } else if (options.strictQuery) {
207
235
  delete obj[path];
208
236
  }
209
- } else if (val === null || val === undefined) {
237
+ } else if (val == null) {
210
238
  obj[path] = null;
211
239
  continue;
212
240
  } else if (val.constructor.name === 'Object') {
package/lib/collection.js CHANGED
@@ -161,6 +161,22 @@ Collection.prototype.findAndModify = function() {
161
161
  throw new Error('Collection#findAndModify unimplemented by driver');
162
162
  };
163
163
 
164
+ /**
165
+ * Abstract method that drivers must implement.
166
+ */
167
+
168
+ Collection.prototype.findOneAndUpdate = function() {
169
+ throw new Error('Collection#findOneAndUpdate unimplemented by driver');
170
+ };
171
+
172
+ /**
173
+ * Abstract method that drivers must implement.
174
+ */
175
+
176
+ Collection.prototype.findOneAndDelete = function() {
177
+ throw new Error('Collection#findOneAndDelete unimplemented by driver');
178
+ };
179
+
164
180
  /**
165
181
  * Abstract method that drivers must implement.
166
182
  */
package/lib/connection.js CHANGED
@@ -59,7 +59,8 @@ function Connection(base) {
59
59
  this.pass = null;
60
60
  this.name = null;
61
61
  this.options = null;
62
- this.otherDbs = [];
62
+ this.otherDbs = []; // FIXME: To be replaced with relatedDbs
63
+ this.relatedDbs = {}; // Hashmap of other dbs that share underlying connection
63
64
  this.states = STATES;
64
65
  this._readyState = STATES.disconnected;
65
66
  this._closeCalled = false;
@@ -103,11 +104,16 @@ Object.defineProperty(Connection.prototype, 'readyState', {
103
104
 
104
105
  if (this._readyState !== val) {
105
106
  this._readyState = val;
106
- // loop over the otherDbs on this connection and change their state
107
+ // [legacy] loop over the otherDbs on this connection and change their state
107
108
  for (var i = 0; i < this.otherDbs.length; i++) {
108
109
  this.otherDbs[i].readyState = val;
109
110
  }
110
111
 
112
+ // loop over relatedDbs on this connection and change their state
113
+ for (var k in this.relatedDbs) {
114
+ this.relatedDbs[k].readyState = val;
115
+ }
116
+
111
117
  if (STATES.connected === val) {
112
118
  this._hasOpened = true;
113
119
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  var Readable = require('stream').Readable;
6
+ var eachAsync = require('../services/cursor/eachAsync');
6
7
  var util = require('util');
7
8
  var utils = require('../utils');
8
9
 
@@ -10,7 +11,7 @@ var utils = require('../utils');
10
11
  * An AggregationCursor is a concurrency primitive for processing aggregation
11
12
  * results one document at a time. It is analogous to QueryCursor.
12
13
  *
13
- * An AggregationCursor fulfills the [Node.js streams3 API](https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/),
14
+ * An AggregationCursor fulfills the Node.js streams3 API,
14
15
  * in addition to several other mechanisms for loading documents from MongoDB
15
16
  * one at a time.
16
17
  *
@@ -184,52 +185,23 @@ AggregationCursor.prototype.next = function(callback) {
184
185
  * Returns a promise that resolves when done.
185
186
  *
186
187
  * @param {Function} fn
188
+ * @param {Object} [options]
189
+ * @param {Number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
187
190
  * @param {Function} [callback] executed when all docs have been processed
188
191
  * @return {Promise}
189
192
  * @api public
190
193
  * @method eachAsync
191
194
  */
192
195
 
193
- AggregationCursor.prototype.eachAsync = function(fn, callback) {
194
- var handleNextResult = function(doc, callback) {
195
- var promise = fn(doc);
196
- if (promise && typeof promise.then === 'function') {
197
- promise.then(
198
- function() { callback(null); },
199
- function(error) { callback(error); });
200
- } else {
201
- callback(null);
202
- }
203
- };
204
-
205
- var iterate = callback => {
206
- return _next(this, function(error, doc) {
207
- if (error) {
208
- return callback(error);
209
- }
210
- if (!doc) {
211
- return callback(null);
212
- }
213
- handleNextResult(doc, function(error) {
214
- if (error) {
215
- return callback(error);
216
- }
217
- // Make sure to clear the stack re: gh-4697
218
- setTimeout(function() {
219
- iterate(callback);
220
- }, 0);
221
- });
222
- });
223
- };
196
+ AggregationCursor.prototype.eachAsync = function(fn, opts, callback) {
197
+ var _this = this;
198
+ if (typeof opts === 'function') {
199
+ callback = opts;
200
+ opts = {};
201
+ }
202
+ opts = opts || {};
224
203
 
225
- return utils.promiseOrCallback(callback, cb => {
226
- iterate(function(error) {
227
- if (error) {
228
- return cb(error);
229
- }
230
- cb(null);
231
- });
232
- });
204
+ return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts, callback);
233
205
  };
234
206
 
235
207
  /**
@@ -10,7 +10,7 @@ var utils = require('../utils');
10
10
 
11
11
  /**
12
12
  * A QueryCursor is a concurrency primitive for processing query results
13
- * one document at a time. A QueryCursor fulfills the [Node.js streams3 API](https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/),
13
+ * one document at a time. A QueryCursor fulfills the Node.js streams3 API,
14
14
  * in addition to several other mechanisms for loading documents from MongoDB
15
15
  * one at a time.
16
16
  *
package/lib/document.js CHANGED
@@ -87,6 +87,10 @@ function Document(obj, fields, skipId, options) {
87
87
 
88
88
  this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren);
89
89
 
90
+ // By default, defaults get applied **before** setting initial values
91
+ // Re: gh-6155
92
+ $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, true);
93
+
90
94
  if (obj) {
91
95
  if (obj instanceof Document) {
92
96
  this.isNew = obj.isNew;
@@ -99,7 +103,10 @@ function Document(obj, fields, skipId, options) {
99
103
  }
100
104
  }
101
105
 
102
- $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren);
106
+ // Function defaults get applied **after** setting initial values so they
107
+ // see the full doc rather than an empty one, unless they opt out.
108
+ // Re: gh-3781, gh-6155
109
+ $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, false);
103
110
 
104
111
  this.$__._id = this._id;
105
112
 
@@ -202,7 +209,7 @@ function $__hasIncludedChildren(fields) {
202
209
  * ignore
203
210
  */
204
211
 
205
- function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren) {
212
+ function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren, isBeforeSetters) {
206
213
  const paths = Object.keys(doc.schema.paths);
207
214
  const plen = paths.length;
208
215
 
@@ -222,6 +229,10 @@ function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren) {
222
229
  let doc_ = doc._doc;
223
230
 
224
231
  for (let j = 0; j < len; ++j) {
232
+ if (doc_ == null) {
233
+ break;
234
+ }
235
+
225
236
  let piece = path[j];
226
237
  curPath += (!curPath.length ? '' : '.') + piece;
227
238
 
@@ -242,6 +253,18 @@ function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren) {
242
253
  break;
243
254
  }
244
255
 
256
+ if (typeof type.defaultValue === 'function') {
257
+ if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
258
+ break;
259
+ }
260
+ if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
261
+ break;
262
+ }
263
+ } else if (!isBeforeSetters) {
264
+ // Non-function defaults should always run **before** setters
265
+ continue;
266
+ }
267
+
245
268
  if (fields && exclude !== null) {
246
269
  if (exclude === true) {
247
270
  // apply defaults to all non-excluded fields
@@ -1111,19 +1134,43 @@ Document.prototype.$ignore = function(path) {
1111
1134
  /**
1112
1135
  * Returns the list of paths that have been modified.
1113
1136
  *
1137
+ * @param {Object} [options]
1138
+ * @param {Boolean} [options.includeChildren=false] if true, returns children of modified paths as well. For example, if false, the list of modified paths for `doc.colors = { primary: 'blue' };` will **not** contain `colors.primary`
1114
1139
  * @return {Array}
1115
1140
  * @api public
1116
1141
  */
1117
1142
 
1118
- Document.prototype.modifiedPaths = function() {
1143
+ Document.prototype.modifiedPaths = function(options) {
1144
+ options = options || {};
1119
1145
  var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
1146
+ var _this = this;
1120
1147
  return directModifiedPaths.reduce(function(list, path) {
1121
1148
  var parts = path.split('.');
1122
- return list.concat(parts.reduce(function(chains, part, i) {
1149
+ list = list.concat(parts.reduce(function(chains, part, i) {
1123
1150
  return chains.concat(parts.slice(0, i).concat(part).join('.'));
1124
1151
  }, []).filter(function(chain) {
1125
1152
  return (list.indexOf(chain) === -1);
1126
1153
  }));
1154
+
1155
+ if (!options.includeChildren) {
1156
+ return list;
1157
+ }
1158
+
1159
+ var cur = _this.get(path);
1160
+ if (cur != null && typeof cur === 'object') {
1161
+ if (cur._doc) {
1162
+ cur = cur._doc;
1163
+ }
1164
+ Object.keys(cur).
1165
+ filter(function(key) {
1166
+ return list.indexOf(path + '.' + key) === -1;
1167
+ }).
1168
+ forEach(function(key) {
1169
+ list.push(path + '.' + key);
1170
+ });
1171
+ }
1172
+
1173
+ return list;
1127
1174
  }, []);
1128
1175
  };
1129
1176
 
@@ -1497,8 +1544,8 @@ function _getPathsToValidate(doc) {
1497
1544
  */
1498
1545
 
1499
1546
  Document.prototype.$__validate = function(callback) {
1500
- var _this = this;
1501
- var _complete = function() {
1547
+ const _this = this;
1548
+ const _complete = function() {
1502
1549
  var err = _this.$__.validationError;
1503
1550
  _this.$__.validationError = undefined;
1504
1551
  _this.emit('validate', _this);
@@ -1516,11 +1563,11 @@ Document.prototype.$__validate = function(callback) {
1516
1563
  };
1517
1564
 
1518
1565
  // only validate required fields when necessary
1519
- var paths = _getPathsToValidate(this);
1566
+ const paths = _getPathsToValidate(this);
1520
1567
 
1521
1568
  if (paths.length === 0) {
1522
1569
  return process.nextTick(function() {
1523
- var error = _complete();
1570
+ const error = _complete();
1524
1571
  if (error) {
1525
1572
  return _this.schema.s.hooks.execPost('validate:error', _this, [ _this], { error: error }, function(error) {
1526
1573
  callback(error);
@@ -1530,11 +1577,11 @@ Document.prototype.$__validate = function(callback) {
1530
1577
  });
1531
1578
  }
1532
1579
 
1533
- var validated = {};
1534
- var total = 0;
1580
+ const validated = {};
1581
+ let total = 0;
1535
1582
 
1536
1583
  var complete = function() {
1537
- var error = _complete();
1584
+ const error = _complete();
1538
1585
  if (error) {
1539
1586
  return _this.schema.s.hooks.execPost('validate:error', _this, [ _this], { error: error }, function(error) {
1540
1587
  callback(error);
@@ -1552,7 +1599,7 @@ Document.prototype.$__validate = function(callback) {
1552
1599
  total++;
1553
1600
 
1554
1601
  process.nextTick(function() {
1555
- var p = _this.schema.path(path);
1602
+ const p = _this.schema.path(path);
1556
1603
  if (!p) {
1557
1604
  return --total || complete();
1558
1605
  }
@@ -1563,10 +1610,11 @@ Document.prototype.$__validate = function(callback) {
1563
1610
  return;
1564
1611
  }
1565
1612
 
1566
- var val = _this.getValue(path);
1567
- var scope = path in _this.$__.pathsToScopes ?
1613
+ const val = _this.getValue(path);
1614
+ const scope = path in _this.$__.pathsToScopes ?
1568
1615
  _this.$__.pathsToScopes[path] :
1569
1616
  _this;
1617
+
1570
1618
  p.doValidate(val, function(err) {
1571
1619
  if (err) {
1572
1620
  _this.invalidate(path, err, undefined, true);
@@ -1576,8 +1624,8 @@ Document.prototype.$__validate = function(callback) {
1576
1624
  });
1577
1625
  };
1578
1626
 
1579
- var numPaths = paths.length;
1580
- for (var i = 0; i < numPaths; ++i) {
1627
+ const numPaths = paths.length;
1628
+ for (let i = 0; i < numPaths; ++i) {
1581
1629
  validatePath(paths[i]);
1582
1630
  }
1583
1631
  };
@@ -2647,8 +2695,26 @@ Document.prototype.depopulate = function(path) {
2647
2695
  if (typeof path === 'string') {
2648
2696
  path = path.split(' ');
2649
2697
  }
2650
- for (var i = 0; i < path.length; i++) {
2651
- var populatedIds = this.populated(path[i]);
2698
+ var i;
2699
+ var populatedIds;
2700
+
2701
+ if (arguments.length === 0) {
2702
+ // Depopulate all
2703
+ var keys = Object.keys(this.$__.populated);
2704
+
2705
+ for (i = 0; i < keys.length; i++) {
2706
+ populatedIds = this.populated(keys[i]);
2707
+ if (!populatedIds) {
2708
+ continue;
2709
+ }
2710
+ delete this.$__.populated[keys[i]];
2711
+ this.$set(keys[i], populatedIds);
2712
+ }
2713
+ return this;
2714
+ }
2715
+
2716
+ for (i = 0; i < path.length; i++) {
2717
+ populatedIds = this.populated(path[i]);
2652
2718
  if (!populatedIds) {
2653
2719
  continue;
2654
2720
  }
@@ -40,7 +40,10 @@ NativeConnection.prototype.__proto__ = MongooseConnection.prototype;
40
40
  * @api public
41
41
  */
42
42
 
43
- NativeConnection.prototype.useDb = function(name) {
43
+ NativeConnection.prototype.useDb = function(name, options) {
44
+ // Return immediately if cached
45
+ if (options && options.useCache && this.relatedDbs[name]) return this.relatedDbs[name];
46
+
44
47
  // we have to manually copy all of the attributes...
45
48
  var newConn = new this.constructor();
46
49
  newConn.name = name;
@@ -88,6 +91,12 @@ NativeConnection.prototype.useDb = function(name) {
88
91
  this.otherDbs.push(newConn);
89
92
  newConn.otherDbs.push(this);
90
93
 
94
+ // push onto the relatedDbs cache, this is used when state changes
95
+ if (options && options.useCache) {
96
+ this.relatedDbs[newConn.name] = newConn;
97
+ newConn.relatedDbs = this.relatedDbs;
98
+ }
99
+
91
100
  return newConn;
92
101
  };
93
102