mongoose 4.12.2 → 4.12.6

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/.travis.yml CHANGED
@@ -18,3 +18,5 @@ script:
18
18
  - npm test
19
19
  - npm run lint
20
20
  - npm run nsp
21
+ notifications:
22
+ email: false
package/History.md CHANGED
@@ -1,4 +1,36 @@
1
- 4.12.2 / 2017-10-13
1
+ 4.12.6 / 2017-11-01
2
+ ===================
3
+ * fix(schema): make clone() copy query helpers correctly #5752
4
+ * fix: undeprecate `ensureIndex()` and use it by default #3280
5
+
6
+ 4.12.5 / 2017-10-29
7
+ ===================
8
+ * fix(query): correctly handle `$in` and required for $pull and update validators #5744
9
+ * feat(aggegate): add $addFields pipeline operator #5740 [AyushG3112](https://github.com/AyushG3112)
10
+ * fix(document): catch sync errors in document pre hooks and report as error #5738
11
+ * fix(populate): handle slice projections correctly when automatically selecting populated fields #5737
12
+ * fix(discriminator): fix hooks for embedded discriminators #5706 [wlingke](https://github.com/wlingke)
13
+ * fix(model): throw sane error when customer calls `mongoose.Model()` over `mongoose.model()` #2005
14
+
15
+ 4.12.4 / 2017-10-21
16
+ ===================
17
+ * test(plugins): add coverage for idGetter with id as a schema property #5713 [wlingke](https://github.com/wlingke)
18
+ * fix(model): avoid copying recursive $$context object when creating discriminator after querying #5721
19
+ * fix(connection): ensure connection promise helpers are removed before emitting 'connected' #5714
20
+ * docs(schema): add notes about runSettersOnQuery to schema setters #5705
21
+ * fix(collection): ensure queued operations run on the next tick #5562
22
+
23
+ 4.12.3 / 2017-10-16
24
+ ===================
25
+ * fix(connection): emit 'reconnect' event as well as 'reconnected' for consistency with driver #5719
26
+ * fix: correctly bubble up left/joined events for replica set #5718
27
+ * fix(connection): allow passing in `autoIndex` as top-level option rather than requiring `config.autoIndex` #5711
28
+ * docs(connection): improve docs regarding reconnectTries, autoReconnect, and bufferMaxEntries #5711
29
+ * fix(query): handle null with addToSet/push/pull/pullAll update validators #5710
30
+ * fix(model): handle setDefaultsOnInsert option for bulkWrite updateOne and updateMany #5708
31
+ * fix(query): avoid infinite recursion edge case when cloning a buffer #5702
32
+
33
+ 4.12.2 / 2017-10-14
2
34
  ===================
3
35
  * docs(faq): add FAQ about using arrow functions for getters/setters, virtuals, and methods #5700
4
36
  * docs(schema): document the childSchemas property and add to public API #5695
package/lib/collection.js CHANGED
@@ -80,7 +80,10 @@ Collection.prototype.conn;
80
80
 
81
81
  Collection.prototype.onOpen = function() {
82
82
  this.buffer = false;
83
- this.doQueue();
83
+ var _this = this;
84
+ setImmediate(function() {
85
+ _this.doQueue();
86
+ });
84
87
  };
85
88
 
86
89
  /**
package/lib/connection.js CHANGED
@@ -763,9 +763,13 @@ Connection.prototype.openUri = function(uri, options, callback) {
763
763
  if (options) {
764
764
  options = utils.clone(options, { retainKeyOrder: true });
765
765
  delete options.useMongoClient;
766
- if (options.config && options.config.autoIndex != null) {
767
- this.config.autoIndex = options.config.autoIndex !== false;
766
+ var autoIndex = options.config && options.config.autoIndex != null ?
767
+ options.config.autoIndex :
768
+ options.autoIndex;
769
+ if (autoIndex != null) {
770
+ this.config.autoIndex = autoIndex !== false;
768
771
  delete options.config;
772
+ delete options.autoIndex;
769
773
  }
770
774
 
771
775
  // Backwards compat
@@ -795,6 +799,7 @@ Connection.prototype.openUri = function(uri, options, callback) {
795
799
  // Backwards compat for mongoose 4.x
796
800
  db.on('reconnect', function() {
797
801
  _this.readyState = STATES.connected;
802
+ _this.emit('reconnect');
798
803
  _this.emit('reconnected');
799
804
  });
800
805
  db.s.topology.on('reconnectFailed', function() {
@@ -808,6 +813,9 @@ Connection.prototype.openUri = function(uri, options, callback) {
808
813
  _this.emit('timeout');
809
814
  });
810
815
 
816
+ delete _this.then;
817
+ delete _this.catch;
818
+
811
819
  _this.db = db;
812
820
  _this.readyState = STATES.connected;
813
821
 
@@ -818,8 +826,6 @@ Connection.prototype.openUri = function(uri, options, callback) {
818
826
  }
819
827
 
820
828
  callback && callback(null, _this);
821
- delete _this.then;
822
- delete _this.catch;
823
829
  resolve(_this);
824
830
  _this.emit('open');
825
831
  });
@@ -4,7 +4,6 @@
4
4
 
5
5
  var MongooseCollection = require('../../collection');
6
6
  var Collection = require('mongodb').Collection;
7
- var util = require('util');
8
7
  var utils = require('../../utils');
9
8
 
10
9
  /**
@@ -156,13 +155,6 @@ for (var i in Collection.prototype) {
156
155
  iter(i);
157
156
  }
158
157
 
159
- /*!
160
- * ignore
161
- */
162
-
163
- Collection.prototype.ensureIndex = util.deprecate(Collection.prototype.ensureIndex,
164
- '`ensureIndex()` is deprecated in Mongoose >= 4.12.0, use `createIndex()` instead');
165
-
166
158
  /**
167
159
  * Debug print helper
168
160
  *
@@ -162,6 +162,7 @@ function listen(conn) {
162
162
  });
163
163
  conn.db.on('reconnect', function() {
164
164
  conn.readyState = STATES.connected;
165
+ conn.emit('reconnect');
165
166
  conn.emit('reconnected');
166
167
  conn.onOpen();
167
168
  });
@@ -171,6 +172,7 @@ function listen(conn) {
171
172
  conn.db.on('open', function(err, db) {
172
173
  if (STATES.disconnected === conn.readyState && db && db.databaseName) {
173
174
  conn.readyState = STATES.connected;
175
+ conn.emit('reconnect');
174
176
  conn.emit('reconnected');
175
177
  }
176
178
  });
@@ -204,6 +206,14 @@ NativeConnection.prototype.doOpenSet = function(fn) {
204
206
  : new ReplSetServers(servers, this.options.replset || this.options.replSet);
205
207
  this.db = new Db(this.name, server, this.options.db);
206
208
 
209
+ this.db.s.topology.on('left', function(data) {
210
+ _this.emit('left', data);
211
+ });
212
+
213
+ this.db.s.topology.on('joined', function(data) {
214
+ _this.emit('joined', data);
215
+ });
216
+
207
217
  this.db.on('fullsetup', function() {
208
218
  _this.emit('fullsetup');
209
219
  });
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
 
7
7
  /*!
8
8
  * MissingSchema Error constructor.
package/lib/error/cast.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
  var util = require('util');
7
7
 
8
8
  /**
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
 
7
7
  /**
8
8
  * Casting Error constructor.
@@ -3,7 +3,7 @@
3
3
  * Module dependencies.
4
4
  */
5
5
 
6
- var MongooseError = require('../error.js');
6
+ var MongooseError = require('./');
7
7
 
8
8
  /*!
9
9
  * DivergentArrayError constructor.
@@ -37,7 +37,7 @@ module.exports = exports = MongooseError;
37
37
  * @api public
38
38
  */
39
39
 
40
- MongooseError.messages = require('./error/messages');
40
+ MongooseError.messages = require('./messages');
41
41
 
42
42
  // backward compat
43
43
  MongooseError.Messages = MongooseError.messages;
@@ -51,16 +51,16 @@ MongooseError.Messages = MongooseError.messages;
51
51
  * @api public
52
52
  */
53
53
 
54
- MongooseError.DocumentNotFoundError = require('./error/notFound');
54
+ MongooseError.DocumentNotFoundError = require('./notFound');
55
55
 
56
56
  /*!
57
57
  * Expose subclasses
58
58
  */
59
59
 
60
- MongooseError.CastError = require('./error/cast');
61
- MongooseError.ValidationError = require('./error/validation');
62
- MongooseError.ValidatorError = require('./error/validator');
63
- MongooseError.VersionError = require('./error/version');
64
- MongooseError.OverwriteModelError = require('./error/overwriteModel');
65
- MongooseError.MissingSchemaError = require('./error/missingSchema');
66
- MongooseError.DivergentArrayError = require('./error/divergentArray');
60
+ MongooseError.CastError = require('./cast');
61
+ MongooseError.ValidationError = require('./validation');
62
+ MongooseError.ValidatorError = require('./validator');
63
+ MongooseError.VersionError = require('./version');
64
+ MongooseError.OverwriteModelError = require('./overwriteModel');
65
+ MongooseError.MissingSchemaError = require('./missingSchema');
66
+ MongooseError.DivergentArrayError = require('./divergentArray');
@@ -3,7 +3,7 @@
3
3
  * Module dependencies.
4
4
  */
5
5
 
6
- var MongooseError = require('../error.js');
6
+ var MongooseError = require('./');
7
7
 
8
8
  /*!
9
9
  * MissingSchema Error constructor.
@@ -4,7 +4,7 @@
4
4
  * Module dependencies.
5
5
  */
6
6
 
7
- var MongooseError = require('../error.js');
7
+ var MongooseError = require('./');
8
8
  var util = require('util');
9
9
 
10
10
  /*!
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
 
7
7
  /**
8
8
  * Strict mode error constructor
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
 
7
7
  /**
8
8
  * Constructor for errors that happen when a parameter that's expected to be
@@ -3,7 +3,7 @@
3
3
  * Module dependencies.
4
4
  */
5
5
 
6
- var MongooseError = require('../error.js');
6
+ var MongooseError = require('./');
7
7
 
8
8
  /*!
9
9
  * OverwriteModel Error constructor.
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
 
7
7
  /**
8
8
  * Strict mode error constructor
@@ -2,7 +2,7 @@
2
2
  * Module requirements
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
  var utils = require('../utils');
7
7
 
8
8
  /**
@@ -2,7 +2,7 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseError = require('../error.js');
5
+ var MongooseError = require('./');
6
6
 
7
7
  /**
8
8
  * Schema validator error
@@ -4,7 +4,7 @@
4
4
  * Module dependencies.
5
5
  */
6
6
 
7
- var MongooseError = require('../error.js');
7
+ var MongooseError = require('./');
8
8
 
9
9
  /**
10
10
  * Version Error constructor.
package/lib/model.js CHANGED
@@ -23,6 +23,7 @@ var isPathSelectedInclusive = require('./services/projection/isPathSelectedInclu
23
23
  var mpath = require('mpath');
24
24
  var parallel = require('async/parallel');
25
25
  var parallelLimit = require('async/parallelLimit');
26
+ var setDefaultsOnInsert = require('./services/setDefaultsOnInsert');
26
27
  var util = require('util');
27
28
  var utils = require('./utils');
28
29
 
@@ -45,6 +46,11 @@ var VERSION_WHERE = 1,
45
46
  */
46
47
 
47
48
  function Model(doc, fields, skipId) {
49
+ if (fields instanceof Schema) {
50
+ throw new TypeError('2nd argument to `Model` must be a POJO or string, ' +
51
+ '**not** a schema. Make sure you\'re calling `mongoose.model()`, not ' +
52
+ '`mongoose.Model()`.');
53
+ }
48
54
  Document.call(this, doc, fields, skipId, true);
49
55
  }
50
56
 
@@ -929,7 +935,7 @@ Model.init = function init(callback) {
929
935
  *
930
936
  * ####Example:
931
937
  *
932
- * Event.createIndexes(function (err) {
938
+ * Event.ensureIndexes(function (err) {
933
939
  * if (err) return handleError(err);
934
940
  * });
935
941
  *
@@ -952,14 +958,14 @@ Model.init = function init(callback) {
952
958
  * @api public
953
959
  */
954
960
 
955
- Model.createIndexes = Model.ensureIndexes = function createIndexes(options, callback) {
961
+ Model.ensureIndexes = function ensureIndexes(options, callback) {
956
962
  if (typeof options === 'function') {
957
963
  callback = options;
958
964
  options = null;
959
965
  }
960
966
 
961
967
  if (options && options.__noPromise) {
962
- _createIndexes(this, options, callback);
968
+ _ensureIndexes(this, options, callback);
963
969
  return;
964
970
  }
965
971
 
@@ -970,7 +976,7 @@ Model.createIndexes = Model.ensureIndexes = function createIndexes(options, call
970
976
  var _this = this;
971
977
  var Promise = PromiseProvider.get();
972
978
  return new Promise.ES6(function(resolve, reject) {
973
- _createIndexes(_this, options || {}, function(error) {
979
+ _ensureIndexes(_this, options || {}, function(error) {
974
980
  if (error) {
975
981
  callback && callback(error);
976
982
  reject(error);
@@ -981,8 +987,31 @@ Model.createIndexes = Model.ensureIndexes = function createIndexes(options, call
981
987
  });
982
988
  };
983
989
 
984
- function _createIndexes(model, options, callback) {
990
+ /**
991
+ * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#createIndex)
992
+ * function. The `ensureIndex()` function checks to see if an index with that
993
+ * name already exists, and, if not, does not attempt to create the index.
994
+ * `createIndex()` bypasses this check.
995
+ *
996
+ * @param {Object} [options] internal options
997
+ * @param {Function} [cb] optional callback
998
+ * @return {Promise}
999
+ * @api public
1000
+ */
1001
+
1002
+ Model.createIndexes = function createIndexes(options, callback) {
1003
+ if (typeof options === 'function') {
1004
+ callback = options;
1005
+ options = {};
1006
+ }
1007
+ options = options || {};
1008
+ options.createIndex = true;
1009
+ return this.ensureIndexes(options, callback);
1010
+ };
1011
+
1012
+ function _ensureIndexes(model, options, callback) {
985
1013
  var indexes = model.schema.indexes();
1014
+ options = options || {};
986
1015
 
987
1016
  var done = function(err) {
988
1017
  if (err && model.schema.options.emitIndexErrors) {
@@ -1009,7 +1038,7 @@ function _createIndexes(model, options, callback) {
1009
1038
  };
1010
1039
 
1011
1040
  var create = function() {
1012
- if (options && options._automatic) {
1041
+ if (options._automatic) {
1013
1042
  if (model.schema.options.autoIndex === false ||
1014
1043
  (model.schema.options.autoIndex == null && model.db.config.autoIndex === false)) {
1015
1044
  return done();
@@ -1024,7 +1053,8 @@ function _createIndexes(model, options, callback) {
1024
1053
  _handleSafe(options);
1025
1054
 
1026
1055
  indexSingleStart(indexFields, options);
1027
- model.collection.createIndex(indexFields, indexOptions, utils.tick(function(err, name) {
1056
+ var methodName = options.createIndex ? 'createIndex' : 'ensureIndex';
1057
+ model.collection[methodName](indexFields, indexOptions, utils.tick(function(err, name) {
1028
1058
  indexSingleDone(err, indexFields, indexOptions, name);
1029
1059
  if (err) {
1030
1060
  return done(err);
@@ -2239,12 +2269,18 @@ Model.bulkWrite = function(ops, options, callback) {
2239
2269
  });
2240
2270
  };
2241
2271
  } else if (op['updateOne']) {
2272
+ op = op['updateOne'];
2242
2273
  return function(callback) {
2243
2274
  try {
2244
- op['updateOne']['filter'] = cast(_this.schema,
2245
- op['updateOne']['filter']);
2246
- op['updateOne']['update'] = castUpdate(_this.schema,
2247
- op['updateOne']['update'], _this.schema.options.strict);
2275
+ op['filter'] = cast(_this.schema, op['filter']);
2276
+ op['update'] = castUpdate(_this.schema, op['update'],
2277
+ _this.schema.options.strict);
2278
+ if (op.setDefaultsOnInsert) {
2279
+ setDefaultsOnInsert(op['filter'], _this.schema, op['update'], {
2280
+ setDefaultsOnInsert: true,
2281
+ upsert: op.upsert
2282
+ });
2283
+ }
2248
2284
  } catch (error) {
2249
2285
  return callback(error);
2250
2286
  }
@@ -2252,14 +2288,20 @@ Model.bulkWrite = function(ops, options, callback) {
2252
2288
  callback(null);
2253
2289
  };
2254
2290
  } else if (op['updateMany']) {
2291
+ op = op['updateMany'];
2255
2292
  return function(callback) {
2256
2293
  try {
2257
- op['updateMany']['filter'] = cast(_this.schema,
2258
- op['updateMany']['filter']);
2259
- op['updateMany']['update'] = castUpdate(_this.schema, op['updateMany']['update'], {
2294
+ op['filter'] = cast(_this.schema, op['filter']);
2295
+ op['update'] = castUpdate(_this.schema, op['update'], {
2260
2296
  strict: _this.schema.options.strict,
2261
2297
  overwrite: false
2262
2298
  });
2299
+ if (op.setDefaultsOnInsert) {
2300
+ setDefaultsOnInsert(op['filter'], _this.schema, op['update'], {
2301
+ setDefaultsOnInsert: true,
2302
+ upsert: op.upsert
2303
+ });
2304
+ }
2263
2305
  } catch (error) {
2264
2306
  return callback(error);
2265
2307
  }
package/lib/query.js CHANGED
@@ -15,6 +15,7 @@ var mquery = require('mquery');
15
15
  var readPref = require('./drivers').ReadPreference;
16
16
  var selectPopulatedFields = require('./services/query/selectPopulatedFields');
17
17
  var setDefaultsOnInsert = require('./services/setDefaultsOnInsert');
18
+ var slice = require('sliced');
18
19
  var updateValidators = require('./services/updateValidators');
19
20
  var util = require('util');
20
21
  var utils = require('./utils');
@@ -263,6 +264,49 @@ Query.prototype.toConstructor = function toConstructor() {
263
264
  * @api public
264
265
  */
265
266
 
267
+ Query.prototype.slice = function() {
268
+ if (arguments.length === 0) {
269
+ return this;
270
+ }
271
+
272
+ this._validate('slice');
273
+
274
+ var path;
275
+ var val;
276
+
277
+ if (arguments.length === 1) {
278
+ var arg = arguments[0];
279
+ if (typeof arg === 'object' && !Array.isArray(arg)) {
280
+ var keys = Object.keys(arg);
281
+ var numKeys = keys.length;
282
+ for (var i = 0; i < numKeys; ++i) {
283
+ this.slice(keys[i], arg[keys[i]]);
284
+ }
285
+ return this;
286
+ }
287
+ this._ensurePath('slice');
288
+ path = this._path;
289
+ val = arguments[0];
290
+ } else if (arguments.length === 2) {
291
+ if ('number' === typeof arguments[0]) {
292
+ this._ensurePath('slice');
293
+ path = this._path;
294
+ val = slice(arguments);
295
+ } else {
296
+ path = arguments[0];
297
+ val = arguments[1];
298
+ }
299
+ } else if (arguments.length === 3) {
300
+ path = arguments[0];
301
+ val = slice(arguments, 1);
302
+ }
303
+
304
+ var p = {};
305
+ p[path] = { $slice: val };
306
+ return this.select(p);
307
+ };
308
+
309
+
266
310
  /**
267
311
  * Specifies the complementary comparison value for paths specified with `where()`
268
312
  *
@@ -2227,7 +2271,7 @@ Query.prototype._findAndModify = function(type, callback) {
2227
2271
  };
2228
2272
  } else {
2229
2273
  castedDoc = castDoc(this, opts.overwrite);
2230
- castedDoc = setDefaultsOnInsert(this, schema, castedDoc, opts);
2274
+ castedDoc = setDefaultsOnInsert(this._conditions, schema, castedDoc, opts);
2231
2275
  if (!castedDoc) {
2232
2276
  if (opts.upsert) {
2233
2277
  // still need to do the upsert to empty doc
@@ -2939,7 +2983,7 @@ function _update(query, op, conditions, doc, options, callback) {
2939
2983
  }
2940
2984
  }
2941
2985
 
2942
- castedDoc = setDefaultsOnInsert(query, query.schema, castedDoc, options);
2986
+ castedDoc = setDefaultsOnInsert(query._conditions, query.schema, castedDoc, options);
2943
2987
  if (!castedDoc) {
2944
2988
  // Make sure promises know that this is still an update, see gh-2796
2945
2989
  query.op = op;
@@ -11,6 +11,7 @@ var EventEmitter = require('events').EventEmitter;
11
11
  var MongooseDocumentArray = require('../types/documentarray');
12
12
  var SchemaType = require('../schematype');
13
13
  var Subdocument = require('../types/embedded');
14
+ var applyHooks = require('../services/model/applyHooks');
14
15
  var discriminator = require('../services/model/discriminator');
15
16
  var util = require('util');
16
17
  var utils = require('../utils');
@@ -119,6 +120,8 @@ DocumentArray.prototype.discriminator = function(name, schema) {
119
120
 
120
121
  this.casterConstructor.discriminators[name] = EmbeddedDocument;
121
122
 
123
+ applyHooks(EmbeddedDocument, schema);
124
+
122
125
  return this.casterConstructor.discriminators[name];
123
126
  };
124
127
 
@@ -8,6 +8,7 @@ var $exists = require('./operators/exists');
8
8
  var EventEmitter = require('events').EventEmitter;
9
9
  var SchemaType = require('../schematype');
10
10
  var Subdocument = require('../types/subdocument');
11
+ var applyHooks = require('../services/model/applyHooks');
11
12
  var castToNumber = require('./operators/helpers').castToNumber;
12
13
  var discriminator = require('../services/model/discriminator');
13
14
  var geospatial = require('./operators/geospatial');
@@ -253,5 +254,8 @@ Embedded.prototype.discriminator = function(name, schema) {
253
254
  discriminator(this.caster, name, schema);
254
255
 
255
256
  this.caster.discriminators[name] = _createConstructor(schema);
257
+
258
+ applyHooks(this.caster.discriminators[name], schema);
259
+
256
260
  return this.caster.discriminators[name];
257
261
  };
@@ -20,7 +20,7 @@ var SchemaType = require('../schematype'),
20
20
  */
21
21
 
22
22
  function ObjectId(key, options) {
23
- var isKeyHexStr = typeof key === 'string' && /^a-f0-9$/i.test(key);
23
+ var isKeyHexStr = typeof key === 'string' && key.length === 24 && /^a-f0-9$/i.test(key);
24
24
  var suppressWarning = options && options.suppressWarning;
25
25
  if ((isKeyHexStr || typeof key === 'undefined') && !suppressWarning) {
26
26
  console.warn('mongoose: To create a new ObjectId please try ' +
@@ -116,7 +116,7 @@ SchemaString.prototype.enum = function() {
116
116
  };
117
117
 
118
118
  /**
119
- * Adds a lowercase setter.
119
+ * Adds a lowercase [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
120
120
  *
121
121
  * ####Example:
122
122
  *
@@ -125,6 +125,12 @@ SchemaString.prototype.enum = function() {
125
125
  * var m = new M({ email: 'SomeEmail@example.COM' });
126
126
  * console.log(m.email) // someemail@example.com
127
127
  *
128
+ * NOTE: Setters do not run on queries by default. Use the `runSettersOnQuery` option:
129
+ *
130
+ * // Must use `runSettersOnQuery` as shown below, otherwise `email` will
131
+ * // **not** be lowercased.
132
+ * M.updateOne({}, { $set: { email: 'SomeEmail@example.COM' } }, { runSettersOnQuery: true });
133
+ *
128
134
  * @api public
129
135
  * @return {SchemaType} this
130
136
  */
@@ -145,7 +151,7 @@ SchemaString.prototype.lowercase = function(shouldApply) {
145
151
  };
146
152
 
147
153
  /**
148
- * Adds an uppercase setter.
154
+ * Adds an uppercase [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
149
155
  *
150
156
  * ####Example:
151
157
  *
@@ -154,6 +160,12 @@ SchemaString.prototype.lowercase = function(shouldApply) {
154
160
  * var m = new M({ caps: 'an example' });
155
161
  * console.log(m.caps) // AN EXAMPLE
156
162
  *
163
+ * NOTE: Setters do not run on queries by default. Use the `runSettersOnQuery` option:
164
+ *
165
+ * // Must use `runSettersOnQuery` as shown below, otherwise `email` will
166
+ * // **not** be lowercased.
167
+ * M.updateOne({}, { $set: { email: 'SomeEmail@example.COM' } }, { runSettersOnQuery: true });
168
+ *
157
169
  * @api public
158
170
  * @return {SchemaType} this
159
171
  */
@@ -174,7 +186,7 @@ SchemaString.prototype.uppercase = function(shouldApply) {
174
186
  };
175
187
 
176
188
  /**
177
- * Adds a trim setter.
189
+ * Adds a trim [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
178
190
  *
179
191
  * The string value will be trimmed when set.
180
192
  *
@@ -187,6 +199,12 @@ SchemaString.prototype.uppercase = function(shouldApply) {
187
199
  * var m = new M({ name: string })
188
200
  * console.log(m.name.length) // 9
189
201
  *
202
+ * NOTE: Setters do not run on queries by default. Use the `runSettersOnQuery` option:
203
+ *
204
+ * // Must use `runSettersOnQuery` as shown below, otherwise `email` will
205
+ * // **not** be lowercased.
206
+ * M.updateOne({}, { $set: { email: 'SomeEmail@example.COM' } }, { runSettersOnQuery: true });
207
+ *
190
208
  * @api public
191
209
  * @return {SchemaType} this
192
210
  */
package/lib/schema.js CHANGED
@@ -298,11 +298,13 @@ Schema.prototype.tree;
298
298
  Schema.prototype.clone = function() {
299
299
  var s = new Schema(this.paths, this.options);
300
300
  // Clone the call queue
301
+ var cloneOpts = { retainKeyOrder: true };
301
302
  s.callQueue = this.callQueue.map(function(f) { return f; });
302
- s.methods = utils.clone(this.methods);
303
- s.statics = utils.clone(this.statics);
303
+ s.methods = utils.clone(this.methods, cloneOpts);
304
+ s.statics = utils.clone(this.statics, cloneOpts);
305
+ s.query = utils.clone(this.query, cloneOpts);
304
306
  s.plugins = Array.prototype.slice.call(this.plugins);
305
- s._indexes = utils.clone(this._indexes);
307
+ s._indexes = utils.clone(this._indexes, cloneOpts);
306
308
  s.s.hooks = this.s.hooks.clone();
307
309
  return s;
308
310
  };
package/lib/schematype.js CHANGED
@@ -42,6 +42,13 @@ function SchemaType(path, options, instance) {
42
42
  this[i].apply(this, opts);
43
43
  }
44
44
  }
45
+
46
+ Object.defineProperty(this, '$$context', {
47
+ enumerable: false,
48
+ configurable: false,
49
+ writable: true,
50
+ value: null
51
+ });
45
52
  }
46
53
 
47
54
  /**
@@ -237,25 +244,37 @@ SchemaType.prototype.sparse = function(bool) {
237
244
  *
238
245
  * You can set up email lower case normalization easily via a Mongoose setter.
239
246
  *
240
- * function toLower (v) {
247
+ * function toLower(v) {
241
248
  * return v.toLowerCase();
242
249
  * }
243
250
  *
244
251
  * var UserSchema = new Schema({
245
252
  * email: { type: String, set: toLower }
246
- * })
253
+ * });
247
254
  *
248
- * var User = db.model('User', UserSchema)
255
+ * var User = db.model('User', UserSchema);
249
256
  *
250
- * var user = new User({email: 'AVENUE@Q.COM'})
257
+ * var user = new User({email: 'AVENUE@Q.COM'});
251
258
  * console.log(user.email); // 'avenue@q.com'
252
259
  *
253
260
  * // or
254
- * var user = new User
255
- * user.email = 'Avenue@Q.com'
256
- * console.log(user.email) // 'avenue@q.com'
261
+ * var user = new User();
262
+ * user.email = 'Avenue@Q.com';
263
+ * console.log(user.email); // 'avenue@q.com'
257
264
  *
258
- * As you can see above, 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.
265
+ * As you can see above, setters allow you to transform the data before it stored in MongoDB.
266
+ *
267
+ * NOTE: setters by default do **not** run on queries by default.
268
+ *
269
+ * // Will **not** run the `toLower()` setter by default.
270
+ * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } });
271
+ *
272
+ * Use the `runSettersOnQuery` option to opt-in to running setters on `User.update()`:
273
+ *
274
+ * // Turn on `runSettersOnQuery` to run the setters from your schema.
275
+ * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } }, {
276
+ * runSettersOnQuery: true
277
+ * });
259
278
  *
260
279
  * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._
261
280
  *
@@ -41,5 +41,5 @@ function isPathInFields(userProvidedFields, path) {
41
41
  }
42
42
  cur += '.' + pieces[i];
43
43
  }
44
- return false;
44
+ return userProvidedFields[cur] != null;
45
45
  }
@@ -5,7 +5,7 @@ var modifiedPaths = require('./common').modifiedPaths;
5
5
  /**
6
6
  * Applies defaults to update and findOneAndUpdate operations.
7
7
  *
8
- * @param {Query} query
8
+ * @param {Object} filter
9
9
  * @param {Schema} schema
10
10
  * @param {Object} castedDoc
11
11
  * @param {Object} options
@@ -13,7 +13,7 @@ var modifiedPaths = require('./common').modifiedPaths;
13
13
  * @api private
14
14
  */
15
15
 
16
- module.exports = function(query, schema, castedDoc, options) {
16
+ module.exports = function(filter, schema, castedDoc, options) {
17
17
  var keys = Object.keys(castedDoc || {});
18
18
  var updatedKeys = {};
19
19
  var updatedValues = {};
@@ -33,11 +33,11 @@ module.exports = function(query, schema, castedDoc, options) {
33
33
  modifiedPaths(castedDoc, '', modified);
34
34
  }
35
35
 
36
- var paths = Object.keys(query._conditions);
36
+ var paths = Object.keys(filter);
37
37
  var numPaths = paths.length;
38
38
  for (i = 0; i < numPaths; ++i) {
39
39
  var path = paths[i];
40
- var condition = query._conditions[path];
40
+ var condition = filter[path];
41
41
  if (condition && typeof condition === 'object') {
42
42
  var conditionKeys = Object.keys(condition);
43
43
  var numConditionKeys = conditionKeys.length;
@@ -29,19 +29,22 @@ module.exports = function(query, schema, castedDoc, options) {
29
29
  var numKeys = keys.length;
30
30
  var hasDollarUpdate = false;
31
31
  var modified = {};
32
+ var currentUpdate;
33
+ var key;
32
34
 
33
35
  for (var i = 0; i < numKeys; ++i) {
34
36
  if (keys[i].charAt(0) === '$') {
35
- if (keys[i] === '$push' || keys[i] === '$addToSet' ||
36
- keys[i] === '$pull' || keys[i] === '$pullAll') {
37
+ hasDollarUpdate = true;
38
+ if (keys[i] === '$push' || keys[i] === '$addToSet') {
37
39
  _keys = Object.keys(castedDoc[keys[i]]);
38
40
  for (var ii = 0; ii < _keys.length; ++ii) {
39
- if (castedDoc[keys[i]][_keys[ii]].$each) {
41
+ currentUpdate = castedDoc[keys[i]][_keys[ii]];
42
+ if (currentUpdate && currentUpdate.$each) {
40
43
  arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
41
- concat(castedDoc[keys[i]][_keys[ii]].$each);
44
+ concat(currentUpdate.$each);
42
45
  } else {
43
46
  arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
44
- concat([castedDoc[keys[i]][_keys[ii]]]);
47
+ concat([currentUpdate]);
45
48
  }
46
49
  }
47
50
  continue;
@@ -53,14 +56,15 @@ module.exports = function(query, schema, castedDoc, options) {
53
56
  for (var j = 0; j < numPaths; ++j) {
54
57
  var updatedPath = paths[j].replace('.$.', '.0.');
55
58
  updatedPath = updatedPath.replace(/\.\$$/, '.0');
56
- if (keys[i] === '$set' || keys[i] === '$setOnInsert') {
59
+ key = keys[i];
60
+ if (key === '$set' || key === '$setOnInsert' ||
61
+ key === '$pull' || key === '$pullAll') {
57
62
  updatedValues[updatedPath] = flat[paths[j]];
58
- } else if (keys[i] === '$unset') {
63
+ } else if (key === '$unset') {
59
64
  updatedValues[updatedPath] = undefined;
60
65
  }
61
66
  updatedKeys[updatedPath] = true;
62
67
  }
63
- hasDollarUpdate = true;
64
68
  }
65
69
  }
66
70
 
@@ -46,9 +46,18 @@ function MongooseBuffer(value, encode, offset) {
46
46
 
47
47
  // make sure these internal props don't show up in Object.keys()
48
48
  Object.defineProperties(buf, {
49
- validators: {value: []},
50
- _path: {value: path},
51
- _parent: {value: doc}
49
+ validators: {
50
+ value: [],
51
+ enumerable: false
52
+ },
53
+ _path: {
54
+ value: path,
55
+ enumerable: false
56
+ },
57
+ _parent: {
58
+ value: doc,
59
+ enumerable: false
60
+ }
52
61
  });
53
62
 
54
63
  if (doc && typeof path === 'string') {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "4.12.2",
4
+ "version": "4.12.6",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "async": "2.1.4",
23
23
  "bson": "~1.0.4",
24
- "hooks-fixed": "2.0.0",
24
+ "hooks-fixed": "2.0.2",
25
25
  "kareem": "1.5.0",
26
26
  "mongodb": "2.2.33",
27
27
  "mpath": "0.3.0",
@@ -52,7 +52,7 @@
52
52
  "node-static": "0.7.7",
53
53
  "nsp": "~2.8.1",
54
54
  "power-assert": "1.4.1",
55
- "q": "1.4.1",
55
+ "q": "1.5.1",
56
56
  "tbd": "0.6.4",
57
57
  "uglify-js": "2.7.0",
58
58
  "uuid": "2.0.3",