mongoose 5.0.17 → 5.1.2

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.
@@ -13,18 +13,6 @@ module.exports = function(schema) {
13
13
  }
14
14
 
15
15
  schema.virtual('id').get(idGetter);
16
-
17
- if ('toHexString' in schema.methods) {
18
- return;
19
- }
20
-
21
- // Re: gh-6115, make it easy to get something usable as an ObjectID regardless
22
- // of whether a property is populated or not. With this, you can do
23
- // `blogPost.author.toHexString()` and get a hex string regardless of whether
24
- // `author` is populated
25
- schema.methods.toHexString = function() {
26
- return this.id;
27
- };
28
16
  };
29
17
 
30
18
  /*!
@@ -23,7 +23,7 @@ module.exports = function(schema) {
23
23
  }
24
24
 
25
25
  each(subdocs, function(subdoc, cb) {
26
- subdoc.save(function(err) {
26
+ subdoc.save({ suppressWarning: true }, function(err) {
27
27
  cb(err);
28
28
  });
29
29
  }, function(error) {
package/lib/query.js CHANGED
@@ -10,6 +10,7 @@ const QueryCursor = require('./cursor/QueryCursor');
10
10
  const ReadPreference = require('./drivers').ReadPreference;
11
11
  const cast = require('./cast');
12
12
  const castUpdate = require('./services/query/castUpdate');
13
+ const completeMany = require('./services/query/completeMany');
13
14
  const get = require('lodash.get');
14
15
  const hasDollarKeys = require('./services/query/hasDollarKeys');
15
16
  const helpers = require('./queryhelpers');
@@ -166,7 +167,7 @@ Query.prototype.toConstructor = function toConstructor() {
166
167
  Query.call(this, criteria, options || null, model, coll);
167
168
  };
168
169
 
169
- util.inherits(CustomQuery, Query);
170
+ util.inherits(CustomQuery, model.Query);
170
171
 
171
172
  // set inherited defaults
172
173
  var p = CustomQuery.prototype;
@@ -996,6 +997,7 @@ Query.prototype.read = function read(pref, tags) {
996
997
  *
997
998
  * The following options are for all operations:
998
999
  * - [collation](https://docs.mongodb.com/manual/reference/collation/)
1000
+ * - [session](https://docs.mongodb.com/manual/reference/server-sessions/)
999
1001
  *
1000
1002
  * @param {Object} options
1001
1003
  * @api public
@@ -1082,6 +1084,11 @@ Query.prototype.getUpdate = function() {
1082
1084
  * @receiver Query
1083
1085
  */
1084
1086
 
1087
+ Query.prototype._fieldsForExec = function() {
1088
+ return utils.clone(this._fields);
1089
+ };
1090
+
1091
+
1085
1092
  /**
1086
1093
  * Return an update document with corrected $set operations.
1087
1094
  *
@@ -1302,16 +1309,22 @@ Query.prototype._find = function(callback) {
1302
1309
 
1303
1310
  if (this.error() != null) {
1304
1311
  callback(this.error());
1305
- return this;
1312
+ return null;
1306
1313
  }
1307
1314
 
1308
1315
  this._applyPaths();
1309
1316
  this._fields = this._castFields(this._fields);
1310
1317
 
1311
- var fields = this._fieldsForExec();
1312
- var mongooseOptions = this._mongooseOptions;
1313
- var _this = this;
1314
- var userProvidedFields = _this._userProvidedFields || {};
1318
+ const fields = this._fieldsForExec();
1319
+ const mongooseOptions = this._mongooseOptions;
1320
+ const _this = this;
1321
+ const userProvidedFields = _this._userProvidedFields || {};
1322
+
1323
+ // Separate options to pass down to `completeMany()` in case we need to
1324
+ // set a session on the document
1325
+ const completeManyOptions = Object.assign({}, {
1326
+ session: get(this, 'options.session', null)
1327
+ });
1315
1328
 
1316
1329
  var cb = function(err, docs) {
1317
1330
  if (err) {
@@ -1325,23 +1338,25 @@ Query.prototype._find = function(callback) {
1325
1338
  if (!mongooseOptions.populate) {
1326
1339
  return !!mongooseOptions.lean === true
1327
1340
  ? callback(null, docs)
1328
- : completeMany(_this.model, docs, fields, userProvidedFields, null, callback);
1341
+ : completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
1329
1342
  }
1330
1343
 
1331
- var pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
1344
+ const pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
1332
1345
  pop.__noPromise = true;
1346
+ completeManyOptions.populated = pop;
1333
1347
  _this.model.populate(docs, pop, function(err, docs) {
1334
1348
  if (err) return callback(err);
1335
1349
  return !!mongooseOptions.lean === true
1336
1350
  ? callback(null, docs)
1337
- : completeMany(_this.model, docs, fields, userProvidedFields, pop, callback);
1351
+ : completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
1338
1352
  });
1339
1353
  };
1340
1354
 
1341
1355
  var options = this._optionsForExec();
1342
1356
  options.fields = this._fieldsForExec();
1343
1357
  var filter = this._conditions;
1344
- return this._collection.find(filter, options, cb);
1358
+ this._collection.find(filter, options, cb);
1359
+ return null;
1345
1360
  };
1346
1361
 
1347
1362
  /**
@@ -1436,43 +1451,6 @@ Query.prototype.merge = function(source) {
1436
1451
  return this;
1437
1452
  };
1438
1453
 
1439
- /*!
1440
- * hydrates many documents
1441
- *
1442
- * @param {Model} model
1443
- * @param {Array} docs
1444
- * @param {Object} fields
1445
- * @param {Query} self
1446
- * @param {Array} [pop] array of paths used in population
1447
- * @param {Function} callback
1448
- */
1449
-
1450
- function completeMany(model, docs, fields, userProvidedFields, pop, callback) {
1451
- var arr = [];
1452
- var count = docs.length;
1453
- var len = count;
1454
- var opts = pop ? { populated: pop } : undefined;
1455
- var error = null;
1456
- function init(_error) {
1457
- if (_error != null) {
1458
- error = error || _error;
1459
- }
1460
- if (error != null) {
1461
- --count || process.nextTick(() => callback(error));
1462
- return;
1463
- }
1464
- --count || process.nextTick(() => callback(error, arr));
1465
- }
1466
- for (var i = 0; i < len; ++i) {
1467
- arr[i] = helpers.createModel(model, docs[i], fields, userProvidedFields);
1468
- try {
1469
- arr[i].init(docs[i], opts, init);
1470
- } catch (error) {
1471
- init(error);
1472
- }
1473
- }
1474
- }
1475
-
1476
1454
  /**
1477
1455
  * Adds a collation to this op (MongoDB 3.4 and up)
1478
1456
  *
@@ -1490,6 +1468,44 @@ Query.prototype.collation = function(value) {
1490
1468
  return this;
1491
1469
  };
1492
1470
 
1471
+ /**
1472
+ * Hydrate a single doc from `findOne()`, `findOneAndUpdate()`, etc.
1473
+ *
1474
+ * @api private
1475
+ */
1476
+
1477
+ Query.prototype._completeOne = function(doc, res, callback) {
1478
+ if (!doc) {
1479
+ return callback(null, null);
1480
+ }
1481
+
1482
+ const model = this.model;
1483
+ const projection = utils.clone(this._fields);
1484
+ const userProvidedFields = this._userProvidedFields || {};
1485
+ // `populate`, `lean`
1486
+ const mongooseOptions = this._mongooseOptions;
1487
+ // `rawResult`
1488
+ const options = this.options;
1489
+
1490
+ if (!mongooseOptions.populate) {
1491
+ return mongooseOptions.lean ?
1492
+ _completeOneLean(doc, res, options, callback) :
1493
+ completeOne(model, doc, res, options, projection, userProvidedFields,
1494
+ null, callback);
1495
+ }
1496
+
1497
+ const pop = helpers.preparePopulationOptionsMQ(this, this._mongooseOptions);
1498
+ model.populate(doc, pop, (err, doc) => {
1499
+ if (err) {
1500
+ return callback(err);
1501
+ }
1502
+ return mongooseOptions.lean ?
1503
+ _completeOneLean(doc, res, options, callback) :
1504
+ completeOne(model, doc, res, options, projection, userProvidedFields,
1505
+ pop, callback);
1506
+ });
1507
+ };
1508
+
1493
1509
  /**
1494
1510
  * Thunk around findOne()
1495
1511
  *
@@ -1502,42 +1518,21 @@ Query.prototype._findOne = function(callback) {
1502
1518
  this._castConditions();
1503
1519
 
1504
1520
  if (this.error()) {
1505
- return callback(this.error());
1521
+ callback(this.error());
1522
+ return null;
1506
1523
  }
1507
1524
 
1508
1525
  this._applyPaths();
1509
1526
  this._fields = this._castFields(this._fields);
1510
1527
 
1511
- var options = this._mongooseOptions;
1512
- var projection = this._fieldsForExec();
1513
- var userProvidedFields = this._userProvidedFields || {};
1514
- var _this = this;
1515
-
1516
1528
  // don't pass in the conditions because we already merged them in
1517
- Query.base.findOne.call(_this, {}, function(err, doc) {
1529
+ Query.base.findOne.call(this, {}, (err, doc) => {
1518
1530
  if (err) {
1519
- return callback(err);
1520
- }
1521
- if (!doc) {
1522
- return callback(null, null);
1523
- }
1524
-
1525
- if (!options.populate) {
1526
- return !!options.lean === true
1527
- ? callback(null, doc)
1528
- : completeOne(_this.model, doc, null, {}, projection, userProvidedFields, null, callback);
1531
+ callback(err);
1532
+ return null;
1529
1533
  }
1530
1534
 
1531
- var pop = helpers.preparePopulationOptionsMQ(_this, options);
1532
- pop.__noPromise = true;
1533
- _this.model.populate(doc, pop, function(err, doc) {
1534
- if (err) {
1535
- return callback(err);
1536
- }
1537
- return !!options.lean === true
1538
- ? callback(null, doc)
1539
- : completeOne(_this.model, doc, null, {}, projection, userProvidedFields, pop, callback);
1540
- });
1535
+ this._completeOne(doc, null, callback);
1541
1536
  });
1542
1537
  };
1543
1538
 
@@ -2015,6 +2010,8 @@ function completeOne(model, doc, res, options, fields, userProvidedFields, pop,
2015
2010
  return process.nextTick(() => callback(err));
2016
2011
  }
2017
2012
 
2013
+ casted.$session(options.session);
2014
+
2018
2015
  if (options.rawResult) {
2019
2016
  res.value = casted;
2020
2017
  return process.nextTick(() => callback(null, res));
@@ -2167,7 +2164,6 @@ Query.prototype._findOneAndUpdate = function(callback) {
2167
2164
  }
2168
2165
 
2169
2166
  this._findAndModify('update', callback);
2170
- return this;
2171
2167
  };
2172
2168
 
2173
2169
  /**
@@ -2247,6 +2243,128 @@ Query.prototype.findOneAndRemove = function(conditions, options, callback) {
2247
2243
  return this;
2248
2244
  };
2249
2245
 
2246
+ /**
2247
+ * Issues a MongoDB [findOneAndDelete](https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndDelete/) command.
2248
+ *
2249
+ * Finds a matching document, removes it, and passes the found document (if any) to the callback. Executes immediately if `callback` is passed.
2250
+ *
2251
+ * This function triggers the following middleware.
2252
+ *
2253
+ * - `findOneAndDelete()`
2254
+ *
2255
+ * This function differs slightly from `Model.findOneAndRemove()` in that
2256
+ * `findOneAndRemove()` becomes a [MongoDB `findAndModify()` command](https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/),
2257
+ * as opposed to a `findOneAndDelete()` command. For most mongoose use cases,
2258
+ * this distinction is purely pedantic. You should use `findOneAndDelete()`
2259
+ * unless you have a good reason not to.
2260
+ *
2261
+ * ####Available options
2262
+ *
2263
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
2264
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2265
+ * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2266
+ *
2267
+ * ####Callback Signature
2268
+ * function(error, doc) {
2269
+ * // error: any errors that occurred
2270
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
2271
+ * }
2272
+ *
2273
+ * ####Examples
2274
+ *
2275
+ * A.where().findOneAndDelete(conditions, options, callback) // executes
2276
+ * A.where().findOneAndDelete(conditions, options) // return Query
2277
+ * A.where().findOneAndDelete(conditions, callback) // executes
2278
+ * A.where().findOneAndDelete(conditions) // returns Query
2279
+ * A.where().findOneAndDelete(callback) // executes
2280
+ * A.where().findOneAndDelete() // returns Query
2281
+ *
2282
+ * @method findOneAndDelete
2283
+ * @memberOf Query
2284
+ * @param {Object} [conditions]
2285
+ * @param {Object} [options]
2286
+ * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2287
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2288
+ * @param {Function} [callback] optional params are (error, document)
2289
+ * @return {Query} this
2290
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2291
+ * @api public
2292
+ */
2293
+
2294
+ Query.prototype.findOneAndDelete = function(conditions, options, callback) {
2295
+ this.op = 'findOneAndDelete';
2296
+ this._validate();
2297
+
2298
+ switch (arguments.length) {
2299
+ case 2:
2300
+ if (typeof options === 'function') {
2301
+ callback = options;
2302
+ options = {};
2303
+ }
2304
+ break;
2305
+ case 1:
2306
+ if (typeof conditions === 'function') {
2307
+ callback = conditions;
2308
+ conditions = undefined;
2309
+ options = undefined;
2310
+ }
2311
+ break;
2312
+ }
2313
+
2314
+ if (mquery.canMerge(conditions)) {
2315
+ this.merge(conditions);
2316
+ }
2317
+
2318
+ options && this.setOptions(options);
2319
+
2320
+ if (!callback) {
2321
+ return this;
2322
+ }
2323
+
2324
+ this._findOneAndDelete(callback);
2325
+
2326
+ return this;
2327
+ };
2328
+
2329
+ /*!
2330
+ * Thunk around findOneAndDelete()
2331
+ *
2332
+ * @param {Function} [callback]
2333
+ * @return {Query} this
2334
+ * @api private
2335
+ */
2336
+ Query.prototype._findOneAndDelete = function(callback) {
2337
+ this._castConditions();
2338
+
2339
+ if (this.error() != null) {
2340
+ callback(this.error());
2341
+ return null;
2342
+ }
2343
+
2344
+ const filter = this._conditions;
2345
+ const options = this._optionsForExec();
2346
+ let fields = null;
2347
+
2348
+ if (this._fields != null) {
2349
+ options.projection = this._castFields(utils.clone(this._fields));
2350
+ fields = options.projection;
2351
+ if (fields instanceof Error) {
2352
+ callback(fields);
2353
+ return null;
2354
+ }
2355
+ }
2356
+
2357
+ this._collection.collection.findOneAndDelete(filter, options, (err, res) => {
2358
+ if (err) {
2359
+ return callback(err);
2360
+ }
2361
+
2362
+ const doc = res.value;
2363
+
2364
+ return this._completeOne(doc, res, callback);
2365
+ });
2366
+ };
2367
+
2250
2368
  /*!
2251
2369
  * Thunk around findOneAndRemove()
2252
2370
  *
@@ -2256,10 +2374,11 @@ Query.prototype.findOneAndRemove = function(conditions, options, callback) {
2256
2374
  */
2257
2375
  Query.prototype._findOneAndRemove = function(callback) {
2258
2376
  if (this.error() != null) {
2259
- return callback(this.error());
2377
+ callback(this.error());
2378
+ return;
2260
2379
  }
2261
2380
 
2262
- Query.base.findOneAndRemove.call(this, callback);
2381
+ this._findAndModify('remove', callback);
2263
2382
  };
2264
2383
 
2265
2384
  /*!
@@ -2346,7 +2465,6 @@ Query.prototype._findAndModify = function(type, callback) {
2346
2465
  }
2347
2466
 
2348
2467
  this._applyPaths();
2349
- var userProvidedFields = this._userProvidedFields || {};
2350
2468
 
2351
2469
  var options = this._mongooseOptions;
2352
2470
 
@@ -2365,32 +2483,7 @@ Query.prototype._findAndModify = function(type, callback) {
2365
2483
  return callback(err);
2366
2484
  }
2367
2485
 
2368
- if (!doc || (utils.isObject(doc) && Object.keys(doc).length === 0)) {
2369
- if (opts.rawResult) {
2370
- return callback(null, res);
2371
- }
2372
- return callback(null, null);
2373
- }
2374
-
2375
- if (!options.populate) {
2376
- if (!!options.lean === true) {
2377
- return _completeOneLean(doc, res, opts, callback);
2378
- }
2379
- return completeOne(_this.model, doc, res, opts, fields, userProvidedFields, null, callback);
2380
- }
2381
-
2382
- var pop = helpers.preparePopulationOptionsMQ(_this, options);
2383
- pop.__noPromise = true;
2384
- _this.model.populate(doc, pop, function(err, doc) {
2385
- if (err) {
2386
- return callback(err);
2387
- }
2388
-
2389
- if (!!options.lean === true) {
2390
- return _completeOneLean(doc, res, opts, callback);
2391
- }
2392
- return completeOne(_this.model, doc, res, opts, fields, userProvidedFields, pop, callback);
2393
- });
2486
+ _this._completeOne(doc, res, callback);
2394
2487
  };
2395
2488
 
2396
2489
  var _callback;
@@ -2550,7 +2643,7 @@ function _updateThunk(op, callback) {
2550
2643
 
2551
2644
  if (this.error() != null) {
2552
2645
  callback(this.error());
2553
- return this;
2646
+ return null;
2554
2647
  }
2555
2648
 
2556
2649
  var castedQuery = this._conditions;
@@ -2566,12 +2659,12 @@ function _updateThunk(op, callback) {
2566
2659
 
2567
2660
  if (castedDoc instanceof Error) {
2568
2661
  callback(castedDoc);
2569
- return this;
2662
+ return null;
2570
2663
  }
2571
2664
 
2572
2665
  if (castedDoc == null || Object.keys(castedDoc).length === 0) {
2573
2666
  callback(null, 0);
2574
- return this;
2667
+ return null;
2575
2668
  }
2576
2669
 
2577
2670
  castedDoc = setDefaultsOnInsert(this._conditions, this.model.schema,
@@ -2603,7 +2696,7 @@ function _updateThunk(op, callback) {
2603
2696
  callback(err);
2604
2697
  });
2605
2698
  }
2606
- return this;
2699
+ return null;
2607
2700
  }
2608
2701
 
2609
2702
  if (castedDoc.toBSON) {
@@ -2611,7 +2704,7 @@ function _updateThunk(op, callback) {
2611
2704
  }
2612
2705
 
2613
2706
  this._collection[op](castedQuery, castedDoc, options, callback);
2614
- return this;
2707
+ return null;
2615
2708
  }
2616
2709
 
2617
2710
  /*!
@@ -1,11 +1,12 @@
1
+ 'use strict';
1
2
 
2
3
  /*!
3
4
  * Module dependencies
4
5
  */
5
6
 
6
- var get = require('lodash.get');
7
- var isDefiningProjection = require('./services/projection/isDefiningProjection');
8
- var utils = require('./utils');
7
+ const get = require('lodash.get');
8
+ const isDefiningProjection = require('./services/projection/isDefiningProjection');
9
+ const utils = require('./utils');
9
10
 
10
11
  /*!
11
12
  * Prepare a set of path options for query population.
@@ -16,7 +17,7 @@ var utils = require('./utils');
16
17
  */
17
18
 
18
19
  exports.preparePopulationOptions = function preparePopulationOptions(query, options) {
19
- var pop = utils.object.vals(query.options.populate);
20
+ const pop = utils.object.vals(query.options.populate);
20
21
 
21
22
  // lean options should trickle through all queries
22
23
  if (options.lean) {
@@ -36,7 +37,7 @@ exports.preparePopulationOptions = function preparePopulationOptions(query, opti
36
37
  */
37
38
 
38
39
  exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query, options) {
39
- var pop = utils.object.vals(query._mongooseOptions.populate);
40
+ const pop = utils.object.vals(query._mongooseOptions.populate);
40
41
 
41
42
  // lean options should trickle through all queries
42
43
  if (options.lean) {
@@ -54,12 +55,12 @@ exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query,
54
55
  * @param {string} value
55
56
  */
56
57
  function getDiscriminatorByValue(model, value) {
57
- var discriminator = null;
58
+ let discriminator = null;
58
59
  if (!model.discriminators) {
59
60
  return discriminator;
60
61
  }
61
- for (var name in model.discriminators) {
62
- var it = model.discriminators[name];
62
+ for (const name in model.discriminators) {
63
+ const it = model.discriminators[name];
63
64
  if (
64
65
  it.schema &&
65
66
  it.schema.discriminatorMapping &&
@@ -85,37 +86,63 @@ exports.getDiscriminatorByValue = getDiscriminatorByValue;
85
86
  * @return {Model}
86
87
  */
87
88
  exports.createModel = function createModel(model, doc, fields, userProvidedFields) {
88
- var discriminatorMapping = model.schema
89
- ? model.schema.discriminatorMapping
90
- : null;
89
+ const discriminatorMapping = model.schema ?
90
+ model.schema.discriminatorMapping :
91
+ null;
91
92
 
92
- var key = discriminatorMapping && discriminatorMapping.isRoot
93
- ? discriminatorMapping.key
94
- : null;
93
+ const key = discriminatorMapping && discriminatorMapping.isRoot ?
94
+ discriminatorMapping.key :
95
+ null;
95
96
 
96
- var value = doc[key];
97
+ const value = doc[key];
97
98
  if (key && value && model.discriminators) {
98
- var discriminator = model.discriminators[value] || getDiscriminatorByValue(model, value);
99
+ const discriminator = model.discriminators[value] || getDiscriminatorByValue(model, value);
99
100
  if (discriminator) {
100
- var _fields = utils.clone(userProvidedFields);
101
+ const _fields = utils.clone(userProvidedFields);
101
102
  exports.applyPaths(_fields, discriminator.schema);
102
103
  return new discriminator(undefined, _fields, true);
103
104
  }
104
105
  }
105
106
 
106
- return new model(undefined, fields, { skipId: true, isNew: false });
107
+ const skipDefaults = gatherPaths(doc, {}, '');
108
+ return new model(undefined, fields, {
109
+ skipId: true,
110
+ isNew: false,
111
+ skipDefaults: skipDefaults
112
+ });
107
113
  };
108
114
 
115
+ /*!
116
+ *
117
+ */
118
+
119
+ function gatherPaths(obj, map, path) {
120
+ for (const key of Object.keys(obj)) {
121
+ const fullPath = path ? path + '.' + key : key;
122
+ map[fullPath] = true;
123
+ if (obj[key] != null &&
124
+ typeof obj[key] === 'object' &&
125
+ !Array.isArray(obj) &&
126
+ !(obj instanceof Map) &&
127
+ !obj[key]._bsontype &&
128
+ !utils.isMongooseObject(obj[key])) {
129
+ gatherPaths(obj[key], map, fullPath);
130
+ }
131
+ }
132
+
133
+ return map;
134
+ }
135
+
109
136
  /*!
110
137
  * ignore
111
138
  */
112
139
 
113
140
  exports.applyPaths = function applyPaths(fields, schema) {
114
141
  // determine if query is selecting or excluding fields
115
- var exclude;
116
- var keys;
117
- var ki;
118
- var field;
142
+ let exclude;
143
+ let keys;
144
+ let ki;
145
+ let field;
119
146
 
120
147
  if (fields) {
121
148
  keys = Object.keys(fields);
@@ -138,11 +165,11 @@ exports.applyPaths = function applyPaths(fields, schema) {
138
165
  // if selecting, apply default schematype select:true fields
139
166
  // if excluding, apply schematype select:false fields
140
167
 
141
- var selected = [];
142
- var excluded = [];
143
- var stack = [];
168
+ let selected = [];
169
+ let excluded = [];
170
+ let stack = [];
144
171
 
145
- var analyzePath = function(path, type) {
172
+ let analyzePath = function(path, type) {
146
173
  if (typeof type.selected !== 'boolean') return;
147
174
 
148
175
  var plusPath = '+' + path;
@@ -160,8 +187,8 @@ exports.applyPaths = function applyPaths(fields, schema) {
160
187
  }
161
188
 
162
189
  // check for parent exclusions
163
- var pieces = path.split('.');
164
- var root = pieces[0];
190
+ let pieces = path.split('.');
191
+ let root = pieces[0];
165
192
  if (~excluded.indexOf(root)) {
166
193
  return;
167
194
  }
@@ -170,10 +197,10 @@ exports.applyPaths = function applyPaths(fields, schema) {
170
197
  // don't explicitly project in the discriminator key because that will
171
198
  // project out everything else under the parent path
172
199
  if (!exclude && get(type, 'options.$skipDiscriminatorCheck', false)) {
173
- var cur = '';
174
- for (var i = 0; i < pieces.length; ++i) {
200
+ let cur = '';
201
+ for (let i = 0; i < pieces.length; ++i) {
175
202
  cur += (cur.length === 0 ? '' : '.') + pieces[i];
176
- var projection = get(fields, cur, false);
203
+ const projection = get(fields, cur, false);
177
204
  if (projection && typeof projection !== 'object') {
178
205
  return;
179
206
  }
@@ -208,7 +235,7 @@ exports.applyPaths = function applyPaths(fields, schema) {
208
235
 
209
236
  analyzeSchema(schema);
210
237
 
211
- var i;
238
+ let i;
212
239
  switch (exclude) {
213
240
  case true:
214
241
  for (i = 0; i < excluded.length; ++i) {
@@ -221,8 +221,8 @@ SchemaArray.prototype.cast = function(value, doc, init) {
221
221
  */
222
222
 
223
223
  SchemaArray.prototype.castForQuery = function($conditional, value) {
224
- var handler,
225
- val;
224
+ var handler;
225
+ var val;
226
226
 
227
227
  if (arguments.length === 2) {
228
228
  handler = this.$conditionalHandlers[$conditional];
@@ -355,13 +355,16 @@ handle.$type = $type;
355
355
  handle.$eq =
356
356
  handle.$gt =
357
357
  handle.$gte =
358
- handle.$in =
359
358
  handle.$lt =
360
359
  handle.$lte =
361
360
  handle.$ne =
362
361
  handle.$nin =
363
362
  handle.$regex = SchemaArray.prototype.castForQuery;
364
363
 
364
+ // `$in` is special because you can also include an empty array in the query
365
+ // like `$in: [1, []]`, see gh-5913
366
+ handle.$in = SchemaType.prototype.$conditionalHandlers.$in;
367
+
365
368
  /*!
366
369
  * Module exports.
367
370
  */