mongoose 5.0.2 → 5.0.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/CONTRIBUTING.md CHANGED
@@ -60,3 +60,32 @@ Visit `http://localhost:8088` and you should see the docs with your local change
60
60
  ### Plugins website
61
61
 
62
62
  The [plugins](http://plugins.mongoosejs.io/) site is also an [open source project](https://github.com/vkarpov15/mongooseplugins) that you can get involved with. Feel free to fork and improve it as well!
63
+
64
+
65
+ ## Financial contributions
66
+
67
+ We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/mongoose).
68
+ Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
69
+
70
+
71
+ ## Credits
72
+
73
+
74
+ ### Contributors
75
+
76
+ Thank you to all the people who have already contributed to mongoose!
77
+ <a href="graphs/contributors"><img src="https://opencollective.com/mongoose/contributors.svg?width=890" /></a>
78
+
79
+
80
+ ### Backers
81
+
82
+ Thank you to all our backers! [[Become a backer](https://opencollective.com/mongoose#backer)]
83
+
84
+ <a href="https://opencollective.com/mongoose#backers" target="_blank"><img src="https://opencollective.com/mongoose/backers.svg?width=890"></a>
85
+
86
+
87
+ ### Sponsors
88
+
89
+ Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/mongoose#sponsor))
90
+
91
+ <a href="https://mixmax.com" target="_blank"><img src="http://mongoosejs.com/docs/images/mixmax.png"></a>
package/History.md CHANGED
@@ -1,3 +1,49 @@
1
+ 5.0.6 / 2018-02-15
2
+ ==================
3
+ * refactor(query.castUpdate): avoid creating error until necessary #6137
4
+ * docs(api): fix missing api docs #6136 [lineus](https://github.com/lineus)
5
+ * fix(schema): copy virtuals when using `clone()` #6133
6
+ * fix(update): avoid digging into buffers with upsert and replaceOne #6124
7
+ * fix(schema): support `enum` on arrays of strings #6102
8
+ * fix(update): cast `$addToSet: [1, 2]` -> `$addToSet: { $each: [1, 2] }` #6086
9
+
10
+ 5.0.5 / 2018-02-13
11
+ ==================
12
+ * docs: make > show up correctly in API docs #6114
13
+ * fix(query): support `where()` overwriting primitive with object #6097
14
+ * fix(schematype): don't run internal `resetId` setter on queries with _id #6093
15
+ * fix(discriminator): don't copy `discriminators` property from base schema #6064
16
+ * fix(utils): respect `valueOf()` when merging object for update #6059
17
+ * docs(validation): fix typo 'maxLength' #4720
18
+ * fix(document): apply defaults after setting initial value so default functions don't see empty doc #3781
19
+
20
+ 5.0.4 / 2018-02-08
21
+ ==================
22
+ * docs: add lambda guide #6107
23
+ * fix(connection): add `dbName` option to work around `mongodb+srv` not supporting db name in URI #6106
24
+ * fix(schematype): fix regexp typo in ObjectId #6098 [JoshuaWise](https://github.com/JoshuaWise)
25
+ * perf(document): re-use the modifiedPaths list #6092 [tarun1793](https://github.com/tarun1793)
26
+ * fix: use console.info() instead of console.error() for debug output #6088 [yuristsepaniuk](https://github.com/yuristsepaniuk)
27
+ * docs(validation): clean up runValidators and isAsync options docs for 5.x #6083
28
+ * docs(model): use array instead of spread consistently for aggregate() #6070
29
+ * fix(schema): make aliases handle mongoose-lean-virtuals #6069
30
+ * docs(layout): add link to subdocs guide #6056
31
+ * fix(query): make strictQuery: true strip out fields that aren't in the schema #6032
32
+ * docs(guide): add notes for `strictQuery` option #6032
33
+
34
+ 4.13.11 / 2018-02-07
35
+ ====================
36
+ * docs: fix links in 4.x docs #6081
37
+ * chore: add release script that uses --tag for npm publish for 4.x releases #6063
38
+
39
+ 5.0.3 / 2018-01-31
40
+ ==================
41
+ * fix: consistently use process.nextTick() to avoid sinon.useFakeTimers() causing ops to hang #6074
42
+ * docs(aggregate): fix typo #6072 [adursun](https://github.com/adursun)
43
+ * chore: add return type to `mongoose.model()` docs [bryant1410](https://github.com/bryant1410)
44
+ * fix(document): depopulate push()-ed docs when saving #6048
45
+ * fix: upgrade mongodb -> 3.0.2 #6019
46
+
1
47
  5.0.2 / 2018-01-28
2
48
  ==================
3
49
  * fix(schema): do not overwrite default values in schema when nested timestamps are provided #6024 [cdeveas](https://github.com/cdeveas)
package/lib/aggregate.js CHANGED
@@ -20,7 +20,7 @@ var read = Query.prototype.read;
20
20
  * ]);
21
21
  *
22
22
  * Model.
23
- * aggregate({ $match: { age: { $gte: 21 }}}).
23
+ * aggregate([{ $match: { age: { $gte: 21 }}}]).
24
24
  * unwind('tags').
25
25
  * exec(callback);
26
26
  *
@@ -392,7 +392,7 @@ Aggregate.prototype.graphLookup = function(options) {
392
392
  };
393
393
 
394
394
  /**
395
- * Appepnds new custom $sample operator(s) to this aggregate pipeline.
395
+ * Appends new custom $sample operator(s) to this aggregate pipeline.
396
396
  *
397
397
  * ####Examples:
398
398
  *
package/lib/cast.js CHANGED
@@ -33,6 +33,8 @@ module.exports = function cast(schema, obj, options, context) {
33
33
  var type;
34
34
  var val;
35
35
 
36
+ options = options || {};
37
+
36
38
  while (i--) {
37
39
  path = paths[i];
38
40
  val = obj[path];
@@ -192,15 +194,17 @@ module.exports = function cast(schema, obj, options, context) {
192
194
  }
193
195
  }
194
196
 
195
- if (options && options.upsert && options.strict && !schema.nested[path]) {
197
+ if (options.upsert && options.strict && !schema.nested[path]) {
196
198
  if (options.strict === 'throw') {
197
199
  throw new StrictModeError(path);
198
200
  }
199
201
  throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
200
202
  'schema, strict mode is `true`, and upsert is `true`.');
201
- } else if (options && options.strictQuery === 'throw') {
203
+ } else if (options.strictQuery === 'throw') {
202
204
  throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
203
205
  'schema and strictQuery is true.');
206
+ } else if (options.strictQuery) {
207
+ delete obj[path];
204
208
  }
205
209
  } else if (val === null || val === undefined) {
206
210
  obj[path] = null;
package/lib/collection.js CHANGED
@@ -1,9 +1,12 @@
1
+ 'use strict';
2
+
1
3
  /*!
2
4
  * Module dependencies.
3
5
  */
4
6
 
5
- var EventEmitter = require('events').EventEmitter;
6
- var STATES = require('./connectionstate');
7
+ const EventEmitter = require('events').EventEmitter;
8
+ const STATES = require('./connectionstate');
9
+ const utils = require('./utils');
7
10
 
8
11
  /**
9
12
  * Abstract Collection constructor
@@ -81,7 +84,7 @@ Collection.prototype.conn;
81
84
  Collection.prototype.onOpen = function() {
82
85
  this.buffer = false;
83
86
  var _this = this;
84
- setImmediate(function() {
87
+ utils.immediate(function() {
85
88
  _this.doQueue();
86
89
  });
87
90
  };
package/lib/connection.js CHANGED
@@ -286,12 +286,12 @@ Connection.prototype.openUri = function(uri, options, callback) {
286
286
  options = null;
287
287
  }
288
288
 
289
- var Promise = PromiseProvider.get();
290
- var _this = this;
289
+ const Promise = PromiseProvider.get();
290
+ const _this = this;
291
291
 
292
292
  if (options) {
293
293
  options = utils.clone(options);
294
- var autoIndex = options.config && options.config.autoIndex != null ?
294
+ const autoIndex = options.config && options.config.autoIndex != null ?
295
295
  options.config.autoIndex :
296
296
  options.autoIndex;
297
297
  if (autoIndex != null) {
@@ -325,8 +325,10 @@ Connection.prototype.openUri = function(uri, options, callback) {
325
325
  }
326
326
 
327
327
  this._connectionOptions = options;
328
+ let dbName = options.dbName;
329
+ delete options.dbName;
328
330
 
329
- var promise = new Promise((resolve, reject) => {
331
+ const promise = new Promise((resolve, reject) => {
330
332
  parser(uri, options, (error, parsed) => {
331
333
  if (error) {
332
334
  if (_this.listeners('error').length > 0) {
@@ -335,7 +337,8 @@ Connection.prototype.openUri = function(uri, options, callback) {
335
337
  return reject(error);
336
338
  }
337
339
 
338
- this.name = parsed.dbName;
340
+ dbName = dbName || parsed.dbName;
341
+ this.name = dbName;
339
342
  this.host = parsed.servers[0].host || parsed.servers[0].domain_socket;
340
343
  this.port = parsed.servers[0].port || 27017;
341
344
  if (parsed.auth) {
@@ -353,7 +356,7 @@ Connection.prototype.openUri = function(uri, options, callback) {
353
356
  }
354
357
  return reject(error);
355
358
  }
356
- var db = client.db(parsed.dbName);
359
+ const db = client.db(dbName);
357
360
  _this.db = db;
358
361
  _this.client = client;
359
362
 
@@ -394,7 +397,7 @@ Connection.prototype.openUri = function(uri, options, callback) {
394
397
  delete _this.catch;
395
398
  _this.readyState = STATES.connected;
396
399
 
397
- for (var i in _this.collections) {
400
+ for (let i in _this.collections) {
398
401
  if (utils.object.hasOwnProperty(_this.collections, i)) {
399
402
  _this.collections[i].onOpen();
400
403
  }
package/lib/document.js CHANGED
@@ -16,6 +16,7 @@ var VirtualType = require('./virtualtype');
16
16
  var utils = require('./utils');
17
17
  var clone = utils.clone;
18
18
  var isDefiningProjection = require('./services/projection/isDefiningProjection');
19
+ var isExclusive = require('./services/projection/isExclusive');
19
20
  var isMongooseObject = utils.isMongooseObject;
20
21
  var inspect = require('util').inspect;
21
22
  var internalToObjectOptions = require('./options').internalToObjectOptions;
@@ -71,7 +72,20 @@ function Document(obj, fields, skipId, options) {
71
72
  }
72
73
 
73
74
  this.$__.emitter.setMaxListeners(0);
74
- this._doc = this.$__buildDoc(obj, fields, skipId);
75
+
76
+ let exclude = null;
77
+
78
+ // determine if this doc is a result of a query with
79
+ // excluded fields
80
+ if (fields && utils.getFunctionName(fields.constructor) === 'Object') {
81
+ exclude = isExclusive(fields);
82
+ }
83
+
84
+ let hasIncludedChildren = exclude === false && fields ?
85
+ $__hasIncludedChildren(fields) :
86
+ {};
87
+
88
+ this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren);
75
89
 
76
90
  if (obj) {
77
91
  if (obj instanceof Document) {
@@ -85,6 +99,8 @@ function Document(obj, fields, skipId, options) {
85
99
  }
86
100
  }
87
101
 
102
+ $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren);
103
+
88
104
  this.$__._id = this._id;
89
105
 
90
106
  if (!schema.options.strict && obj) {
@@ -163,91 +179,52 @@ Document.prototype.id;
163
179
 
164
180
  Document.prototype.errors;
165
181
 
166
- /**
167
- * Builds the default doc structure
168
- *
169
- * @param {Object} obj
170
- * @param {Object} [fields]
171
- * @param {Boolean} [skipId]
172
- * @return {Object}
173
- * @api private
174
- * @method $__buildDoc
175
- * @memberOf Document
182
+ /*!
183
+ * ignore
176
184
  */
177
185
 
178
- Document.prototype.$__buildDoc = function(obj, fields, skipId) {
179
- var doc = {};
180
- var exclude = null;
181
- var keys;
182
- var ki;
183
- var _this = this;
184
-
185
- // determine if this doc is a result of a query with
186
- // excluded fields
187
-
188
- if (fields && utils.getFunctionName(fields.constructor) === 'Object') {
189
- keys = Object.keys(fields);
190
- ki = keys.length;
191
-
192
- if (ki === 1 && keys[0] === '_id') {
193
- exclude = !!fields[keys[ki]];
194
- } else {
195
- while (ki--) {
196
- // Does this projection explicitly define inclusion/exclusion?
197
- // Explicitly avoid `$meta` and `$slice`
198
- if (keys[ki] !== '_id' && isDefiningProjection(fields[keys[ki]])) {
199
- exclude = !fields[keys[ki]];
200
- break;
201
- }
202
- }
186
+ function $__hasIncludedChildren(fields) {
187
+ let hasIncludedChildren = {};
188
+ let keys = Object.keys(fields);
189
+ for (var j = 0; j < keys.length; ++j) {
190
+ let parts = keys[j].split('.');
191
+ let c = [];
192
+ for (var k = 0; k < parts.length; ++k) {
193
+ c.push(parts[k]);
194
+ hasIncludedChildren[c.join('.')] = 1;
203
195
  }
204
196
  }
205
197
 
206
- var paths = Object.keys(this.schema.paths);
207
- var plen = paths.length;
208
- var ii = 0;
198
+ return hasIncludedChildren;
199
+ }
209
200
 
210
- var hasIncludedChildren = {};
211
- if (exclude === false && fields) {
212
- keys = Object.keys(fields);
213
- for (var j = 0; j < keys.length; ++j) {
214
- var parts = keys[j].split('.');
215
- var c = [];
216
- for (var k = 0; k < parts.length; ++k) {
217
- c.push(parts[k]);
218
- hasIncludedChildren[c.join('.')] = 1;
219
- }
220
- }
221
- }
201
+ /*!
202
+ * ignore
203
+ */
222
204
 
223
- for (; ii < plen; ++ii) {
224
- var p = paths[ii];
205
+ function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren) {
206
+ const paths = Object.keys(doc.schema.paths);
207
+ const plen = paths.length;
225
208
 
226
- if (p === '_id') {
227
- if (skipId) {
228
- continue;
229
- }
230
- if (obj && '_id' in obj) {
231
- continue;
232
- }
233
- }
209
+ for (let i = 0; i < plen; ++i) {
210
+ let def;
211
+ let curPath = '';
212
+ let p = paths[i];
234
213
 
235
- var type = this.schema.paths[p];
236
- var path = p.split('.');
237
- var len = path.length;
238
- var last = len - 1;
239
- var curPath = '';
240
- var doc_ = doc;
241
- var i = 0;
242
- var included = false;
214
+ if (p === '_id' && skipId) {
215
+ continue;
216
+ }
243
217
 
244
- for (; i < len; ++i) {
245
- var piece = path[i],
246
- def;
218
+ let type = doc.schema.paths[p];
219
+ let path = p.split('.');
220
+ let len = path.length;
221
+ let included = false;
222
+ let doc_ = doc._doc;
247
223
 
224
+ for (let j = 0; j < len; ++j) {
225
+ let piece = path[j];
248
226
  curPath += (!curPath.length ? '' : '.') + piece;
249
227
 
250
- // support excluding intermediary levels
251
228
  if (exclude === true) {
252
229
  if (curPath in fields) {
253
230
  break;
@@ -260,7 +237,11 @@ Document.prototype.$__buildDoc = function(obj, fields, skipId) {
260
237
  }
261
238
  }
262
239
 
263
- if (i === last) {
240
+ if (j === len - 1) {
241
+ if (doc_[piece] !== void 0) {
242
+ break;
243
+ }
244
+
264
245
  if (fields && exclude !== null) {
265
246
  if (exclude === true) {
266
247
  // apply defaults to all non-excluded fields
@@ -268,33 +249,95 @@ Document.prototype.$__buildDoc = function(obj, fields, skipId) {
268
249
  continue;
269
250
  }
270
251
 
271
- def = type.getDefault(_this, false);
252
+ def = type.getDefault(doc, false);
272
253
  if (typeof def !== 'undefined') {
273
254
  doc_[piece] = def;
274
- _this.$__.activePaths.default(p);
255
+ doc.$__.activePaths.default(p);
275
256
  }
276
257
  } else if (included) {
277
258
  // selected field
278
- def = type.getDefault(_this, false);
259
+ def = type.getDefault(doc, false);
279
260
  if (typeof def !== 'undefined') {
280
261
  doc_[piece] = def;
281
- _this.$__.activePaths.default(p);
262
+ doc.$__.activePaths.default(p);
282
263
  }
283
264
  }
284
265
  } else {
285
- def = type.getDefault(_this, false);
266
+ def = type.getDefault(doc, false);
286
267
  if (typeof def !== 'undefined') {
287
268
  doc_[piece] = def;
288
- _this.$__.activePaths.default(p);
269
+ doc.$__.activePaths.default(p);
289
270
  }
290
271
  }
291
272
  } else {
273
+ doc_ = doc_[piece];
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Builds the default doc structure
281
+ *
282
+ * @param {Object} obj
283
+ * @param {Object} [fields]
284
+ * @param {Boolean} [skipId]
285
+ * @api private
286
+ * @method $__buildDoc
287
+ * @memberOf Document
288
+ */
289
+
290
+ Document.prototype.$__buildDoc = function(obj, fields, skipId, exclude, hasIncludedChildren) {
291
+ const doc = {};
292
+
293
+ const paths = Object.keys(this.schema.paths);
294
+ const plen = paths.length;
295
+ let ii = 0;
296
+
297
+ for (; ii < plen; ++ii) {
298
+ var p = paths[ii];
299
+
300
+ if (p === '_id') {
301
+ if (skipId) {
302
+ continue;
303
+ }
304
+ if (obj && '_id' in obj) {
305
+ continue;
306
+ }
307
+ }
308
+
309
+ const path = p.split('.');
310
+ const len = path.length;
311
+ const last = len - 1;
312
+ let curPath = '';
313
+ let doc_ = doc;
314
+ let included = false;
315
+
316
+ for (let i = 0; i < len; ++i) {
317
+ const piece = path[i];
318
+
319
+ curPath += (!curPath.length ? '' : '.') + piece;
320
+
321
+ // support excluding intermediary levels
322
+ if (exclude === true) {
323
+ if (curPath in fields) {
324
+ break;
325
+ }
326
+ } else if (exclude === false && fields && !included) {
327
+ if (curPath in fields) {
328
+ included = true;
329
+ } else if (!hasIncludedChildren[curPath]) {
330
+ break;
331
+ }
332
+ }
333
+
334
+ if (i < last) {
292
335
  doc_ = doc_[piece] || (doc_[piece] = {});
293
336
  }
294
337
  }
295
338
  }
296
339
 
297
- return doc;
340
+ this._doc = doc;
298
341
  };
299
342
 
300
343
  /*!
@@ -1103,12 +1146,12 @@ Document.prototype.modifiedPaths = function() {
1103
1146
  * @api public
1104
1147
  */
1105
1148
 
1106
- Document.prototype.isModified = function(paths) {
1149
+ Document.prototype.isModified = function(paths, modifiedPaths) {
1107
1150
  if (paths) {
1108
1151
  if (!Array.isArray(paths)) {
1109
1152
  paths = paths.split(' ');
1110
1153
  }
1111
- var modified = this.modifiedPaths();
1154
+ var modified = modifiedPaths || this.modifiedPaths();
1112
1155
  var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
1113
1156
  var isModifiedChild = paths.some(function(path) {
1114
1157
  return !!~modified.indexOf(path);
@@ -1394,9 +1437,10 @@ function _getPathsToValidate(doc) {
1394
1437
  var subdocs = doc.$__getAllSubdocs();
1395
1438
  var subdoc;
1396
1439
  len = subdocs.length;
1440
+ var modifiedPaths = doc.modifiedPaths();
1397
1441
  for (i = 0; i < len; ++i) {
1398
1442
  subdoc = subdocs[i];
1399
- if (doc.isModified(subdoc.$basePath) &&
1443
+ if (doc.isModified(subdoc.$basePath, modifiedPaths) &&
1400
1444
  !doc.isDirectModified(subdoc.$basePath)) {
1401
1445
  // Remove child paths for now, because we'll be validating the whole
1402
1446
  // subdoc
@@ -182,7 +182,7 @@ NativeCollection.prototype.$print = function(name, i, args) {
182
182
  }
183
183
  var params = '(' + _args.join(', ') + ')';
184
184
 
185
- console.error(moduleName + functionCall + params);
185
+ console.info(moduleName + functionCall + params);
186
186
  };
187
187
 
188
188
  /**
package/lib/index.js CHANGED
@@ -285,6 +285,7 @@ Mongoose.prototype.pluralize = function(fn) {
285
285
  * @param {Schema} [schema]
286
286
  * @param {String} [collection] name (optional, inferred from model name)
287
287
  * @param {Boolean} [skipInit] whether to skip initialization (defaults to false)
288
+ * @return {Model}
288
289
  * @api public
289
290
  */
290
291
 
package/lib/model.js CHANGED
@@ -770,7 +770,7 @@ Model.prototype.remove = function remove(options, fn) {
770
770
 
771
771
  Model.prototype.$__remove = function $__remove(options, cb) {
772
772
  if (this.$__.isDeleted) {
773
- return setImmediate(() => cb(null, this));
773
+ return utils.immediate(() => cb(null, this));
774
774
  }
775
775
 
776
776
  var where = this.$__where();
@@ -1030,7 +1030,7 @@ function _ensureIndexes(model, options, callback) {
1030
1030
  };
1031
1031
 
1032
1032
  if (!indexes.length) {
1033
- setImmediate(function() {
1033
+ utils.immediate(function() {
1034
1034
  done();
1035
1035
  });
1036
1036
  return;
@@ -1071,7 +1071,7 @@ function _ensureIndexes(model, options, callback) {
1071
1071
  }));
1072
1072
  };
1073
1073
 
1074
- setImmediate(function() {
1074
+ utils.immediate(function() {
1075
1075
  // If buffering is off, do this manually.
1076
1076
  if (options._automatic && !model.collection.collection) {
1077
1077
  model.collection.addQueue(create, []);
@@ -2751,13 +2751,13 @@ Model.mapReduce = function mapReduce(o, callback) {
2751
2751
  * ####Example:
2752
2752
  *
2753
2753
  * // Find the max balance of all accounts
2754
- * Users.aggregate(
2754
+ * Users.aggregate([
2755
2755
  * { $group: { _id: null, maxBalance: { $max: '$balance' }}},
2756
- * { $project: { _id: 0, maxBalance: 1 }},
2757
- * function (err, res) {
2758
- * if (err) return handleError(err);
2759
- * console.log(res); // [ { maxBalance: 98000 } ]
2760
- * });
2756
+ * { $project: { _id: 0, maxBalance: 1 }}
2757
+ * ]).
2758
+ * then(function (res) {
2759
+ * console.log(res); // [ { maxBalance: 98000 } ]
2760
+ * });
2761
2761
  *
2762
2762
  * // Or use the aggregation pipeline builder.
2763
2763
  * Users.aggregate()
@@ -2766,7 +2766,7 @@ Model.mapReduce = function mapReduce(o, callback) {
2766
2766
  * .exec(function (err, res) {
2767
2767
  * if (err) return handleError(err);
2768
2768
  * console.log(res); // [ { maxBalance: 98 } ]
2769
- * });
2769
+ * });
2770
2770
  *
2771
2771
  * ####NOTE:
2772
2772
  *
@@ -3036,7 +3036,7 @@ function populate(model, docs, options, callback) {
3036
3036
 
3037
3037
  modelsMap = getModelsMapForPopulate(model, docs, options);
3038
3038
  if (modelsMap instanceof Error) {
3039
- return setImmediate(function() {
3039
+ return utils.immediate(function() {
3040
3040
  callback(modelsMap);
3041
3041
  });
3042
3042
  }
@@ -17,6 +17,7 @@ var Types = {
17
17
  };
18
18
  var Mixed = require('./mixed');
19
19
  var cast = require('../cast');
20
+ var get = require('lodash.get');
20
21
  var util = require('util');
21
22
  var utils = require('../utils');
22
23
  var castToNumber = require('./operators/helpers').castToNumber;
@@ -119,6 +120,23 @@ SchemaArray.schemaName = 'Array';
119
120
  SchemaArray.prototype = Object.create(SchemaType.prototype);
120
121
  SchemaArray.prototype.constructor = SchemaArray;
121
122
 
123
+ /**
124
+ * Adds an enum validator if this is an array of strings. Equivalent to
125
+ * `SchemaString.prototype.enum()`
126
+ *
127
+ * @param {String|Object} [args...] enumeration values
128
+ * @return {SchemaType} this
129
+ */
130
+
131
+ SchemaArray.prototype.enum = function() {
132
+ const instance = get(this, 'caster.instance');
133
+ if (instance !== 'String') {
134
+ throw new Error('`enum` can only be set on an array of strings, not ' + instance);
135
+ }
136
+ this.caster.enum.apply(this.caster, arguments);
137
+ return this;
138
+ };
139
+
122
140
  /**
123
141
  * Check if the given value satisfies a required validator. The given value
124
142
  * must be not null nor undefined, and have a positive length.
@@ -20,7 +20,7 @@ var SchemaType = require('../schematype'),
20
20
  */
21
21
 
22
22
  function ObjectId(key, options) {
23
- var isKeyHexStr = typeof key === 'string' && key.length === 24 && /^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 ' +
@@ -209,15 +209,16 @@ function defaultId() {
209
209
  function resetId(v) {
210
210
  Document || (Document = require('./../document'));
211
211
 
212
- if (v === void 0) {
213
- var _v = new oid;
214
- this.$__._id = _v;
215
- return _v;
216
- }
217
-
218
212
  if (this instanceof Document) {
213
+ if (v === void 0) {
214
+ var _v = new oid;
215
+ this.$__._id = _v;
216
+ return _v;
217
+ }
218
+
219
219
  this.$__._id = v;
220
220
  }
221
+
221
222
  return v;
222
223
  }
223
224