mongoose 4.11.12 → 4.12.1

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
@@ -17,3 +17,4 @@ before_script:
17
17
  script:
18
18
  - npm test
19
19
  - npm run lint
20
+ - npm run nsp
package/History.md CHANGED
@@ -1,3 +1,48 @@
1
+ 4.12.1 / 2017-10-08
2
+ ===================
3
+ * fix(document): create new doc when setting single nested, no more set() on copy of priorVal #5693
4
+ * fix(model): recursively call applyMethods on child schemas for global plugins #5690
5
+ * docs: fix bad promise lib example on home page #5686
6
+ * fix(query): handle false when checking for inclusive/exclusive projection #5685
7
+ * fix(discriminator): allow reusing child schema #5684
8
+ * fix: make addToSet() on empty array with subdoc trigger manual population #5504
9
+
10
+ 4.12.0 / 2017-10-02
11
+ ===================
12
+ * docs(validation): add docs coverage for ValidatorError.reason #5681
13
+ * feat(discriminator): always add discriminatorKey to base schema to allow updating #5613
14
+ * fix(document): make nested docs no longer inherit parent doc's schema props #5586 #5546 #5470
15
+ * feat(query): run update validators on $pull and $pullAll #5555
16
+ * feat(query): add .error() helper to query to error out in pre hooks #5520
17
+ * feat(connection): add dropCollection() helper #5393
18
+ * feat(schema): add schema-level collation option #5295
19
+ * feat(types): add `discriminator()` function for single nested subdocs #5244
20
+ * feat(document): add $isDeleted() getter/setter for better support for soft deletes #4428
21
+ * feat(connection): bubble up reconnectFailed event when driver gives up reconnecting #4027
22
+ * fix(query): report error if passing array or other non-object as filter to update query #3677
23
+ * fix(collection): use createIndex() instead of deprecated ensureIndex() #3280
24
+
25
+ 4.11.14 / 2017-09-30
26
+ ====================
27
+ * chore: add nsp check to the CI build #5679 [hairyhenderson](https://github.com/hairyhenderson)
28
+ * fix: bump mquery because of security issue with debug package #5677 #5675 [jonathanprl](https://github.com/jonathanprl)
29
+ * fix(populate): automatically select() populated()-ed fields #5669
30
+ * fix(connection): make force close work as expected #5664
31
+ * fix(document): treat $elemMatch as inclusive projection #5661
32
+ * docs(model/query): clarify which functions fire which middleware #5654
33
+ * fix(model): make `init()` public and return a promise that resolves when indexes are done building #5563
34
+
35
+ 4.11.13 / 2017-09-24
36
+ ====================
37
+ * fix(query): correctly run replaceOne with update validators #5665 [sime1](https://github.com/sime1)
38
+ * fix(schema): replace mistype in setupTimestamp method #5656 [zipp3r](https://github.com/zipp3r)
39
+ * fix(query): avoid throwing cast error for strict: throw with nested id in query #5640
40
+ * fix(model): ensure class gets combined schema when using class syntax with discriminators #5635
41
+ * fix(document): handle setting doc array to array of top-level docs #5632
42
+ * fix(model): handle casting findOneAndUpdate() with overwrite and upsert #5631
43
+ * fix(update): correctly handle $ in updates #5628
44
+ * fix(types): handle manual population consistently for unshift() and splice() #5504
45
+
1
46
  4.11.12 / 2017-09-18
2
47
  ====================
3
48
  * docs(model): asterisk should not render as markdown bullet #5644 [timkinnane](https://github.com/timkinnane)
package/lib/aggregate.js CHANGED
@@ -63,10 +63,15 @@ function Aggregate() {
63
63
 
64
64
  Aggregate.prototype.model = function(model) {
65
65
  this._model = model;
66
- if (this.options.readPreference == null &&
67
- model.schema &&
68
- model.schema.options.read != null) {
69
- this.options.readPreference = model.schema.options.read;
66
+ if (model.schema != null) {
67
+ if (this.options.readPreference == null &&
68
+ model.schema.options.read != null) {
69
+ this.options.readPreference = model.schema.options.read;
70
+ }
71
+ if (this.options.collation == null &&
72
+ model.schema.options.collation != null) {
73
+ this.options.collation = model.schema.options.collation;
74
+ }
70
75
  }
71
76
  return this;
72
77
  };
package/lib/cast.js CHANGED
@@ -192,7 +192,7 @@ module.exports = function cast(schema, obj, options, context) {
192
192
  }
193
193
  }
194
194
 
195
- if (options && options.upsert && options.strict) {
195
+ if (options && options.upsert && options.strict && !schema.nested[path]) {
196
196
  if (options.strict === 'throw') {
197
197
  throw new StrictModeError(path);
198
198
  }
package/lib/collection.js CHANGED
@@ -89,8 +89,8 @@ Collection.prototype.onOpen = function() {
89
89
  * @api private
90
90
  */
91
91
 
92
- Collection.prototype.onClose = function() {
93
- if (this.opts.bufferCommands) {
92
+ Collection.prototype.onClose = function(force) {
93
+ if (this.opts.bufferCommands && !force) {
94
94
  this.buffer = true;
95
95
  }
96
96
  };
@@ -139,6 +139,14 @@ Collection.prototype.ensureIndex = function() {
139
139
  throw new Error('Collection#ensureIndex unimplemented by driver');
140
140
  };
141
141
 
142
+ /**
143
+ * Abstract method that drivers must implement.
144
+ */
145
+
146
+ Collection.prototype.createIndex = function() {
147
+ throw new Error('Collection#ensureIndex unimplemented by driver');
148
+ };
149
+
142
150
  /**
143
151
  * Abstract method that drivers must implement.
144
152
  */
package/lib/connection.js CHANGED
@@ -369,42 +369,70 @@ Connection.prototype._openWithoutPromise = function() {
369
369
  };
370
370
 
371
371
  /**
372
- * Helper for `dropDatabase()`.
372
+ * Helper for `dropCollection()`. Will delete the given collection, including
373
+ * all documents and indexes.
373
374
  *
374
- * @param {Function} callback
375
+ * @param {string} collection The collection to delete
376
+ * @param {Function} [callback]
375
377
  * @return {Promise}
376
378
  * @api public
377
379
  */
378
380
 
379
- Connection.prototype.dropDatabase = function(callback) {
380
- var Promise = PromiseProvider.get();
381
- var _this = this;
382
- var promise = new Promise.ES6(function(resolve, reject) {
383
- if (_this.readyState !== STATES.connected) {
384
- _this.on('open', function() {
385
- _this.db.dropDatabase(function(error) {
381
+ Connection.prototype.dropCollection = _wrapConnHelper(function dropCollection(collection, cb) {
382
+ this.db.dropCollection(collection, cb);
383
+ });
384
+
385
+ /**
386
+ * Helper for `dropDatabase()`. Deletes the given database, including all
387
+ * collections, documents, and indexes.
388
+ *
389
+ * @param {Function} [callback]
390
+ * @return {Promise}
391
+ * @api public
392
+ */
393
+
394
+ Connection.prototype.dropDatabase = _wrapConnHelper(function dropDatabase(cb) {
395
+ this.db.dropDatabase(cb);
396
+ });
397
+
398
+ /*!
399
+ * ignore
400
+ */
401
+
402
+ function _wrapConnHelper(fn) {
403
+ return function() {
404
+ var _this = this;
405
+ var Promise = PromiseProvider.get();
406
+ var argsWithoutCb = Array.prototype.slice.call(arguments, 0, fn.length - 1);
407
+ var cb = arguments[arguments.length - 1];
408
+ var promise = new Promise.ES6(function(resolve, reject) {
409
+ if (_this.readyState !== STATES.connected) {
410
+ _this.on('open', function() {
411
+ fn.apply(_this, argsWithoutCb.concat([function(error) {
412
+ if (error) {
413
+ reject(error);
414
+ } else {
415
+ resolve();
416
+ }
417
+ }]));
418
+ });
419
+ } else {
420
+ fn.apply(_this, argsWithoutCb.concat([function(error) {
386
421
  if (error) {
387
422
  reject(error);
388
423
  } else {
389
424
  resolve();
390
425
  }
391
- });
392
- });
393
- } else {
394
- _this.db.dropDatabase(function(error) {
395
- if (error) {
396
- reject(error);
397
- } else {
398
- resolve();
399
- }
400
- });
426
+ }]));
427
+ }
428
+ });
429
+ if (cb) {
430
+ promise.
431
+ then(function() { cb(); }, function(error) { cb(error); });
401
432
  }
402
- });
403
- if (callback) {
404
- promise.then(function() { callback(); }, callback);
405
- }
406
- return promise;
407
- };
433
+ return promise;
434
+ };
435
+ }
408
436
 
409
437
  /*!
410
438
  * ignore
@@ -769,6 +797,9 @@ Connection.prototype.openUri = function(uri, options, callback) {
769
797
  _this.readyState = STATES.connected;
770
798
  _this.emit('reconnected');
771
799
  });
800
+ db.s.topology.on('reconnectFailed', function() {
801
+ _this.emit('reconnectFailed');
802
+ });
772
803
  db.s.topology.on('close', function() {
773
804
  // Implicitly emits 'disconnected'
774
805
  _this.readyState = STATES.disconnected;
@@ -807,16 +838,25 @@ Connection.prototype.openUri = function(uri, options, callback) {
807
838
  /**
808
839
  * Closes the connection
809
840
  *
841
+ * @param {Boolean} [force] optional
810
842
  * @param {Function} [callback] optional
811
843
  * @return {Connection} self
812
844
  * @api public
813
845
  */
814
846
 
815
- Connection.prototype.close = function(callback) {
847
+ Connection.prototype.close = function(force, callback) {
816
848
  var _this = this;
817
849
  var Promise = PromiseProvider.get();
850
+
851
+ if (typeof force === 'function') {
852
+ callback = force;
853
+ force = false;
854
+ }
855
+
856
+ this.$wasForceClosed = !!force;
857
+
818
858
  return new Promise.ES6(function(resolve, reject) {
819
- _this._close(function(error) {
859
+ _this._close(force, function(error) {
820
860
  callback && callback(error);
821
861
  if (error) {
822
862
  reject(error);
@@ -830,10 +870,11 @@ Connection.prototype.close = function(callback) {
830
870
  /**
831
871
  * Handles closing the connection
832
872
  *
873
+ * @param {Boolean} force
833
874
  * @param {Function} callback
834
875
  * @api private
835
876
  */
836
- Connection.prototype._close = function(callback) {
877
+ Connection.prototype._close = function(force, callback) {
837
878
  var _this = this;
838
879
  this._closeCalled = true;
839
880
 
@@ -845,11 +886,11 @@ Connection.prototype._close = function(callback) {
845
886
  case 1: // connected
846
887
  case 4: // unauthorized
847
888
  this.readyState = STATES.disconnecting;
848
- this.doClose(function(err) {
889
+ this.doClose(force, function(err) {
849
890
  if (err) {
850
891
  _this.error(err, callback);
851
892
  } else {
852
- _this.onClose();
893
+ _this.onClose(force);
853
894
  callback && callback();
854
895
  }
855
896
  });
@@ -880,18 +921,18 @@ Connection.prototype._close = function(callback) {
880
921
  * @api private
881
922
  */
882
923
 
883
- Connection.prototype.onClose = function() {
924
+ Connection.prototype.onClose = function(force) {
884
925
  this.readyState = STATES.disconnected;
885
926
 
886
927
  // avoid having the collection subscribe to our event emitter
887
928
  // to prevent 0.3 warning
888
929
  for (var i in this.collections) {
889
930
  if (utils.object.hasOwnProperty(this.collections, i)) {
890
- this.collections[i].onClose();
931
+ this.collections[i].onClose(force);
891
932
  }
892
933
  }
893
934
 
894
- this.emit('close');
935
+ this.emit('close', force);
895
936
  };
896
937
 
897
938
  /**
@@ -906,6 +947,8 @@ Connection.prototype.onClose = function() {
906
947
  */
907
948
 
908
949
  Connection.prototype.collection = function(name, options) {
950
+ options = options ? utils.clone(options, { retainKeyOrder: true }) : {};
951
+ options.$wasForceClosed = this.$wasForceClosed;
909
952
  if (!(name in this.collections)) {
910
953
  this.collections[name] = new Collection(name, this, options);
911
954
  }
package/lib/document.js CHANGED
@@ -41,11 +41,12 @@ var idGetter = require('./plugins/idGetter');
41
41
  * @api private
42
42
  */
43
43
 
44
- function Document(obj, fields, skipId) {
44
+ function Document(obj, fields, skipId, options) {
45
45
  this.$__ = new InternalCache;
46
46
  this.$__.emitter = new EventEmitter();
47
47
  this.isNew = true;
48
48
  this.errors = undefined;
49
+ this.$__.$options = options || {};
49
50
 
50
51
  var schema = this.schema;
51
52
 
@@ -181,8 +182,12 @@ Document.prototype.$__buildDoc = function(obj, fields, skipId) {
181
182
  exclude = !!fields[keys[ki]];
182
183
  } else {
183
184
  while (ki--) {
184
- if (keys[ki] !== '_id' &&
185
- (!fields[keys[ki]] || typeof fields[keys[ki]] !== 'object')) {
185
+ // Does this projection explicitly define inclusion/exclusion?
186
+ // Explicitly avoid `$meta` and `$slice`
187
+ var isDefiningProjection = !fields[keys[ki]] ||
188
+ typeof fields[keys[ki]] !== 'object' ||
189
+ fields[keys[ki]].$elemMatch != null;
190
+ if (keys[ki] !== '_id' && isDefiningProjection) {
186
191
  exclude = !fields[keys[ki]];
187
192
  break;
188
193
  }
@@ -495,7 +500,6 @@ Document.prototype.update = function update() {
495
500
  */
496
501
 
497
502
  Document.prototype.set = function(path, val, type, options) {
498
- //console.log('set', path, val, this, new Error().stack)
499
503
  if (type && utils.getFunctionName(type.constructor) === 'Object') {
500
504
  options = type;
501
505
  type = undefined;
@@ -754,7 +758,10 @@ Document.prototype.set = function(path, val, type, options) {
754
758
  didPopulate = true;
755
759
  }
756
760
 
757
- val = schema.applySetters(val, this, false, priorVal);
761
+ var setterContext = constructing && this.$__.$options.priorDoc ?
762
+ this.$__.$options.priorDoc :
763
+ this;
764
+ val = schema.applySetters(val, setterContext, false, priorVal);
758
765
 
759
766
  if (!didPopulate && this.$__.populated) {
760
767
  delete this.$__.populated[path];
@@ -1123,6 +1130,33 @@ Document.prototype.$isDefault = function(path) {
1123
1130
  return (path in this.$__.activePaths.states.default);
1124
1131
  };
1125
1132
 
1133
+ /**
1134
+ * Getter/setter, determines whether the document was removed or not.
1135
+ *
1136
+ * ####Example:
1137
+ * product.remove(function (err, product) {
1138
+ * product.isDeleted(); // true
1139
+ * product.remove(); // no-op, doesn't send anything to the db
1140
+ *
1141
+ * product.isDeleted(false);
1142
+ * product.isDeleted(); // false
1143
+ * product.remove(); // will execute a remove against the db
1144
+ * })
1145
+ *
1146
+ * @param {Boolean} [val] optional, overrides whether mongoose thinks the doc is deleted
1147
+ * @return {Boolean} whether mongoose thinks this doc is deleted.
1148
+ * @api public
1149
+ */
1150
+
1151
+ Document.prototype.$isDeleted = function(val) {
1152
+ if (arguments.length === 0) {
1153
+ return !!this.$__.isDeleted;
1154
+ }
1155
+
1156
+ this.$__.isDeleted = !!val;
1157
+ return this;
1158
+ };
1159
+
1126
1160
  /**
1127
1161
  * Returns true if `path` was directly set and modified, else false.
1128
1162
  *
@@ -2,9 +2,10 @@
2
2
  * Module dependencies.
3
3
  */
4
4
 
5
- var MongooseCollection = require('../../collection'),
6
- Collection = require('mongodb').Collection,
7
- utils = require('../../utils');
5
+ var MongooseCollection = require('../../collection');
6
+ var Collection = require('mongodb').Collection;
7
+ var util = require('util');
8
+ var utils = require('../../utils');
8
9
 
9
10
  /**
10
11
  * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
@@ -93,8 +94,8 @@ NativeCollection.prototype.onOpen = function() {
93
94
  * @api private
94
95
  */
95
96
 
96
- NativeCollection.prototype.onClose = function() {
97
- MongooseCollection.prototype.onClose.call(this);
97
+ NativeCollection.prototype.onClose = function(force) {
98
+ MongooseCollection.prototype.onClose.call(this, force);
98
99
  };
99
100
 
100
101
  /*!
@@ -103,6 +104,10 @@ NativeCollection.prototype.onClose = function() {
103
104
 
104
105
  function iter(i) {
105
106
  NativeCollection.prototype[i] = function() {
107
+ // If user force closed, queueing will hang forever. See #5664
108
+ if (this.opts.$wasForceClosed) {
109
+ return this.conn.db.collection(this.name)[i].apply(collection, args);
110
+ }
106
111
  if (this.buffer) {
107
112
  this.addQueue(i, arguments);
108
113
  return;
@@ -151,6 +156,13 @@ for (var i in Collection.prototype) {
151
156
  iter(i);
152
157
  }
153
158
 
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
+
154
166
  /**
155
167
  * Debug print helper
156
168
  *
@@ -144,7 +144,7 @@ function listen(conn) {
144
144
  }
145
145
  conn.db._listening = true;
146
146
 
147
- conn.db.on('close', function() {
147
+ conn.db.on('close', function(force) {
148
148
  if (conn._closeCalled) return;
149
149
 
150
150
  // the driver never emits an `open` event. auto_reconnect still
@@ -155,7 +155,7 @@ function listen(conn) {
155
155
  conn.emit('close');
156
156
  return;
157
157
  }
158
- conn.onClose();
158
+ conn.onClose(force);
159
159
  });
160
160
  conn.db.on('error', function(err) {
161
161
  conn.emit('error', err);
@@ -224,13 +224,14 @@ NativeConnection.prototype.doOpenSet = function(fn) {
224
224
  /**
225
225
  * Closes the connection
226
226
  *
227
- * @param {Function} fn
227
+ * @param {Boolean} [force]
228
+ * @param {Function} [fn]
228
229
  * @return {Connection} this
229
230
  * @api private
230
231
  */
231
232
 
232
- NativeConnection.prototype.doClose = function(fn) {
233
- this.db.close(fn);
233
+ NativeConnection.prototype.doClose = function(force, fn) {
234
+ this.db.close(force, fn);
234
235
  return this;
235
236
  };
236
237
 
@@ -0,0 +1,36 @@
1
+ /*!
2
+ * Module dependencies.
3
+ */
4
+
5
+ var MongooseError = require('../error.js');
6
+
7
+ /**
8
+ * Constructor for errors that happen when a parameter that's expected to be
9
+ * an object isn't an object
10
+ *
11
+ * @param {Any} value
12
+ * @param {String} paramName
13
+ * @param {String} fnName
14
+ * @inherits MongooseError
15
+ * @api private
16
+ */
17
+
18
+ function ObjectParameterError(value, paramName, fnName) {
19
+ MongooseError.call(this, 'Parameter "' + paramName + '" to ' + fnName +
20
+ '() must be an object, got ' + value.toString());
21
+ this.name = 'ObjectParameterError';
22
+ if (Error.captureStackTrace) {
23
+ Error.captureStackTrace(this);
24
+ } else {
25
+ this.stack = new Error().stack;
26
+ }
27
+ }
28
+
29
+ /*!
30
+ * Inherits from MongooseError.
31
+ */
32
+
33
+ ObjectParameterError.prototype = Object.create(MongooseError.prototype);
34
+ ObjectParameterError.prototype.constructor = MongooseError;
35
+
36
+ module.exports = ObjectParameterError;