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/lib/index.js CHANGED
@@ -79,6 +79,7 @@ Mongoose.prototype.STATES = STATES;
79
79
  * Currently supported options are:
80
80
  * - 'debug': prints the operations mongoose sends to MongoDB to the console
81
81
  * - 'bufferCommands': enable/disable mongoose's buffering mechanism for all connections and models
82
+ * - 'useFindAndModify': true by default. Set to `false` to make `findOneAndUpdate()` and `findOneAndRemove()` use native `findOneAndUpdate()` rather than `findAndModify()`.
82
83
  *
83
84
  * @param {String} key
84
85
  * @param {String|Function|Boolean} value
@@ -476,7 +477,7 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {
476
477
  });
477
478
 
478
479
  /*!
479
- * Driver depentend APIs
480
+ * Driver dependent APIs
480
481
  */
481
482
 
482
483
  var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
package/lib/model.js CHANGED
@@ -22,6 +22,7 @@ var applyStatics = require('./services/model/applyStatics');
22
22
  var cast = require('./cast');
23
23
  var castUpdate = require('./services/query/castUpdate');
24
24
  var discriminator = require('./services/model/discriminator');
25
+ var getDiscriminatorByValue = require('./queryhelpers').getDiscriminatorByValue;
25
26
  var internalToObjectOptions = require('./options').internalToObjectOptions;
26
27
  var isPathSelectedInclusive = require('./services/projection/isPathSelectedInclusive');
27
28
  var get = require('lodash.get');
@@ -829,10 +830,11 @@ Model.prototype.model = function model(name) {
829
830
  *
830
831
  * @param {String} name discriminator model name
831
832
  * @param {Schema} schema discriminator model schema
833
+ * @param {String} value discriminator model typeKey value
832
834
  * @api public
833
835
  */
834
836
 
835
- Model.discriminator = function(name, schema) {
837
+ Model.discriminator = function(name, schema, value) {
836
838
  var model;
837
839
  if (typeof name === 'function') {
838
840
  model = name;
@@ -842,7 +844,7 @@ Model.discriminator = function(name, schema) {
842
844
  }
843
845
  }
844
846
 
845
- schema = discriminator(this, name, schema);
847
+ schema = discriminator(this, name, schema, value);
846
848
  if (this.db.models[name]) {
847
849
  throw new OverwriteModelError(name);
848
850
  }
@@ -2034,7 +2036,7 @@ Model.create = function create(doc, callback) {
2034
2036
  args.forEach(doc => {
2035
2037
  toExecute.push(callback => {
2036
2038
  var Model = this.discriminators && doc[discriminatorKey] ?
2037
- this.discriminators[doc[discriminatorKey]] :
2039
+ this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this, doc[discriminatorKey]) :
2038
2040
  this;
2039
2041
  var toSave = doc;
2040
2042
  var callbackWrapper = (error, doc) => {
package/lib/query.js CHANGED
@@ -1021,6 +1021,15 @@ Query.prototype.setOptions = function(options, overwrite) {
1021
1021
  }
1022
1022
  }
1023
1023
 
1024
+ if ('useFindAndModify' in options) {
1025
+ this._mongooseOptions.useFindAndModify = options.useFindAndModify;
1026
+ delete options.useFindAndModify;
1027
+ }
1028
+ if ('omitUndefined' in options) {
1029
+ this._mongooseOptions.omitUndefined = options.omitUndefined;
1030
+ delete options.omitUndefined;
1031
+ }
1032
+
1024
1033
  return Query.base.setOptions.call(this, options);
1025
1034
  };
1026
1035
 
@@ -1437,14 +1446,14 @@ function completeMany(model, docs, fields, userProvidedFields, pop, callback) {
1437
1446
  var opts = pop ? { populated: pop } : undefined;
1438
1447
  var error = null;
1439
1448
  function init(_error) {
1449
+ if (_error != null) {
1450
+ error = error || _error;
1451
+ }
1440
1452
  if (error != null) {
1453
+ --count || callback(error);
1441
1454
  return;
1442
1455
  }
1443
- if (_error != null) {
1444
- error = _error;
1445
- return callback(error);
1446
- }
1447
- --count || callback(null, arr);
1456
+ --count || callback(error, arr);
1448
1457
  }
1449
1458
  for (var i = 0; i < len; ++i) {
1450
1459
  arr[i] = helpers.createModel(model, docs[i], fields, userProvidedFields);
@@ -1590,13 +1599,6 @@ Query.prototype.findOne = function(conditions, projection, options, callback) {
1590
1599
  this.merge(conditions);
1591
1600
 
1592
1601
  prepareDiscriminatorCriteria(this);
1593
-
1594
- try {
1595
- this.cast(this.model);
1596
- this.error(null);
1597
- } catch (err) {
1598
- this.error(err);
1599
- }
1600
1602
  } else if (conditions != null) {
1601
1603
  this.error(new ObjectParameterError(conditions, 'filter', 'findOne'));
1602
1604
  }
@@ -2143,8 +2145,6 @@ Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
2143
2145
  */
2144
2146
 
2145
2147
  Query.prototype._findOneAndUpdate = function(callback) {
2146
- this._castConditions();
2147
-
2148
2148
  if (this.error() != null) {
2149
2149
  return callback(this.error());
2150
2150
  }
@@ -2238,8 +2238,6 @@ Query.prototype.findOneAndRemove = function(conditions, options, callback) {
2238
2238
  * @api private
2239
2239
  */
2240
2240
  Query.prototype._findOneAndRemove = function(callback) {
2241
- this._castConditions();
2242
-
2243
2241
  if (this.error() != null) {
2244
2242
  return callback(this.error());
2245
2243
  }
@@ -2377,8 +2375,68 @@ Query.prototype._findAndModify = function(type, callback) {
2377
2375
  });
2378
2376
  };
2379
2377
 
2378
+ var _callback;
2379
+
2380
+ var useFindAndModify = true;
2381
+ var base = _this.model && _this.model.base;
2382
+ if ('useFindAndModify' in base.options) {
2383
+ useFindAndModify = base.get('useFindAndModify');
2384
+ }
2385
+ if ('useFindAndModify' in options) {
2386
+ useFindAndModify = options.useFindAndModify;
2387
+ }
2388
+ if (useFindAndModify === false) {
2389
+ // Bypass mquery
2390
+ var collection = _this._collection.collection;
2391
+ if ('new' in opts) {
2392
+ opts.returnOriginal = !opts['new'];
2393
+ delete opts['new'];
2394
+ }
2395
+ if ('fields' in opts) {
2396
+ opts.projection = opts.fields;
2397
+ delete opts.fields;
2398
+ }
2399
+
2400
+ if (type === 'remove') {
2401
+ collection.findOneAndDelete(castedQuery, opts, utils.tick(function(error, res) {
2402
+ return cb(error, res ? res.value : res, res);
2403
+ }));
2404
+
2405
+ return this;
2406
+ }
2407
+
2408
+ if (opts.runValidators && doValidate) {
2409
+ _callback = function(error) {
2410
+ if (error) {
2411
+ return callback(error);
2412
+ }
2413
+ if (castedDoc && castedDoc.toBSON) {
2414
+ castedDoc = castedDoc.toBSON();
2415
+ }
2416
+ collection.findOneAndUpdate(castedQuery, castedDoc, opts, utils.tick(function(error, res) {
2417
+ return cb(error, res ? res.value : res, res);
2418
+ }));
2419
+ };
2420
+
2421
+ try {
2422
+ doValidate(_callback);
2423
+ } catch (error) {
2424
+ callback(error);
2425
+ }
2426
+ } else {
2427
+ if (castedDoc && castedDoc.toBSON) {
2428
+ castedDoc = castedDoc.toBSON();
2429
+ }
2430
+ collection.findOneAndUpdate(castedQuery, castedDoc, opts, utils.tick(function(error, res) {
2431
+ return cb(error, res ? res.value : res, res);
2432
+ }));
2433
+ }
2434
+
2435
+ return this;
2436
+ }
2437
+
2380
2438
  if (opts.runValidators && doValidate) {
2381
- var _callback = function(error) {
2439
+ _callback = function(error) {
2382
2440
  if (error) {
2383
2441
  return callback(error);
2384
2442
  }
@@ -3006,9 +3064,16 @@ Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
3006
3064
  } else {
3007
3065
  strict = true;
3008
3066
  }
3067
+
3068
+ var omitUndefined = false;
3069
+ if ('omitUndefined' in this._mongooseOptions) {
3070
+ omitUndefined = this._mongooseOptions.omitUndefined;
3071
+ }
3072
+
3009
3073
  return castUpdate(this.schema, obj, {
3010
3074
  overwrite: overwrite,
3011
- strict: strict
3075
+ strict: strict,
3076
+ omitUndefined
3012
3077
  }, this);
3013
3078
  };
3014
3079
 
@@ -3214,8 +3279,7 @@ Query.prototype._applyPaths = function applyPaths() {
3214
3279
 
3215
3280
  /**
3216
3281
  * Returns a wrapper around a [mongodb driver cursor](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html).
3217
- * A QueryCursor exposes a [Streams3](https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/)-compatible
3218
- * interface, as well as a `.next()` function.
3282
+ * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function.
3219
3283
  *
3220
3284
  * The `.cursor()` function triggers pre find hooks, but **not** post find hooks.
3221
3285
  *
@@ -46,6 +46,34 @@ exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query,
46
46
  return pop;
47
47
  };
48
48
 
49
+
50
+ /*!
51
+ * returns discriminator by discriminatorMapping.value
52
+ *
53
+ * @param {Model} model
54
+ * @param {string} value
55
+ */
56
+ function getDiscriminatorByValue(model, value) {
57
+ var discriminator = null;
58
+ if (!model.discriminators) {
59
+ return discriminator;
60
+ }
61
+ for (var name in model.discriminators) {
62
+ var it = model.discriminators[name];
63
+ if (
64
+ it.schema &&
65
+ it.schema.discriminatorMapping &&
66
+ it.schema.discriminatorMapping.value == value
67
+ ) {
68
+ discriminator = it;
69
+ break;
70
+ }
71
+ }
72
+ return discriminator;
73
+ }
74
+
75
+ exports.getDiscriminatorByValue = getDiscriminatorByValue;
76
+
49
77
  /*!
50
78
  * If the document is a mapped discriminator type, it returns a model instance for that type, otherwise,
51
79
  * it returns an instance of the given model.
@@ -65,11 +93,14 @@ exports.createModel = function createModel(model, doc, fields, userProvidedField
65
93
  ? discriminatorMapping.key
66
94
  : null;
67
95
 
68
- if (key && doc[key] && model.discriminators && model.discriminators[doc[key]]) {
69
- var discriminator = model.discriminators[doc[key]];
70
- var _fields = utils.clone(userProvidedFields);
71
- exports.applyPaths(_fields, discriminator.schema);
72
- return new model.discriminators[doc[key]](undefined, _fields, true);
96
+ var value = doc[key];
97
+ if (key && value && model.discriminators) {
98
+ var discriminator = model.discriminators[value] || getDiscriminatorByValue(model, value);
99
+ if (discriminator) {
100
+ var _fields = utils.clone(userProvidedFields);
101
+ exports.applyPaths(_fields, discriminator.schema);
102
+ return new discriminator(undefined, _fields, true);
103
+ }
73
104
  }
74
105
 
75
106
  return new model(undefined, fields, true);
@@ -22,6 +22,7 @@ var util = require('util');
22
22
  var utils = require('../utils');
23
23
  var castToNumber = require('./operators/helpers').castToNumber;
24
24
  var geospatial = require('./operators/geospatial');
25
+ var getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
25
26
 
26
27
  var MongooseArray;
27
28
  var EmbeddedDoc;
@@ -93,7 +94,7 @@ function SchemaArray(key, cast, options, schemaOptions) {
93
94
  }
94
95
 
95
96
  if (!('defaultValue' in this) || this.defaultValue !== void 0) {
96
- this.default(function() {
97
+ var defaultFn = function() {
97
98
  var arr = [];
98
99
  if (fn) {
99
100
  arr = defaultArr();
@@ -102,7 +103,9 @@ function SchemaArray(key, cast, options, schemaOptions) {
102
103
  }
103
104
  // Leave it up to `cast()` to convert the array
104
105
  return arr;
105
- });
106
+ };
107
+ defaultFn.$runBeforeSetters = true;
108
+ this.default(defaultFn);
106
109
  }
107
110
  }
108
111
 
@@ -247,10 +250,18 @@ SchemaArray.prototype.castForQuery = function($conditional, value) {
247
250
 
248
251
  if (val &&
249
252
  Constructor.discriminators &&
250
- Constructor.schema.options.discriminatorKey &&
251
- typeof val[Constructor.schema.options.discriminatorKey] === 'string' &&
252
- Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]]) {
253
- Constructor = Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]];
253
+ Constructor.schema &&
254
+ Constructor.schema.options &&
255
+ Constructor.schema.options.discriminatorKey) {
256
+ if (typeof val[Constructor.schema.options.discriminatorKey] === 'string' &&
257
+ Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]]) {
258
+ Constructor = Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]];
259
+ } else {
260
+ var constructorByValue = getDiscriminatorByValue(Constructor, val[Constructor.schema.options.discriminatorKey]);
261
+ if (constructorByValue) {
262
+ Constructor = constructorByValue;
263
+ }
264
+ }
254
265
  }
255
266
 
256
267
  var proto = this.casterConstructor.prototype;
@@ -12,6 +12,7 @@ var SchemaType = require('../schematype');
12
12
  var discriminator = require('../services/model/discriminator');
13
13
  var util = require('util');
14
14
  var utils = require('../utils');
15
+ var getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
15
16
 
16
17
  var MongooseDocumentArray;
17
18
  var Subdocument;
@@ -181,13 +182,7 @@ DocumentArray.prototype.doValidate = function(array, fn, scope, options) {
181
182
  undefined, i);
182
183
  }
183
184
 
184
- // HACK: use $__original_validate to avoid promises so bluebird doesn't
185
- // complain
186
- if (doc.$__original_validate) {
187
- doc.$__original_validate({__noPromise: true}, callback);
188
- } else {
189
- doc.validate({__noPromise: true}, callback);
190
- }
185
+ doc.$__validate(callback);
191
186
  }
192
187
  }, scope);
193
188
  };
@@ -297,9 +292,17 @@ DocumentArray.prototype.cast = function(value, doc, init, prev, options) {
297
292
 
298
293
  var Constructor = this.casterConstructor;
299
294
  if (Constructor.discriminators &&
300
- typeof value[i][Constructor.schema.options.discriminatorKey] === 'string' &&
301
- Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]]) {
302
- Constructor = Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]];
295
+ Constructor.schema &&
296
+ Constructor.schema.options &&
297
+ typeof value[i][Constructor.schema.options.discriminatorKey] === 'string') {
298
+ if (Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]]) {
299
+ Constructor = Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]];
300
+ } else {
301
+ var constructorByValue = getDiscriminatorByValue(Constructor, value[i][Constructor.schema.options.discriminatorKey]);
302
+ if (constructorByValue) {
303
+ Constructor = constructorByValue;
304
+ }
305
+ }
303
306
  }
304
307
 
305
308
  // Check if the document has a different schema (re gh-3701)
@@ -11,6 +11,7 @@ const castToNumber = require('./operators/helpers').castToNumber;
11
11
  const discriminator = require('../services/model/discriminator');
12
12
  const geospatial = require('./operators/geospatial');
13
13
  const internalToObjectOptions = require('../options').internalToObjectOptions;
14
+ var getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
14
15
 
15
16
  let Subdocument;
16
17
 
@@ -140,9 +141,15 @@ Embedded.prototype.cast = function(val, doc, init, priorVal) {
140
141
  var discriminatorKey = Constructor.schema.options.discriminatorKey;
141
142
  if (val != null &&
142
143
  Constructor.discriminators &&
143
- typeof val[discriminatorKey] === 'string' &&
144
- Constructor.discriminators[val[discriminatorKey]]) {
145
- Constructor = Constructor.discriminators[val[discriminatorKey]];
144
+ typeof val[discriminatorKey] === 'string') {
145
+ if (Constructor.discriminators[val[discriminatorKey]]) {
146
+ Constructor = Constructor.discriminators[val[discriminatorKey]];
147
+ } else {
148
+ var constructorByValue = getDiscriminatorByValue(Constructor, val[discriminatorKey]);
149
+ if (constructorByValue) {
150
+ Constructor = constructorByValue;
151
+ }
152
+ }
146
153
  }
147
154
 
148
155
  var subdoc;
@@ -188,7 +195,22 @@ Embedded.prototype.castForQuery = function($conditional, val) {
188
195
  val = this._applySetters(val);
189
196
  }
190
197
 
191
- 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);
192
214
  };
193
215
 
194
216
  /**
@@ -202,11 +224,18 @@ Embedded.prototype.doValidate = function(value, fn, scope) {
202
224
  var discriminatorKey = Constructor.schema.options.discriminatorKey;
203
225
  if (value != null &&
204
226
  Constructor.discriminators &&
205
- typeof value[discriminatorKey] === 'string' &&
206
- Constructor.discriminators[value[discriminatorKey]]) {
207
- Constructor = Constructor.discriminators[value[discriminatorKey]];
227
+ typeof value[discriminatorKey] === 'string') {
228
+ if (Constructor.discriminators[value[discriminatorKey]]) {
229
+ Constructor = Constructor.discriminators[value[discriminatorKey]];
230
+ } else {
231
+ var constructorByValue = getDiscriminatorByValue(Constructor, value[discriminatorKey]);
232
+ if (constructorByValue) {
233
+ Constructor = constructorByValue;
234
+ }
235
+ }
208
236
  }
209
237
 
238
+
210
239
  SchemaType.prototype.doValidate.call(this, value, function(error) {
211
240
  if (error) {
212
241
  return fn(error);
@@ -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/schema.js CHANGED
@@ -29,7 +29,6 @@ var mpath = require('mpath');
29
29
  * - [bufferCommands](/docs/guide.html#bufferCommands): bool - defaults to true
30
30
  * - [capped](/docs/guide.html#capped): bool - defaults to false
31
31
  * - [collection](/docs/guide.html#collection): string - no default
32
- * - [emitIndexErrors](/docs/guide.html#emitIndexErrors): bool - defaults to false.
33
32
  * - [id](/docs/guide.html#id): bool - defaults to true
34
33
  * - [_id](/docs/guide.html#_id): bool - defaults to true
35
34
  * - `minimize`: bool - controls [document#toObject](#document_Document-toObject) behavior when called manually - defaults to true
@@ -1333,33 +1332,39 @@ Schema.prototype.indexes = function() {
1333
1332
  if (path.options.excludeIndexes !== true) {
1334
1333
  collectIndexes(path.schema, prefix + key + '.');
1335
1334
  }
1336
- } else {
1337
- index = path._index || (path.caster && path.caster._index);
1338
-
1339
- if (index !== false && index !== null && index !== undefined) {
1340
- field = {};
1341
- isObject = utils.isObject(index);
1342
- options = isObject ? index : {};
1343
- type = typeof index === 'string' ? index :
1344
- isObject ? index.type :
1345
- false;
1346
-
1347
- if (type && ~Schema.indexTypes.indexOf(type)) {
1348
- field[prefix + key] = type;
1349
- } else if (options.text) {
1350
- field[prefix + key] = 'text';
1351
- delete options.text;
1352
- } else {
1353
- field[prefix + key] = 1;
1354
- }
1355
1335
 
1356
- delete options.type;
1357
- if (!('background' in options)) {
1358
- options.background = true;
1359
- }
1336
+ // Retained to minimize risk of backwards breaking changes due to
1337
+ // gh-6113
1338
+ if ((path instanceof MongooseTypes.DocumentArray)) {
1339
+ continue;
1340
+ }
1341
+ }
1360
1342
 
1361
- indexes.push([field, options]);
1343
+ index = path._index || (path.caster && path.caster._index);
1344
+
1345
+ if (index !== false && index !== null && index !== undefined) {
1346
+ field = {};
1347
+ isObject = utils.isObject(index);
1348
+ options = isObject ? index : {};
1349
+ type = typeof index === 'string' ? index :
1350
+ isObject ? index.type :
1351
+ false;
1352
+
1353
+ if (type && ~Schema.indexTypes.indexOf(type)) {
1354
+ field[prefix + key] = type;
1355
+ } else if (options.text) {
1356
+ field[prefix + key] = 'text';
1357
+ delete options.text;
1358
+ } else {
1359
+ field[prefix + key] = 1;
1362
1360
  }
1361
+
1362
+ delete options.type;
1363
+ if (!('background' in options)) {
1364
+ options.background = true;
1365
+ }
1366
+
1367
+ indexes.push([field, options]);
1363
1368
  }
1364
1369
  }
1365
1370
 
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
@@ -14,7 +14,7 @@ var CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
14
14
  * ignore
15
15
  */
16
16
 
17
- module.exports = function discriminator(model, name, schema) {
17
+ module.exports = function discriminator(model, name, schema, tiedValue) {
18
18
  if (!(schema && schema.instanceOfSchema)) {
19
19
  throw new Error('You must pass a valid discriminator Schema');
20
20
  }
@@ -46,6 +46,11 @@ module.exports = function discriminator(model, name, schema) {
46
46
  '" cannot have field with name "' + key + '"');
47
47
  }
48
48
 
49
+ var value = name;
50
+ if (typeof tiedValue == 'string' && tiedValue.length) {
51
+ value = tiedValue;
52
+ }
53
+
49
54
  function merge(schema, baseSchema) {
50
55
  if (baseSchema.paths._id &&
51
56
  baseSchema.paths._id.options &&
@@ -59,11 +64,11 @@ module.exports = function discriminator(model, name, schema) {
59
64
 
60
65
  var obj = {};
61
66
  obj[key] = {
62
- default: name,
67
+ default: value,
63
68
  select: true,
64
69
  set: function(newName) {
65
- if (newName === name) {
66
- return name;
70
+ if (newName === value) {
71
+ return value;
67
72
  }
68
73
  throw new Error('Can\'t set discriminator key "' + key + '"');
69
74
  },
@@ -71,7 +76,7 @@ module.exports = function discriminator(model, name, schema) {
71
76
  };
72
77
  obj[key][schema.options.typeKey] = String;
73
78
  schema.add(obj);
74
- schema.discriminatorMapping = {key: key, value: name, isRoot: false};
79
+ schema.discriminatorMapping = {key: key, value: value, isRoot: false};
75
80
 
76
81
  if (baseSchema.options.collection) {
77
82
  schema.options.collection = baseSchema.options.collection;