mongoose 5.0.9 → 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,15 @@
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
+
1
13
  5.0.9 / 2018-03-05
2
14
  ==================
3
15
  * perf: bump mongodb -> 3.0.4 to fix SSL perf issue #6065
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/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
package/lib/index.js CHANGED
@@ -477,7 +477,7 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {
477
477
  });
478
478
 
479
479
  /*!
480
- * Driver depentend APIs
480
+ * Driver dependent APIs
481
481
  */
482
482
 
483
483
  var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
package/lib/query.js CHANGED
@@ -2145,8 +2145,6 @@ Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
2145
2145
  */
2146
2146
 
2147
2147
  Query.prototype._findOneAndUpdate = function(callback) {
2148
- this._castConditions();
2149
-
2150
2148
  if (this.error() != null) {
2151
2149
  return callback(this.error());
2152
2150
  }
@@ -2240,8 +2238,6 @@ Query.prototype.findOneAndRemove = function(conditions, options, callback) {
2240
2238
  * @api private
2241
2239
  */
2242
2240
  Query.prototype._findOneAndRemove = function(callback) {
2243
- this._castConditions();
2244
-
2245
2241
  if (this.error() != null) {
2246
2242
  return callback(this.error());
2247
2243
  }
@@ -94,7 +94,7 @@ function SchemaArray(key, cast, options, schemaOptions) {
94
94
  }
95
95
 
96
96
  if (!('defaultValue' in this) || this.defaultValue !== void 0) {
97
- this.default(function() {
97
+ var defaultFn = function() {
98
98
  var arr = [];
99
99
  if (fn) {
100
100
  arr = defaultArr();
@@ -103,7 +103,9 @@ function SchemaArray(key, cast, options, schemaOptions) {
103
103
  }
104
104
  // Leave it up to `cast()` to convert the array
105
105
  return arr;
106
- });
106
+ };
107
+ defaultFn.$runBeforeSetters = true;
108
+ this.default(defaultFn);
107
109
  }
108
110
  }
109
111
 
@@ -152,7 +152,6 @@ Embedded.prototype.cast = function(val, doc, init, priorVal) {
152
152
  }
153
153
  }
154
154
 
155
-
156
155
  var subdoc;
157
156
  if (init) {
158
157
  subdoc = new Constructor(void 0, doc ? doc.$__.selected : void 0, doc);
@@ -196,7 +195,22 @@ Embedded.prototype.castForQuery = function($conditional, val) {
196
195
  val = this._applySetters(val);
197
196
  }
198
197
 
199
- return new this.caster(val);
198
+ var Constructor = this.caster;
199
+ var discriminatorKey = Constructor.schema.options.discriminatorKey;
200
+ if (val != null &&
201
+ Constructor.discriminators &&
202
+ typeof val[discriminatorKey] === 'string') {
203
+ if (Constructor.discriminators[val[discriminatorKey]]) {
204
+ Constructor = Constructor.discriminators[val[discriminatorKey]];
205
+ } else {
206
+ var constructorByValue = getDiscriminatorByValue(Constructor, val[discriminatorKey]);
207
+ if (constructorByValue) {
208
+ Constructor = constructorByValue;
209
+ }
210
+ }
211
+ }
212
+
213
+ return new Constructor(val);
200
214
  };
201
215
 
202
216
  /**
@@ -206,6 +206,8 @@ function defaultId() {
206
206
  return new oid();
207
207
  }
208
208
 
209
+ defaultId.$runBeforeSetters = true;
210
+
209
211
  function resetId(v) {
210
212
  Document || (Document = require('./../document'));
211
213
 
package/lib/schematype.js CHANGED
@@ -245,15 +245,19 @@ SchemaType.prototype.sparse = function(bool) {
245
245
  * }
246
246
  *
247
247
  * // defining within the schema
248
- * var s = new Schema({ name: { type: String, set: capitalize }})
248
+ * var s = new Schema({ name: { type: String, set: capitalize }});
249
249
  *
250
- * // or by retreiving its SchemaType
250
+ * // or with the SchemaType
251
251
  * var s = new Schema({ name: String })
252
- * s.path('name').set(capitalize)
252
+ * s.path('name').set(capitalize);
253
253
  *
254
- * Setters allow you to transform the data before it gets to the raw mongodb document and is set as a value on an actual key.
254
+ * Setters allow you to transform the data before it gets to the raw mongodb
255
+ * document or query.
255
256
  *
256
- * Suppose you are implementing user registration for a website. Users provide an email and password, which gets saved to mongodb. The email is a string that you will want to normalize to lower case, in order to avoid one email having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM.
257
+ * Suppose you are implementing user registration for a website. Users provide
258
+ * an email and password, which gets saved to mongodb. The email is a string
259
+ * that you will want to normalize to lower case, in order to avoid one email
260
+ * having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM.
257
261
  *
258
262
  * You can set up email lower case normalization easily via a Mongoose setter.
259
263
  *
@@ -276,7 +280,8 @@ SchemaType.prototype.sparse = function(bool) {
276
280
  * console.log(user.email); // 'avenue@q.com'
277
281
  * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } }); // update to 'avenue@q.com'
278
282
  *
279
- * As you can see above, setters allow you to transform the data before it stored in MongoDB.
283
+ * As you can see above, setters allow you to transform the data before it
284
+ * stored in MongoDB, or before executing a query.
280
285
  *
281
286
  * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._
282
287
  *
@@ -303,6 +308,24 @@ SchemaType.prototype.sparse = function(bool) {
303
308
  * console.log(v.name); // name is required
304
309
  * console.log(v.taxonomy); // Parvovirinae
305
310
  *
311
+ * You can also use setters to modify other properties on the document. If
312
+ * you're setting a property `name` on a document, the setter will run with
313
+ * `this` as the document. Be careful, in mongoose 5 setters will also run
314
+ * when querying by `name` with `this` as the query.
315
+ *
316
+ * ```javascript
317
+ * const nameSchema = new Schema({ name: String, keywords: [String] });
318
+ * nameSchema.path('name').set(function(v) {
319
+ * // Need to check if `this` is a document, because in mongoose 5
320
+ * // setters will also run on queries, in which case `this` will be a
321
+ * // mongoose query object.
322
+ * if (this instanceof Document && v != null) {
323
+ * this.keywords = v.split(' ');
324
+ * }
325
+ * return v;
326
+ * });
327
+ * ```
328
+ *
306
329
  * @param {Function} fn
307
330
  * @return {SchemaType} this
308
331
  * @api public
package/migrating_to_5.md CHANGED
@@ -33,6 +33,21 @@ mongoose.connect('mongodb://localhost:27017/test');
33
33
  mongoose.model('Test', new Schema({}));
34
34
  ```
35
35
 
36
+ ### Connection Logic and `useMongoClient`
37
+
38
+ The [`useMongoClient` option](http://mongoosejs.com/docs/4.x/docs/connections.html#use-mongo-client) was
39
+ removed in Mongoose 5, it is now always `true`. As a consequence, Mongoose 5
40
+ no longer supports several function signatures for `mongoose.connect()` that
41
+ worked in Mongoose 4.x if the `useMongoClient` option was off. Below are some
42
+ examples of `mongoose.connect()` calls that do **not** work in Mongoose 5.x.
43
+
44
+ * `mongoose.connect('localhost', 27017);`
45
+ * `mongoose.connect('localhost', 'mydb', 27017);`
46
+ * `mongoose.connect('mongodb://host1:27017,mongodb://host2:27017');`
47
+
48
+ In Mongoose 5.x, the first parameter to `mongoose.connect()` and `mongoose.createConnection()`, if specified, **must** be a [MongoDB connection string](https://docs.mongodb.com/manual/reference/connection-string/). The
49
+ connection string and options are then passed down to [the MongoDB Node.js driver's `MongoClient.connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#.connect). Mongoose does not modify the connection string, although `mongoose.connect()` and `mongoose.createConnection()` support a [few additional options in addition to the ones the MongoDB driver supports](http://mongoosejs.com/docs/connections.html#options).
50
+
36
51
  ### Setter Order
37
52
 
38
53
  Setters run in reverse order in 4.x:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "5.0.9",
4
+ "version": "5.0.10",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",