mongoose 5.2.3 → 5.2.7

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.
Files changed (46) hide show
  1. package/.eslintignore +1 -0
  2. package/.travis.yml +1 -1
  3. package/History.md +58 -0
  4. package/README.md +6 -6
  5. package/lib/aggregate.js +22 -0
  6. package/lib/browser.js +2 -0
  7. package/lib/cast/boolean.js +5 -6
  8. package/lib/cast/number.js +49 -0
  9. package/lib/collection.js +2 -5
  10. package/lib/connection.js +22 -3
  11. package/lib/document.js +31 -3
  12. package/lib/driver.js +15 -0
  13. package/lib/drivers/browser/index.js +3 -0
  14. package/lib/drivers/node-mongodb-native/collection.js +23 -9
  15. package/lib/drivers/node-mongodb-native/index.js +1 -0
  16. package/lib/helpers/immediate.js +10 -0
  17. package/lib/helpers/populate/getSchemaTypes.js +80 -62
  18. package/lib/helpers/populate/getVirtual.js +2 -5
  19. package/lib/helpers/query/applyQueryMiddleware.js +37 -0
  20. package/lib/helpers/query/castUpdate.js +34 -12
  21. package/lib/index.js +6 -4
  22. package/lib/model.js +98 -100
  23. package/lib/query.js +125 -25
  24. package/lib/queryhelpers.js +21 -2
  25. package/lib/schema/boolean.js +40 -0
  26. package/lib/schema/documentarray.js +18 -5
  27. package/lib/schema/embedded.js +7 -2
  28. package/lib/schema/number.js +11 -31
  29. package/lib/schema/objectid.js +1 -4
  30. package/lib/schema.js +1 -1
  31. package/lib/schematype.js +35 -19
  32. package/lib/types/buffer.js +2 -2
  33. package/lib/types/decimal128.js +1 -1
  34. package/lib/types/documentarray.js +2 -2
  35. package/lib/types/embedded.js +2 -1
  36. package/lib/types/map.js +4 -0
  37. package/lib/types/objectid.js +1 -1
  38. package/lib/types/subdocument.js +2 -1
  39. package/lib/utils.js +17 -16
  40. package/migrating_to_5.md +1 -1
  41. package/package.json +4 -6
  42. package/tools/checkNodeVersion.js +7 -0
  43. package/tools/sharded.js +4 -1
  44. package/website.js +2 -1
  45. package/lib/drivers/index.js +0 -20
  46. package/lib/drivers/index.web.js +0 -5
package/.eslintignore CHANGED
@@ -5,3 +5,4 @@ test/model.discriminator.test.js
5
5
  tools/
6
6
  test/es6/
7
7
  test/files/
8
+ dist/
package/.travis.yml CHANGED
@@ -16,6 +16,6 @@ before_script:
16
16
  - ./mongodb-linux-x86_64-3.6.4/bin/mongod --fork --nopreallocj --dbpath ./data/db/27017 --syslog --port 27017
17
17
  script:
18
18
  - npm test
19
- - npm run lint
19
+ - node tools/checkNodeVersion.js "4.x || 5.x" || npm run lint
20
20
  notifications:
21
21
  email: false
package/History.md CHANGED
@@ -1,3 +1,61 @@
1
+ 5.2.7 / 2018-08-06
2
+ ==================
3
+ * fix(model): check `expireAfterSeconds` option when diffing indexes in syncIndexes() #6820 #6819 [christopherhex](https://github.com/christopherhex)
4
+ * chore: fix some common test flakes in travis #6816 [Fonger](https://github.com/Fonger)
5
+ * chore: bump eslint and webpack to avoid bad versions of eslint-scope #6814
6
+ * test(model): add delay to session tests to improve pass rate #6811 [Fonger](https://github.com/Fonger)
7
+ * fix(model): support options in `deleteMany` #6810 [Fonger](https://github.com/Fonger)
8
+ * fix(query): don't use $each when pushing an array into an array #6809 [lineus](https://github.com/lineus)
9
+ * chore: bump mquery so eslint isn't a prod dependency #6800
10
+ * fix(populate): correctly get schema type when calling `populate()` on already populated path #6798
11
+ * fix(populate): propagate readConcern options in populate from parent query #6792 #6785 [Fonger](https://github.com/Fonger)
12
+ * docs(connection): add description of useNewUrlParser option #6789
13
+ * fix(query): make select('+path') a no-op if no select prop in schema #6785
14
+ * docs(schematype+validation): document using function syntax for custom validator message #6772
15
+ * fix(update): throw CastError if updating with `$inc: null` #6770
16
+ * fix(connection): throw helpful error when calling `createConnection(undefined)` #6763
17
+
18
+ 5.2.6 / 2018-07-30
19
+ ==================
20
+ * fix(document): don't double-call deeply nested custom getters when using `get()` #6779 #6637
21
+ * fix(query): upgrade mquery for readConcern() helper #6777
22
+ * docs(schematypes): clean up typos #6773 [sajadtorkamani](https://github.com/sajadtorkamani)
23
+ * refactor(browser): fix webpack warnings #6771 #6705
24
+ * fix(populate): make error reported when no `localField` specified catchable #6767
25
+ * docs(connection): use correct form in createConnection example #6766 [lineus](https://github.com/lineus)
26
+ * fix(connection): throw helpful error when using legacy `mongoose.connect()` syntax #6756
27
+ * fix(document): handle overwriting `$session` in `execPopulate()` #6754
28
+ * fix(query): propagate top-level session down to `populate()` #6754
29
+ * fix(aggregate): add `session()` helper for consistency with query api #6752
30
+ * fix(map): avoid infinite recursion when update overwrites a map #6750
31
+ * fix(model): be consistent about passing noop callback to mongoose.model() `init()` as well as db.model() #6707
32
+
33
+ 5.2.5 / 2018-07-23
34
+ ==================
35
+ * fix(boolean): expose `convertToTrue` and `convertToFalse` for custom boolean casting #6758
36
+ * docs(schematypes): add note about what values are converted to booleans #6758
37
+ * fix(document): fix(document): report castError when setting single nested doc to array #6753
38
+ * docs: prefix mongoose.Schema call with new operator #6751 [sajadtorkamani](https://github.com/sajadtorkamani)
39
+ * docs(query): add examples and links to schema writeConcern option for writeConcern helpers #6748
40
+ * docs(middleware): clarify that init middleware is sync #6747
41
+ * perf(model): create error rather than modifying stack for source map perf #6735
42
+ * fix(model): throw helpful error when passing object to aggregate() #6732
43
+ * fix(model): pass Model instance as context to applyGetters when calling getters for virtual populate #6726 [lineus](https://github.com/lineus)
44
+ * fix(documentarray): remove `isNew` and `save` listeners on CastError because otherwise they never get removed #6723
45
+ * docs(model+query): clarify when to use `countDocuments()` vs `estimatedDocumentCount()` #6713
46
+ * fix(populate): correctly set virtual nestedSchemaPath when calling populate() multiple times #6644
47
+ * docs(connections): add note about the `family` option for IPv4 vs IPv6 and add port to example URIs #6566
48
+
49
+ 5.2.4 / 2018-07-16
50
+ ==================
51
+ * docs: Model.insertMany rawResult option in api docs #6724 [lineus](https://github.com/lineus)
52
+ * docs: fix typo on migrating to 5 guide #6722 [iagowp](https://github.com/iagowp)
53
+ * docs: update doc about keepalive #6719 #6718 [simllll](https://github.com/simllll)
54
+ * fix: ensure debug mode doesn't crash with sessions #6712
55
+ * fix(document): report castError when setting single nested doc to primitive value #6710
56
+ * fix(connection): throw helpful error if using `new db.model(foo)(bar)` #6698
57
+ * fix(model): throw readable error with better stack trace when non-cb passed to $wrapCallback() #6640
58
+
1
59
  5.2.3 / 2018-07-11
2
60
  ==================
3
61
  * fix(populate): if a getter is defined on the localField, use it when populating #6702 #6618 [lineus](https://github.com/lineus)
package/README.md CHANGED
@@ -80,14 +80,14 @@ Once connected, the `open` event is fired on the `Connection` instance. If you'r
80
80
  Models are defined through the `Schema` interface.
81
81
 
82
82
  ```js
83
- const Schema = mongoose.Schema,
84
- ObjectId = Schema.ObjectId;
83
+ const Schema = mongoose.Schema;
84
+ const ObjectId = Schema.ObjectId;
85
85
 
86
86
  const BlogPost = new Schema({
87
- author: ObjectId,
88
- title: String,
89
- body: String,
90
-  date: Date
87
+ author: ObjectId,
88
+ title: String,
89
+ body: String,
90
+   date: Date
91
91
  });
92
92
  ```
93
93
 
package/lib/aggregate.js CHANGED
@@ -633,6 +633,27 @@ Aggregate.prototype.hint = function(value) {
633
633
  return this;
634
634
  };
635
635
 
636
+ /**
637
+ * Sets the session for this aggregation. Useful for [transactions](/docs/transactions.html).
638
+ *
639
+ * ####Example:
640
+ *
641
+ * const session = await Model.startSession();
642
+ * await Model.aggregate(..).session(session);
643
+ *
644
+ * @param {ClientSession} session
645
+ * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
646
+ */
647
+
648
+ Aggregate.prototype.session = function(session) {
649
+ if (session == null) {
650
+ delete this.options.session;
651
+ return;
652
+ }
653
+ this.options.session = session;
654
+ return this;
655
+ };
656
+
636
657
  /**
637
658
  * Lets you set arbitrary options, for middleware or plugins.
638
659
  *
@@ -645,6 +666,7 @@ Aggregate.prototype.hint = function(value) {
645
666
  * @param [options.maxTimeMS] number limits the time this aggregation will run, see [MongoDB docs on `maxTimeMS`](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/)
646
667
  * @param [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation
647
668
  * @param [options.collation] object see [`Aggregate.prototype.collation()`](./docs/api.html#aggregate_Aggregate-collation)
669
+ * @param [options.session] ClientSession see [`Aggregate.prototype.session()`](./docs/api.html#aggregate_Aggregate-session)
648
670
  * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
649
671
  * @return {Aggregate} this
650
672
  * @api public
package/lib/browser.js CHANGED
@@ -1,5 +1,7 @@
1
1
  /* eslint-env browser */
2
2
 
3
+ require('./driver').set(require('./drivers/browser'));
4
+
3
5
  var DocumentProvider = require('./document_provider.js');
4
6
  var PromiseProvider = require('./promise_provider');
5
7
 
@@ -2,9 +2,6 @@
2
2
 
3
3
  const CastError = require('../error/cast');
4
4
 
5
- const convertToTrue = [true, 'true', 1, '1', 'yes'];
6
- const convertToFalse = [false, 'false', 0, '0', 'no'];
7
-
8
5
  /*!
9
6
  * Given a value, cast it to a boolean, or throw a `CastError` if the value
10
7
  * cannot be casted. `null` and `undefined` are considered valid.
@@ -21,12 +18,14 @@ module.exports = function castBoolean(value, path) {
21
18
  return value;
22
19
  }
23
20
 
24
- // strict mode (throws if value is not a boolean, instead of converting)
25
- if (convertToTrue.indexOf(value) !== -1) {
21
+ if (module.exports.convertToTrue.has(value)) {
26
22
  return true;
27
23
  }
28
- if (convertToFalse.indexOf(value) !== -1) {
24
+ if (module.exports.convertToFalse.has(value)) {
29
25
  return false;
30
26
  }
31
27
  throw new CastError('boolean', value, path);
32
28
  };
29
+
30
+ module.exports.convertToTrue = new Set([true, 'true', 1, '1', 'yes']);
31
+ module.exports.convertToFalse = new Set([false, 'false', 0, '0', 'no']);
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const CastError = require('../error/cast');
4
+
5
+ /*!
6
+ * Given a value, cast it to a number, or throw a `CastError` if the value
7
+ * cannot be casted. `null` and `undefined` are considered valid.
8
+ *
9
+ * @param {Any} value
10
+ * @param {String} [path] optional the path to set on the CastError
11
+ * @return {Boolean|null|undefined}
12
+ * @throws {CastError} if `value` is not one of the allowed values
13
+ * @api private
14
+ */
15
+
16
+ module.exports = function castNumber(val, path) {
17
+ if (isNaN(val)) {
18
+ throw new CastError('number', val, path);
19
+ }
20
+
21
+ if (val == null) {
22
+ return val;
23
+ }
24
+ if (val === '') {
25
+ return null;
26
+ }
27
+
28
+ if (typeof val === 'string' || typeof val === 'boolean') {
29
+ val = Number(val);
30
+ }
31
+
32
+ if (isNaN(val)) {
33
+ throw new CastError('number', val, path);
34
+ }
35
+ if (val instanceof Number) {
36
+ return val;
37
+ }
38
+ if (typeof val === 'number') {
39
+ return val;
40
+ }
41
+ if (!Array.isArray(val) && typeof val.valueOf === 'function') {
42
+ return Number(val.valueOf());
43
+ }
44
+ if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
45
+ return new Number(val);
46
+ }
47
+
48
+ throw new CastError('number', val, path);
49
+ };
package/lib/collection.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  const EventEmitter = require('events').EventEmitter;
8
8
  const STATES = require('./connectionstate');
9
- const utils = require('./utils');
9
+ const immediate = require('./helpers/immediate');
10
10
 
11
11
  /**
12
12
  * Abstract Collection constructor
@@ -83,10 +83,7 @@ Collection.prototype.conn;
83
83
 
84
84
  Collection.prototype.onOpen = function() {
85
85
  this.buffer = false;
86
- var _this = this;
87
- utils.immediate(function() {
88
- _this.doQueue();
89
- });
86
+ immediate(() => this.doQueue());
90
87
  };
91
88
 
92
89
  /**
package/lib/connection.js CHANGED
@@ -5,9 +5,8 @@
5
5
  */
6
6
 
7
7
  const EventEmitter = require('events').EventEmitter;
8
- const driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
9
8
  const Schema = require('./schema');
10
- const Collection = require(driver + '/collection');
9
+ const Collection = require('./driver').get().Collection;
11
10
  const STATES = require('./connectionstate');
12
11
  const MongooseError = require('./error');
13
12
  const PromiseProvider = require('./promise_provider');
@@ -414,6 +413,19 @@ Connection.prototype.openUri = function(uri, options, callback) {
414
413
  options = null;
415
414
  }
416
415
 
416
+ if (['string', 'number'].indexOf(typeof options) !== -1) {
417
+ throw new MongooseError('Mongoose 5.x no longer supports ' +
418
+ '`mongoose.connect(host, dbname, port)` or ' +
419
+ '`mongoose.createConnection(host, dbname, port)`. See ' +
420
+ 'http://mongoosejs.com/docs/connections.html for supported connection syntax');
421
+ }
422
+
423
+ if (typeof uri !== 'string') {
424
+ throw new MongooseError('The `uri` parameter to `openUri()` must be a ' +
425
+ `string, got "${typeof uri}". Make sure the first parameter to ` +
426
+ '`mongoose.connect()` or `mongoose.createConnection()` is a string.');
427
+ }
428
+
417
429
  const Promise = PromiseProvider.get();
418
430
  const _this = this;
419
431
 
@@ -710,6 +722,12 @@ Connection.prototype.collection = function(name, options) {
710
722
  */
711
723
 
712
724
  Connection.prototype.model = function(name, schema, collection) {
725
+ if (!(this instanceof Connection)) {
726
+ throw new MongooseError('`connection.model()` should not be run with ' +
727
+ '`new`. If you are doing `new db.model(foo)(bar)`, use ' +
728
+ '`db.model(foo)(bar)` instead');
729
+ }
730
+
713
731
  let fn;
714
732
  if (typeof name === 'function') {
715
733
  fn = name;
@@ -752,7 +770,8 @@ Connection.prototype.model = function(name, schema, collection) {
752
770
  }
753
771
 
754
772
  // Errors handled internally, so safe to ignore error
755
- model.init(() => {});
773
+ model.init(function $modelInitNoop() {});
774
+
756
775
  return model;
757
776
  }
758
777
 
package/lib/document.js CHANGED
@@ -396,7 +396,11 @@ Document.prototype.toBSON = function() {
396
396
  /**
397
397
  * Initializes the document without setters or marking anything modified.
398
398
  *
399
- * Called internally after a document is returned from mongodb.
399
+ * Called internally after a document is returned from mongodb. Normally,
400
+ * you do **not** need to call this function on your own.
401
+ *
402
+ * This function triggers `init` [middleware](/docs/middleware.html).
403
+ * Note that `init` hooks are [synchronous](/docs/middleware.html#synchronous).
400
404
  *
401
405
  * @param {Object} doc document returned by mongo
402
406
  * @api public
@@ -1111,6 +1115,8 @@ Document.prototype.get = function(path, type, options) {
1111
1115
  obj = void 0;
1112
1116
  } else if (obj instanceof Map) {
1113
1117
  obj = obj.get(pieces[i]);
1118
+ } else if (i === l - 1) {
1119
+ obj = utils.getValue(pieces[i], obj);
1114
1120
  } else {
1115
1121
  obj = obj[pieces[i]];
1116
1122
  }
@@ -2358,8 +2364,8 @@ Document.prototype.$toObject = function(options, json) {
2358
2364
  *
2359
2365
  * ####Options:
2360
2366
  *
2361
- * - `getters` apply all getters (path and virtual getters)
2362
- * - `virtuals` apply virtual getters (can override `getters` option)
2367
+ * - `getters` apply all getters (path and virtual getters), defaults to false
2368
+ * - `virtuals` apply virtual getters (can override `getters` option), defaults to false
2363
2369
  * - `minimize` remove empty objects (defaults to true)
2364
2370
  * - `transform` a transform function to apply to the resulting document before returning
2365
2371
  * - `depopulate` depopulate any populated paths, replacing them with their original refs (defaults to false)
@@ -2462,6 +2468,12 @@ Document.prototype.$toObject = function(options, json) {
2462
2468
  * _During save, no custom options are applied to the document before being sent to the database._
2463
2469
  *
2464
2470
  * @param {Object} [options]
2471
+ * @param {Boolean} [options.getters=false] if true, apply all getters, including virtuals
2472
+ * @param {Boolean} [options.virtuals=false] if true, apply virtuals. Use `{ getters: true, virtuals: false }` to just apply getters, not virtuals
2473
+ * @param {Boolean} [options.minimize=true] if true, omit any empty objects from the output
2474
+ * @param {Function|null} [options.transform=null] if set, mongoose will call this function to allow you to transform the returned object
2475
+ * @param {Boolean} [options.depopulate=false] if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths.
2476
+ * @param {Boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output
2465
2477
  * @return {Object} js object
2466
2478
  * @see mongodb.Binary http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
2467
2479
  * @api public
@@ -2753,6 +2765,22 @@ Document.prototype.populate = function populate() {
2753
2765
  populateOptions.path = nestedPath + '.' + populateOptions.path;
2754
2766
  });
2755
2767
  }
2768
+
2769
+ // Use `$session()` by default if the document has an associated session
2770
+ // See gh-6754
2771
+ if (this.$session() != null) {
2772
+ const session = this.$session();
2773
+ paths.forEach(path => {
2774
+ if (path.options == null) {
2775
+ path.options = { session: session };
2776
+ return;
2777
+ }
2778
+ if (!('session' in path.options)) {
2779
+ path.options.session = session;
2780
+ }
2781
+ });
2782
+ }
2783
+
2756
2784
  topLevelModel.populate(this, paths, fn);
2757
2785
  }
2758
2786
 
package/lib/driver.js ADDED
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ /*!
4
+ * ignore
5
+ */
6
+
7
+ let driver = null;
8
+
9
+ module.exports.get = function() {
10
+ return driver;
11
+ };
12
+
13
+ module.exports.set = function(v) {
14
+ driver = v;
15
+ };
@@ -3,6 +3,9 @@
3
3
  */
4
4
 
5
5
  exports.Binary = require('./binary');
6
+ exports.Collection = function() {
7
+ throw new Error('Cannot create a collection from browser library');
8
+ };
6
9
  exports.Decimal128 = require('./decimal128');
7
10
  exports.ObjectId = require('./objectid');
8
11
  exports.ReadPreference = require('./ReadPreference');
@@ -7,7 +7,7 @@
7
7
  const MongooseCollection = require('../../collection');
8
8
  const Collection = require('mongodb').Collection;
9
9
  const get = require('lodash.get');
10
- const utils = require('../../utils');
10
+ const sliced = require('sliced');
11
11
 
12
12
  /**
13
13
  * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
@@ -72,7 +72,7 @@ NativeCollection.prototype.onOpen = function() {
72
72
  }
73
73
  } else {
74
74
  // create
75
- var opts = utils.clone(_this.opts.capped);
75
+ const opts = Object.assign({}, _this.opts.capped);
76
76
  opts.capped = true;
77
77
  _this.conn.db.createCollection(_this.name, opts, callback);
78
78
  }
@@ -132,7 +132,7 @@ function iter(i) {
132
132
  if (debug) {
133
133
  if (typeof debug === 'function') {
134
134
  debug.apply(_this,
135
- [_this.name, i].concat(utils.args(args, 0, args.length - 1)));
135
+ [_this.name, i].concat(sliced(args, 0, args.length - 1)));
136
136
  } else {
137
137
  this.$print(_this.name, i, args);
138
138
  }
@@ -224,7 +224,7 @@ function format(obj, sub) {
224
224
  return obj;
225
225
  }
226
226
 
227
- var x = utils.clone(obj, {transform: false});
227
+ var x = require('../../utils').clone(obj, {transform: false});
228
228
  var representation;
229
229
 
230
230
  if (x.constructor.name === 'Binary') {
@@ -242,8 +242,17 @@ function format(obj, sub) {
242
242
  for (var i = 0; i < numKeys; ++i) {
243
243
  key = keys[i];
244
244
  if (x[key]) {
245
+ let error;
245
246
  if (typeof x[key].toBSON === 'function') {
246
- x[key] = x[key].toBSON();
247
+ try {
248
+ // `session.toBSON()` throws an error. This means we throw errors
249
+ // in debug mode when using transactions, see gh-6712. As a
250
+ // workaround, catch `toBSON()` errors, try to serialize without
251
+ // `toBSON()`, and rethrow if serialization still fails.
252
+ x[key] = x[key].toBSON();
253
+ } catch (_error) {
254
+ error = _error;
255
+ }
247
256
  }
248
257
  if (x[key].constructor.name === 'Binary') {
249
258
  x[key] = 'BinData(' + x[key].sub_type + ', "' +
@@ -260,6 +269,11 @@ function format(obj, sub) {
260
269
  x[key] = {inspect: function() { return representation; }};
261
270
  } else if (Array.isArray(x[key])) {
262
271
  x[key] = x[key].map(map);
272
+ } else if (error != null) {
273
+ // If there was an error with `toBSON()` and the object wasn't
274
+ // already converted to a string representation, rethrow it.
275
+ // Open to better ideas on how to handle this.
276
+ throw error;
263
277
  }
264
278
  }
265
279
  }
@@ -268,10 +282,10 @@ function format(obj, sub) {
268
282
  return x;
269
283
  }
270
284
 
271
- return require('util')
272
- .inspect(x, false, 10, true)
273
- .replace(/\n/g, '')
274
- .replace(/\s{2,}/g, ' ');
285
+ return require('util').
286
+ inspect(x, false, 10, true).
287
+ replace(/\n/g, '').
288
+ replace(/\s{2,}/g, ' ');
275
289
  }
276
290
 
277
291
  /**
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  exports.Binary = require('./binary');
6
+ exports.Collection = require('./collection');
6
7
  exports.Decimal128 = require('./decimal128');
7
8
  exports.ObjectId = require('./objectid');
8
9
  exports.ReadPreference = require('./ReadPreference');
@@ -0,0 +1,10 @@
1
+ /*!
2
+ * Centralize this so we can more easily work around issues with people
3
+ * stubbing out `process.nextTick()` in tests using sinon:
4
+ * https://github.com/sinonjs/lolex#automatically-incrementing-mocked-time
5
+ * See gh-6074
6
+ */
7
+
8
+ module.exports = function immediate(cb) {
9
+ return process.nextTick(cb);
10
+ };
@@ -4,8 +4,9 @@
4
4
  * ignore
5
5
  */
6
6
 
7
- var Mixed = require('../../schema/mixed');
8
- var mpath = require('mpath');
7
+ const Mixed = require('../../schema/mixed');
8
+ const get = require('lodash.get');
9
+ const mpath = require('mpath');
9
10
 
10
11
  /*!
11
12
  * @param {Schema} schema
@@ -28,79 +29,96 @@ module.exports = function getSchemaTypes(schema, doc, path) {
28
29
  while (p--) {
29
30
  trypath = parts.slice(0, p).join('.');
30
31
  foundschema = schema.path(trypath);
31
- if (foundschema) {
32
- if (foundschema.caster) {
33
- // array of Mixed?
34
- if (foundschema.caster instanceof Mixed) {
35
- return foundschema.caster;
36
- }
37
32
 
38
- let schemas = null;
39
- if (doc != null && foundschema.schema != null && foundschema.schema.discriminators != null) {
40
- const discriminators = foundschema.schema.discriminators;
41
- const discriminatorKeyPath = trypath + '.' +
42
- foundschema.schema.options.discriminatorKey;
43
- const keys = mpath.get(discriminatorKeyPath, subdoc) || [];
44
- schemas = Object.keys(discriminators).
45
- reduce(function(cur, discriminator) {
46
- if (keys.indexOf(discriminator) !== -1) {
47
- cur.push(discriminators[discriminator]);
48
- }
49
- return cur;
50
- }, []);
51
- }
33
+ if (foundschema == null) {
34
+ continue;
35
+ }
52
36
 
53
- // Now that we found the array, we need to check if there
54
- // are remaining document paths to look up for casting.
55
- // Also we need to handle array.$.path since schema.path
56
- // doesn't work for that.
57
- // If there is no foundschema.schema we are dealing with
58
- // a path like array.$
59
- if (p !== parts.length && foundschema.schema) {
60
- let ret;
61
- if (parts[p] === '$') {
62
- if (p + 1 === parts.length) {
63
- // comments.$
64
- return foundschema;
65
- }
66
- // comments.$.comments.$.title
67
- ret = search(parts.slice(p + 1), schema, mpath.get(trypath, subdoc));
68
- if (ret) {
69
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
70
- !foundschema.schema.$isSingleNested;
71
- }
72
- return ret;
73
- }
37
+ if (foundschema.caster) {
38
+ // array of Mixed?
39
+ if (foundschema.caster instanceof Mixed) {
40
+ return foundschema.caster;
41
+ }
74
42
 
75
- if (schemas != null && schemas.length > 0) {
76
- ret = [];
77
- for (var i = 0; i < schemas.length; ++i) {
78
- let _ret = search(parts.slice(p), schemas[i], mpath.get(trypath, subdoc));
79
- if (_ret != null) {
80
- _ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
81
- !foundschema.schema.$isSingleNested;
82
- if (_ret.$isUnderneathDocArray) {
83
- ret.$isUnderneathDocArray = true;
84
- }
85
- ret.push(_ret);
86
- }
43
+ let schemas = null;
44
+ if (doc != null && foundschema.schema != null && foundschema.schema.discriminators != null) {
45
+ const discriminators = foundschema.schema.discriminators;
46
+ const discriminatorKeyPath = trypath + '.' +
47
+ foundschema.schema.options.discriminatorKey;
48
+ const keys = mpath.get(discriminatorKeyPath, subdoc) || [];
49
+ schemas = Object.keys(discriminators).
50
+ reduce(function(cur, discriminator) {
51
+ if (keys.indexOf(discriminator) !== -1) {
52
+ cur.push(discriminators[discriminator]);
87
53
  }
88
- return ret;
89
- } else {
90
- ret = search(parts.slice(p), foundschema.schema, mpath.get(trypath, subdoc));
54
+ return cur;
55
+ }, []);
56
+ }
57
+
58
+ // Now that we found the array, we need to check if there
59
+ // are remaining document paths to look up for casting.
60
+ // Also we need to handle array.$.path since schema.path
61
+ // doesn't work for that.
62
+ // If there is no foundschema.schema we are dealing with
63
+ // a path like array.$
64
+ if (p !== parts.length && foundschema.schema) {
65
+ let ret;
66
+ if (parts[p] === '$') {
67
+ if (p + 1 === parts.length) {
68
+ // comments.$
69
+ return foundschema;
70
+ }
71
+ // comments.$.comments.$.title
72
+ ret = search(parts.slice(p + 1), schema, mpath.get(trypath, subdoc));
73
+ if (ret) {
74
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
75
+ !foundschema.schema.$isSingleNested;
76
+ }
77
+ return ret;
78
+ }
91
79
 
92
- if (ret) {
93
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
80
+ if (schemas != null && schemas.length > 0) {
81
+ ret = [];
82
+ for (var i = 0; i < schemas.length; ++i) {
83
+ let _ret = search(parts.slice(p), schemas[i], mpath.get(trypath, subdoc));
84
+ if (_ret != null) {
85
+ _ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
94
86
  !foundschema.schema.$isSingleNested;
87
+ if (_ret.$isUnderneathDocArray) {
88
+ ret.$isUnderneathDocArray = true;
89
+ }
90
+ ret.push(_ret);
95
91
  }
92
+ }
93
+ return ret;
94
+ } else {
95
+ ret = search(parts.slice(p), foundschema.schema, mpath.get(trypath, subdoc));
96
96
 
97
- return ret;
97
+ if (ret) {
98
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
99
+ !foundschema.schema.$isSingleNested;
98
100
  }
101
+
102
+ return ret;
99
103
  }
100
104
  }
105
+ }
106
+
107
+ if (doc.$__ && doc.populated(trypath)) {
108
+ const schema = get(doc.$__.populated[trypath], 'options.model.schema');
109
+ if (schema != null) {
110
+ const ret = search(parts.slice(p), schema, mpath.get(trypath, subdoc));
111
+
112
+ if (ret) {
113
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
114
+ !schema.$isSingleNested;
115
+ }
101
116
 
102
- return foundschema;
117
+ return ret;
118
+ }
103
119
  }
120
+
121
+ return foundschema;
104
122
  }
105
123
  }
106
124