mongoose 5.7.8 → 5.7.12

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/History.md CHANGED
@@ -1,3 +1,36 @@
1
+ 5.7.12 / 2019-11-19
2
+ ===================
3
+ * fix: avoid throwing error if calling `push()` on a doc array with no parent #8351 #8317 #8312 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez)
4
+ * fix(connection): only buffer for "open" events when calling connection helper while connecting #8319
5
+ * fix(connection): pull default database from connection string if specified #8355 #8354 [zachazar](https://github.com/zachazar)
6
+ * fix(populate+discriminator): handle populating document whose discriminator value is different from discriminator model name #8324
7
+ * fix: add `mongoose.isValidObjectId()` function to test whether Mongoose can cast a value to an objectid #3823
8
+ * fix(model): support setting `excludeIndexes` as schema option for subdocs #8343
9
+ * fix: add SchemaMapOptions class for options to map schematype #8318
10
+ * docs(query): remove duplicate omitUndefined options #8349 [mdumandag](https://github.com/mdumandag)
11
+ * docs(schema): add Schema#paths docs to public API docs #8340
12
+
13
+ 5.7.11 / 2019-11-14
14
+ ===================
15
+ * fix: update mongodb driver -> 3.3.4 #8276
16
+ * fix(model): throw readable error when casting bulkWrite update without a 'filter' or 'update' #8332 #8331 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez)
17
+ * fix(connection): bubble up connected/disconnected events with unified topology #8338 #8337
18
+ * fix(model): delete $versionError after saving #8326 #8048 [Fonger](https://github.com/Fonger)
19
+ * test(model): add test for issue #8040 #8341 [Fonger](https://github.com/Fonger)
20
+
21
+ 5.7.10 / 2019-11-11
22
+ ===================
23
+ * perf(cursor): remove unnecessary `setTimeout()` in `eachAsync()`, 4x speedup in basic benchmarks #8310
24
+ * docs(README): re-order sections for better readability #8321 [dandv](https://github.com/dandv)
25
+ * chore: make npm test not hard-code file paths #8322 [stieg](https://github.com/stieg)
26
+
27
+ 5.7.9 / 2019-11-08
28
+ ==================
29
+ * fix(schema): support setting schema path to an instance of SchemaTypeOptions to fix integration with mongoose-i18n-localize #8297 #8292
30
+ * fix(populate): make `retainNullValues` set array element to `null` if foreign doc with that id was not found #8293
31
+ * fix(document): support getter setting virtual on manually populated doc when calling toJSON() #8295
32
+ * fix(model): allow objects with `toBSON()` to make it to `save()` #8299
33
+
1
34
  5.7.8 / 2019-11-04
2
35
  ==================
3
36
  * fix(document): allow manually populating path within document array #8273
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mongoose
2
2
 
3
- Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment.
3
+ Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks.
4
4
 
5
5
  [![Slack Status](http://slack.mongoosejs.io/badge.svg)](http://slack.mongoosejs.io)
6
6
  [![Build Status](https://api.travis-ci.org/Automattic/mongoose.svg?branch=master)](https://travis-ci.org/Automattic/mongoose)
@@ -22,16 +22,6 @@ Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed
22
22
  - [Help Forum](http://groups.google.com/group/mongoose-orm)
23
23
  - [MongoDB Support](https://docs.mongodb.org/manual/support/)
24
24
 
25
- ## Importing
26
-
27
- ```javascript
28
- // Using Node.js `require()`
29
- const mongoose = require('mongoose');
30
-
31
- // Using ES6 imports
32
- import mongoose from 'mongoose';
33
- ```
34
-
35
25
  ## Plugins
36
26
 
37
27
  Check out the [plugins search site](http://plugins.mongoosejs.io/) to see hundreds of related modules from the community. Next, learn how to write your own plugin from the [docs](http://mongoosejs.com/docs/plugins.html) or [this blog post](http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins).
@@ -55,6 +45,16 @@ First install [node.js](http://nodejs.org/) and [mongodb](https://www.mongodb.or
55
45
  $ npm install mongoose
56
46
  ```
57
47
 
48
+ ## Importing
49
+
50
+ ```javascript
51
+ // Using Node.js `require()`
52
+ const mongoose = require('mongoose');
53
+
54
+ // Using ES6 imports
55
+ import mongoose from 'mongoose';
56
+ ```
57
+
58
58
  ## Overview
59
59
 
60
60
  ### Connecting to MongoDB
@@ -64,9 +64,7 @@ First, we need to define a connection. If your app uses only one database, you s
64
64
  Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`.
65
65
 
66
66
  ```js
67
- const mongoose = require('mongoose');
68
-
69
- mongoose.connect('mongodb://localhost/my_database', {
67
+ await mongoose.connect('mongodb://localhost/my_database', {
70
68
  useNewUrlParser: true,
71
69
  useUnifiedTopology: true
72
70
  });
@@ -172,7 +170,14 @@ MyModel.find({}, function (err, docs) {
172
170
  });
173
171
  ```
174
172
 
175
- You can also `findOne`, `findById`, `update`, etc. For more details check out [the docs](http://mongoosejs.com/docs/queries.html).
173
+ You can also `findOne`, `findById`, `update`, etc.
174
+
175
+ ```js
176
+ const instance = await MyModel.findOne({ ... });
177
+ console.log(instance.my.key); // 'hello'
178
+ ```
179
+
180
+ For more details check out [the docs](http://mongoosejs.com/docs/queries.html).
176
181
 
177
182
  **Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created:
178
183
 
package/lib/connection.js CHANGED
@@ -420,7 +420,7 @@ function _wrapConnHelper(fn) {
420
420
  Array.prototype.slice.call(arguments, 0, arguments.length - 1) :
421
421
  Array.prototype.slice.call(arguments);
422
422
  return utils.promiseOrCallback(cb, cb => {
423
- if (this.readyState !== STATES.connected) {
423
+ if (this.readyState === STATES.connecting) {
424
424
  this.once('open', function() {
425
425
  fn.apply(this, argsWithoutCb.concat([cb]));
426
426
  });
@@ -605,7 +605,13 @@ Connection.prototype.openUri = function(uri, options, callback) {
605
605
  if (err) {
606
606
  return reject(err);
607
607
  }
608
- this.name = dbName != null ? dbName : get(parsed, 'auth.db', null);
608
+ if (dbName) {
609
+ this.name = dbName;
610
+ } else if (parsed.defaultDatabase) {
611
+ this.name = parsed.defaultDatabase;
612
+ } else {
613
+ this.name = get(parsed, 'auth.db', null);
614
+ }
609
615
  this.host = get(parsed, 'hosts.0.host', 'localhost');
610
616
  this.port = get(parsed, 'hosts.0.port', 27017);
611
617
  this.user = this.user || get(parsed, 'auth.username');
@@ -643,11 +649,14 @@ Connection.prototype.openUri = function(uri, options, callback) {
643
649
  if (options.useUnifiedTopology) {
644
650
  if (type === 'Single') {
645
651
  const server = Array.from(db.s.topology.s.servers.values())[0];
646
- server.s.pool.on('reconnect', () => {
652
+ server.s.pool.on('close', () => {
653
+ _this.readyState = STATES.disconnected;
654
+ });
655
+ server.s.topology.on('serverHeartbeatSucceeded', () => {
647
656
  _handleReconnect();
648
657
  });
649
- server.s.pool.on('reconnectFailed', () => {
650
- _this.emit('reconnectFailed');
658
+ server.s.pool.on('reconnect', () => {
659
+ _handleReconnect();
651
660
  });
652
661
  server.s.pool.on('timeout', () => {
653
662
  _this.emit('timeout');
package/lib/document.js CHANGED
@@ -2836,6 +2836,13 @@ Document.prototype.$toObject = function(options, json) {
2836
2836
  minimize: _minimize
2837
2837
  });
2838
2838
 
2839
+ if (utils.hasUserDefinedProperty(options, 'getters')) {
2840
+ cloneOptions.getters = options.getters;
2841
+ }
2842
+ if (utils.hasUserDefinedProperty(options, 'virtuals')) {
2843
+ cloneOptions.virtuals = options.virtuals;
2844
+ }
2845
+
2839
2846
  const depopulate = options.depopulate ||
2840
2847
  get(options, '_parentOptions.depopulate', false);
2841
2848
  // _isNested will only be true if this is not the top level document, we
@@ -2852,6 +2859,10 @@ Document.prototype.$toObject = function(options, json) {
2852
2859
  options.minimize = _minimize;
2853
2860
 
2854
2861
  cloneOptions._parentOptions = options;
2862
+ cloneOptions._skipSingleNestedGetters = true;
2863
+
2864
+ const gettersOptions = Object.assign({}, cloneOptions);
2865
+ gettersOptions._skipSingleNestedGetters = false;
2855
2866
 
2856
2867
  // remember the root transform function
2857
2868
  // to save it from being overwritten by sub-transform functions
@@ -2860,16 +2871,15 @@ Document.prototype.$toObject = function(options, json) {
2860
2871
  let ret = clone(this._doc, cloneOptions) || {};
2861
2872
 
2862
2873
  if (options.getters) {
2863
- applyGetters(this, ret, cloneOptions);
2864
- // applyGetters for paths will add nested empty objects;
2865
- // if minimize is set, we need to remove them.
2874
+ applyGetters(this, ret, gettersOptions);
2875
+
2866
2876
  if (options.minimize) {
2867
2877
  ret = minimize(ret) || {};
2868
2878
  }
2869
2879
  }
2870
2880
 
2871
- if (options.virtuals || options.getters && options.virtuals !== false) {
2872
- applyVirtuals(this, ret, cloneOptions, options);
2881
+ if (options.virtuals || (options.getters && options.virtuals !== false)) {
2882
+ applyVirtuals(this, ret, gettersOptions, options);
2873
2883
  }
2874
2884
 
2875
2885
  if (options.versionKey === false && this.schema.options.versionKey) {
@@ -3180,12 +3190,7 @@ function applyGetters(self, json, options) {
3180
3190
  v = cur[part];
3181
3191
  if (ii === last) {
3182
3192
  const val = self.get(path);
3183
- // Ignore single nested docs: getters will run because of `clone()`
3184
- // before `applyGetters()` in `$toObject()`. Quirk because single
3185
- // nested subdocs are hydrated docs in `_doc` as opposed to POJOs.
3186
- if (val != null && val.$__ == null) {
3187
- branch[part] = clone(val, options);
3188
- }
3193
+ branch[part] = clone(val, options);
3189
3194
  } else if (v == null) {
3190
3195
  if (part in cur) {
3191
3196
  branch[part] = v;
@@ -99,13 +99,11 @@ function asyncQueue() {
99
99
  };
100
100
 
101
101
  function _step() {
102
- setTimeout(() => {
103
- inProgress = null;
104
- if (_queue.length > 0) {
105
- inProgress = id++;
106
- const fn = _queue.shift();
107
- fn(_step);
108
- }
109
- }, 0);
102
+ inProgress = null;
103
+ if (_queue.length > 0) {
104
+ inProgress = id++;
105
+ const fn = _queue.shift();
106
+ fn(_step);
107
+ }
110
108
  }
111
109
  }
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const getDiscriminatorByValue = require('../../queryhelpers').getDiscriminatorByValue;
3
+ const getDiscriminatorByValue = require('./getDiscriminatorByValue');
4
4
 
5
5
  /*!
6
6
  * Find the correct constructor, taking into account discriminators
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ /*!
4
+ * returns discriminator by discriminatorMapping.value
5
+ *
6
+ * @param {Model} model
7
+ * @param {string} value
8
+ */
9
+
10
+ module.exports = function getDiscriminatorByValue(model, value) {
11
+ let discriminator = null;
12
+ if (!model.discriminators) {
13
+ return discriminator;
14
+ }
15
+ for (const name in model.discriminators) {
16
+ const it = model.discriminators[name];
17
+ if (
18
+ it.schema &&
19
+ it.schema.discriminatorMapping &&
20
+ it.schema.discriminatorMapping.value == value
21
+ ) {
22
+ discriminator = it;
23
+ break;
24
+ }
25
+ }
26
+ return discriminator;
27
+ };
@@ -34,6 +34,9 @@ module.exports = function castBulkWrite(model, op, options) {
34
34
  op = op['updateOne'];
35
35
  return (callback) => {
36
36
  try {
37
+ if (!op['filter']) throw new Error('Must provide a filter object.');
38
+ if (!op['update']) throw new Error('Must provide an update object.');
39
+
37
40
  op['filter'] = cast(model.schema, op['filter']);
38
41
  op['update'] = castUpdate(model.schema, op['update'], {
39
42
  strict: model.schema.options.strict,
@@ -61,6 +64,9 @@ module.exports = function castBulkWrite(model, op, options) {
61
64
  op = op['updateMany'];
62
65
  return (callback) => {
63
66
  try {
67
+ if (!op['filter']) throw new Error('Must provide a filter object.');
68
+ if (!op['update']) throw new Error('Must provide an update object.');
69
+
64
70
  op['filter'] = cast(model.schema, op['filter']);
65
71
  op['update'] = castUpdate(model.schema, op['update'], {
66
72
  strict: model.schema.options.strict,
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const modelSymbol = require('../symbols').modelSymbol;
4
+
3
5
  module.exports = assignRawDocsToIdStructure;
4
6
 
5
7
  /*!
@@ -63,8 +65,10 @@ function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, re
63
65
  } else {
64
66
  newOrder.push(doc);
65
67
  }
68
+ } else if (id != null && id[modelSymbol] != null) {
69
+ newOrder.push(id);
66
70
  } else {
67
- newOrder.push(nullIfNotFound ? null : id);
71
+ newOrder.push(options.retainNullValues || nullIfNotFound ? null : id);
68
72
  }
69
73
  } else {
70
74
  // apply findOne behavior - if document in results, assign, else assign null
@@ -2,6 +2,7 @@
2
2
 
3
3
  const MongooseError = require('../../error/index');
4
4
  const get = require('../get');
5
+ const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
5
6
  const isPathExcluded = require('../projection/isPathExcluded');
6
7
  const getSchemaTypes = require('./getSchemaTypes');
7
8
  const getVirtual = require('./getVirtual');
@@ -297,12 +298,18 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
297
298
 
298
299
  if (!schema && discriminatorKey) {
299
300
  modelForFindSchema = utils.getValue(discriminatorKey, doc);
300
-
301
301
  if (modelForFindSchema) {
302
- try {
303
- modelForCurrentDoc = model.db.model(modelForFindSchema);
304
- } catch (error) {
305
- return error;
302
+ // `modelForFindSchema` is the discriminator value, so we might need
303
+ // find the discriminated model name
304
+ const discriminatorModel = getDiscriminatorByValue(model, modelForFindSchema);
305
+ if (discriminatorModel != null) {
306
+ modelForCurrentDoc = discriminatorModel;
307
+ } else {
308
+ try {
309
+ modelForCurrentDoc = model.db.model(modelForFindSchema);
310
+ } catch (error) {
311
+ return error;
312
+ }
306
313
  }
307
314
 
308
315
  schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);
@@ -36,7 +36,8 @@ module.exports = function getIndexes(schema) {
36
36
 
37
37
  if (path.$isMongooseDocumentArray || path.$isSingleNested) {
38
38
  if (get(path, 'options.excludeIndexes') !== true &&
39
- get(path, 'schemaOptions.excludeIndexes') !== true) {
39
+ get(path, 'schemaOptions.excludeIndexes') !== true &&
40
+ get(path, 'schema.options.excludeIndexes') !== true) {
40
41
  collectIndexes(path.schema, prefix + key + '.');
41
42
  }
42
43
 
package/lib/index.js CHANGED
@@ -933,6 +933,51 @@ Mongoose.prototype.DocumentProvider = require('./document_provider');
933
933
 
934
934
  Mongoose.prototype.ObjectId = SchemaTypes.ObjectId;
935
935
 
936
+ /**
937
+ * Returns true if Mongoose can cast the given value to an ObjectId, or
938
+ * false otherwise.
939
+ *
940
+ * ####Example:
941
+ *
942
+ * mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
943
+ * mongoose.isValidObjectId('0123456789ab'); // true
944
+ * mongoose.isValidObjectId(6); // false
945
+ *
946
+ * @method isValidObjectId
947
+ * @api public
948
+ */
949
+
950
+ Mongoose.prototype.isValidObjectId = function(v) {
951
+ if (v == null) {
952
+ return true;
953
+ }
954
+ const base = this || mongoose;
955
+ const ObjectId = base.driver.get().ObjectId;
956
+ if (v instanceof ObjectId) {
957
+ return true;
958
+ }
959
+
960
+ if (v._id != null) {
961
+ if (v._id instanceof ObjectId) {
962
+ return true;
963
+ }
964
+ if (v._id.toString instanceof Function) {
965
+ v = v._id.toString();
966
+ return typeof v === 'string' && (v.length === 12 || v.length === 24);
967
+ }
968
+ }
969
+
970
+ if (v.toString instanceof Function) {
971
+ v = v.toString();
972
+ }
973
+
974
+ if (typeof v === 'string' && (v.length === 12 || v.length === 24)) {
975
+ return true;
976
+ }
977
+
978
+ return false;
979
+ };
980
+
936
981
  /**
937
982
  * The Mongoose Decimal128 [SchemaType](/docs/schematypes.html). Used for
938
983
  * declaring paths in your schema that should be
package/lib/model.js CHANGED
@@ -30,7 +30,7 @@ const assignVals = require('./helpers/populate/assignVals');
30
30
  const castBulkWrite = require('./helpers/model/castBulkWrite');
31
31
  const discriminator = require('./helpers/model/discriminator');
32
32
  const each = require('./helpers/each');
33
- const getDiscriminatorByValue = require('./queryhelpers').getDiscriminatorByValue;
33
+ const getDiscriminatorByValue = require('./helpers/discriminator/getDiscriminatorByValue');
34
34
  const getModelsMapForPopulate = require('./helpers/populate/getModelsMapForPopulate');
35
35
  const immediate = require('./helpers/immediate');
36
36
  const internalToObjectOptions = require('./options').internalToObjectOptions;
@@ -52,6 +52,10 @@ const modelDbSymbol = Symbol('mongoose#Model#db');
52
52
  const modelSymbol = require('./helpers/symbols').modelSymbol;
53
53
  const subclassedSymbol = Symbol('mongoose#Model#subclassed');
54
54
 
55
+ const saveToObjectOptions = Object.assign({}, internalToObjectOptions, {
56
+ bson: true
57
+ });
58
+
55
59
  /**
56
60
  * A Model is a class that's your primary tool for interacting with MongoDB.
57
61
  * An instance of a Model is called a [Document](./api.html#Document).
@@ -244,7 +248,7 @@ Model.prototype.$__handleSave = function(options, callback) {
244
248
 
245
249
  if (this.isNew) {
246
250
  // send entire doc
247
- const obj = this.toObject(internalToObjectOptions);
251
+ const obj = this.toObject(saveToObjectOptions);
248
252
 
249
253
  if ((obj || {})._id === void 0) {
250
254
  // documents must have an _id else mongoose won't know
@@ -461,7 +465,7 @@ Model.prototype.save = function(options, fn) {
461
465
  this.$__save(options, error => {
462
466
  this.$__.saving = undefined;
463
467
  delete this.$__.saveOptions;
464
- delete this.$__.versionError;
468
+ delete this.$__.$versionError;
465
469
 
466
470
  if (error) {
467
471
  this.$__handleReject(error);
@@ -17,12 +17,7 @@ const SchemaTypeOptions = require('./SchemaTypeOptions');
17
17
 
18
18
  class SchemaArrayOptions extends SchemaTypeOptions {}
19
19
 
20
- const opts = {
21
- enumerable: true,
22
- configurable: true,
23
- writable: true,
24
- value: null
25
- };
20
+ const opts = require('./propertyOptions');
26
21
 
27
22
  /**
28
23
  * If this is an array of strings, an array of allowed values for this path.
@@ -17,12 +17,7 @@ const SchemaTypeOptions = require('./SchemaTypeOptions');
17
17
 
18
18
  class SchemaBufferOptions extends SchemaTypeOptions {}
19
19
 
20
- const opts = {
21
- enumerable: true,
22
- configurable: true,
23
- writable: true,
24
- value: null
25
- };
20
+ const opts = require('./propertyOptions');
26
21
 
27
22
  /**
28
23
  * Set the default subtype for this buffer.
@@ -17,12 +17,7 @@ const SchemaTypeOptions = require('./SchemaTypeOptions');
17
17
 
18
18
  class SchemaDateOptions extends SchemaTypeOptions {}
19
19
 
20
- const opts = {
21
- enumerable: true,
22
- configurable: true,
23
- writable: true,
24
- value: null
25
- };
20
+ const opts = require('./propertyOptions');
26
21
 
27
22
  /**
28
23
  * If set, Mongoose adds a validator that checks that this path is after the
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const SchemaTypeOptions = require('./SchemaTypeOptions');
4
+
5
+ /**
6
+ * The options defined on an Document Array schematype.
7
+ *
8
+ * ####Example:
9
+ *
10
+ * const schema = new Schema({ users: [{ name: string }] });
11
+ * schema.path('users').options; // SchemaDocumentArrayOptions instance
12
+ *
13
+ * @api public
14
+ * @inherits SchemaTypeOptions
15
+ * @constructor SchemaDocumentOptions
16
+ */
17
+
18
+ class SchemaDocumentArrayOptions extends SchemaTypeOptions {}
19
+
20
+ const opts = require('./propertyOptions');
21
+
22
+ /**
23
+ * If `true`, Mongoose will skip building any indexes defined in this array's schema.
24
+ * If not set, Mongoose will build all indexes defined in this array's schema.
25
+ *
26
+ * ####Example:
27
+ *
28
+ * const childSchema = Schema({ name: { type: String, index: true } });
29
+ * // If `excludeIndexes` is `true`, Mongoose will skip building an index
30
+ * // on `arr.name`. Otherwise, Mongoose will build an index on `arr.name`.
31
+ * const parentSchema = Schema({
32
+ * arr: { type: [childSchema], excludeIndexes: true }
33
+ * });
34
+ *
35
+ * @api public
36
+ * @property excludeIndexes
37
+ * @memberOf SchemaDocumentArrayOptions
38
+ * @type Array
39
+ * @instance
40
+ */
41
+
42
+ Object.defineProperty(SchemaDocumentArrayOptions.prototype, 'excludeIndexes', opts);
43
+
44
+ /*!
45
+ * ignore
46
+ */
47
+
48
+ module.exports = SchemaDocumentArrayOptions;
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ const SchemaTypeOptions = require('./SchemaTypeOptions');
4
+
5
+ /**
6
+ * The options defined on a Map schematype.
7
+ *
8
+ * ####Example:
9
+ *
10
+ * const schema = new Schema({ socialMediaHandles: { type: Map, of: String } });
11
+ * schema.path('socialMediaHandles').options; // SchemaMapOptions instance
12
+ *
13
+ * @api public
14
+ * @inherits SchemaTypeOptions
15
+ * @constructor SchemaMapOptions
16
+ */
17
+
18
+ class SchemaMapOptions extends SchemaTypeOptions {}
19
+
20
+ const opts = require('./propertyOptions');
21
+
22
+ /**
23
+ * If set, specifies the type of this map's values. Mongoose will cast
24
+ * this map's values to the given type.
25
+ *
26
+ * If not set, Mongoose will not cast the map's values.
27
+ *
28
+ * ####Example:
29
+ *
30
+ * // Mongoose will cast `socialMediaHandles` values to strings
31
+ * const schema = new Schema({ socialMediaHandles: { type: Map, of: String } });
32
+ * schema.path('socialMediaHandles').options.of; // String
33
+ *
34
+ * @api public
35
+ * @property of
36
+ * @memberOf SchemaMapOptions
37
+ * @type Function|string
38
+ * @instance
39
+ */
40
+
41
+ Object.defineProperty(SchemaMapOptions.prototype, 'of', opts);
42
+
43
+ module.exports = SchemaMapOptions;
@@ -17,12 +17,7 @@ const SchemaTypeOptions = require('./SchemaTypeOptions');
17
17
 
18
18
  class SchemaNumberOptions extends SchemaTypeOptions {}
19
19
 
20
- const opts = {
21
- enumerable: true,
22
- configurable: true,
23
- writable: true,
24
- value: null
25
- };
20
+ const opts = require('./propertyOptions');
26
21
 
27
22
  /**
28
23
  * If set, Mongoose adds a validator that checks that this path is at least the
@@ -17,12 +17,7 @@ const SchemaTypeOptions = require('./SchemaTypeOptions');
17
17
 
18
18
  class SchemaObjectIdOptions extends SchemaTypeOptions {}
19
19
 
20
- const opts = {
21
- enumerable: true,
22
- configurable: true,
23
- writable: true,
24
- value: null
25
- };
20
+ const opts = require('./propertyOptions');
26
21
 
27
22
  /**
28
23
  * If truthy, uses Mongoose's default built-in ObjectId path.
@@ -17,12 +17,7 @@ const SchemaTypeOptions = require('./SchemaTypeOptions');
17
17
 
18
18
  class SchemaStringOptions extends SchemaTypeOptions {}
19
19
 
20
- const opts = {
21
- enumerable: true,
22
- configurable: true,
23
- writable: true,
24
- value: null
25
- };
20
+ const opts = require('./propertyOptions');
26
21
 
27
22
  /**
28
23
  * Array of allowed values for this path
@@ -23,12 +23,7 @@ class SchemaTypeOptions {
23
23
  }
24
24
  }
25
25
 
26
- const opts = {
27
- enumerable: true,
28
- configurable: true,
29
- writable: true,
30
- value: null
31
- };
26
+ const opts = require('./propertyOptions');
32
27
 
33
28
  /**
34
29
  * The type to cast this path to.
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ module.exports = Object.freeze({
4
+ enumerable: true,
5
+ configurable: true,
6
+ writable: true,
7
+ value: void 0
8
+ });
package/lib/query.js CHANGED
@@ -18,6 +18,7 @@ const castArrayFilters = require('./helpers/update/castArrayFilters');
18
18
  const castUpdate = require('./helpers/query/castUpdate');
19
19
  const completeMany = require('./helpers/query/completeMany');
20
20
  const get = require('./helpers/get');
21
+ const getDiscriminatorByValue = require('./helpers/discriminator/getDiscriminatorByValue');
21
22
  const hasDollarKeys = require('./helpers/query/hasDollarKeys');
22
23
  const helpers = require('./queryhelpers');
23
24
  const isInclusive = require('./helpers/projection/isInclusive');
@@ -3862,7 +3863,6 @@ Query.prototype._replaceOne = wrapThunk(function(callback) {
3862
3863
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3863
3864
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3864
3865
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3865
- * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3866
3866
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3867
3867
  * @param {Function} [callback] params are (error, writeOpResult)
3868
3868
  * @return {Query} this
@@ -3928,7 +3928,6 @@ Query.prototype.update = function(conditions, doc, options, callback) {
3928
3928
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3929
3929
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3930
3930
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3931
- * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3932
3931
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3933
3932
  * @param {Function} [callback] params are (error, writeOpResult)
3934
3933
  * @return {Query} this
@@ -3995,7 +3994,6 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
3995
3994
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3996
3995
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3997
3996
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3998
- * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3999
3997
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
4000
3998
  * @param {Function} [callback] params are (error, writeOpResult)
4001
3999
  * @return {Query} this
@@ -4060,7 +4058,6 @@ Query.prototype.updateOne = function(conditions, doc, options, callback) {
4060
4058
  * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
4061
4059
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4062
4060
  * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
4063
- * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
4064
4061
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
4065
4062
  * @param {Function} [callback] params are (error, writeOpResult)
4066
4063
  * @return {Query} this
@@ -4433,7 +4430,7 @@ Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
4433
4430
  typeof filter[schema.options.discriminatorKey] !== 'object' &&
4434
4431
  schema.discriminators != null) {
4435
4432
  const discriminatorValue = filter[schema.options.discriminatorKey];
4436
- const byValue = helpers.getDiscriminatorByValue(this.model, discriminatorValue);
4433
+ const byValue = getDiscriminatorByValue(this.model, discriminatorValue);
4437
4434
  schema = schema.discriminators[discriminatorValue] ||
4438
4435
  (byValue && byValue.schema) ||
4439
4436
  schema;
@@ -7,6 +7,8 @@
7
7
  const checkEmbeddedDiscriminatorKeyProjection =
8
8
  require('./helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection');
9
9
  const get = require('./helpers/get');
10
+ const getDiscriminatorByValue =
11
+ require('./helpers/discriminator/getDiscriminatorByValue');
10
12
  const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
11
13
  const utils = require('./utils');
12
14
 
@@ -71,34 +73,6 @@ exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query,
71
73
  return pop;
72
74
  };
73
75
 
74
-
75
- /*!
76
- * returns discriminator by discriminatorMapping.value
77
- *
78
- * @param {Model} model
79
- * @param {string} value
80
- */
81
- function getDiscriminatorByValue(model, value) {
82
- let discriminator = null;
83
- if (!model.discriminators) {
84
- return discriminator;
85
- }
86
- for (const name in model.discriminators) {
87
- const it = model.discriminators[name];
88
- if (
89
- it.schema &&
90
- it.schema.discriminatorMapping &&
91
- it.schema.discriminatorMapping.value == value
92
- ) {
93
- discriminator = it;
94
- break;
95
- }
96
- }
97
- return discriminator;
98
- }
99
-
100
- exports.getDiscriminatorByValue = getDiscriminatorByValue;
101
-
102
76
  /*!
103
77
  * If the document is a mapped discriminator type, it returns a model instance for that type, otherwise,
104
78
  * it returns an instance of the given model.
@@ -17,7 +17,7 @@ const util = require('util');
17
17
  const utils = require('../utils');
18
18
  const castToNumber = require('./operators/helpers').castToNumber;
19
19
  const geospatial = require('./operators/geospatial');
20
- const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
20
+ const getDiscriminatorByValue = require('../helpers/discriminator/getDiscriminatorByValue');
21
21
 
22
22
  let MongooseArray;
23
23
  let EmbeddedDoc;
@@ -7,6 +7,8 @@
7
7
  const ArrayType = require('./array');
8
8
  const CastError = require('../error/cast');
9
9
  const EventEmitter = require('events').EventEmitter;
10
+ const SchemaDocumentArrayOptions =
11
+ require('../options/SchemaDocumentArrayOptions');
10
12
  const SchemaType = require('../schematype');
11
13
  const discriminator = require('../helpers/model/discriminator');
12
14
  const get = require('../helpers/get');
@@ -91,6 +93,7 @@ DocumentArrayPath.options = { castNonArrays: true };
91
93
  */
92
94
  DocumentArrayPath.prototype = Object.create(ArrayType.prototype);
93
95
  DocumentArrayPath.prototype.constructor = DocumentArrayPath;
96
+ DocumentArrayPath.prototype.OptionsConstructor = SchemaDocumentArrayOptions;
94
97
 
95
98
  /*!
96
99
  * Ignore
package/lib/schema/map.js CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  const MongooseMap = require('../types/map');
8
+ const SchemaMapOptions = require('../options/SchemaMapOptions');
8
9
  const SchemaType = require('../schematype');
9
10
 
10
11
  /*!
@@ -42,4 +43,6 @@ class Map extends SchemaType {
42
43
  }
43
44
  }
44
45
 
46
+ Map.prototype.OptionsConstructor = SchemaMapOptions;
47
+
45
48
  module.exports = Map;
package/lib/schema.js CHANGED
@@ -7,6 +7,7 @@
7
7
  const EventEmitter = require('events').EventEmitter;
8
8
  const Kareem = require('kareem');
9
9
  const SchemaType = require('./schematype');
10
+ const SchemaTypeOptions = require('./options/SchemaTypeOptions');
10
11
  const VirtualType = require('./virtualtype');
11
12
  const applyTimestampsToChildren = require('./helpers/update/applyTimestampsToChildren');
12
13
  const applyTimestampsToUpdate = require('./helpers/update/applyTimestampsToUpdate');
@@ -71,6 +72,9 @@ let id = 0;
71
72
  * - [timestamps](/docs/guide.html#timestamps): object or boolean - defaults to `false`. If true, Mongoose adds `createdAt` and `updatedAt` properties to your schema and manages those properties for you.
72
73
  * - [storeSubdocValidationError](/docs/guide.html#storeSubdocValidationError): boolean - Defaults to true. If false, Mongoose will wrap validation errors in single nested document subpaths into a single validation error on the single nested subdoc's path.
73
74
  *
75
+ * ####Options for Nested Schemas:
76
+ * - `excludeIndexes`: bool - defaults to `false`. If `true`, skip building indexes on this schema's paths.
77
+ *
74
78
  * ####Note:
75
79
  *
76
80
  * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into its parent._
@@ -234,15 +238,17 @@ Object.defineProperty(Schema.prototype, 'childSchemas', {
234
238
  Schema.prototype.obj;
235
239
 
236
240
  /**
237
- * Schema as flat paths
241
+ * The paths defined on this schema. The keys are the top-level paths
242
+ * in this schema, and the values are instances of the SchemaType class.
238
243
  *
239
244
  * ####Example:
240
- * {
241
- * '_id' : SchemaType,
242
- * , 'nested.key' : SchemaType,
243
- * }
245
+ * const schema = new Schema({ name: String }, { _id: false });
246
+ * schema.paths; // { name: SchemaString { ... } }
244
247
  *
245
- * @api private
248
+ * schema.add({ age: Number });
249
+ * schema.paths; // { name: SchemaString { ... }, age: SchemaNumber { ... } }
250
+ *
251
+ * @api public
246
252
  * @property paths
247
253
  * @memberOf Schema
248
254
  * @instance
@@ -426,7 +432,7 @@ Schema.prototype.add = function add(obj, prefix) {
426
432
  '`, got value "' + obj[key][0] + '"');
427
433
  }
428
434
 
429
- if (utils.isPOJO(obj[key]) &&
435
+ if ((utils.isPOJO(obj[key]) || obj[key] instanceof SchemaTypeOptions) &&
430
436
  (!obj[key][this.options.typeKey] || (this.options.typeKey === 'type' && obj[key].type.type))) {
431
437
  if (Object.keys(obj[key]).length) {
432
438
  // nested object { last: { name: String }}
@@ -795,7 +801,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
795
801
  // copy of SchemaTypes re: gh-7158 gh-6933
796
802
  const MongooseTypes = this.base != null ? this.base.Schema.Types : Schema.Types;
797
803
 
798
- if (obj.constructor) {
804
+ if (!utils.isPOJO(obj) && !(obj instanceof SchemaTypeOptions)) {
799
805
  const constructorName = utils.getFunctionName(obj.constructor);
800
806
  if (constructorName !== 'Object') {
801
807
  const oldObj = obj;
@@ -8,7 +8,7 @@ const CoreMongooseArray = require('./core_array');
8
8
  const Document = require('../document');
9
9
  const ObjectId = require('./objectid');
10
10
  const castObjectId = require('../cast/objectid');
11
- const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
11
+ const getDiscriminatorByValue = require('../helpers/discriminator/getDiscriminatorByValue');
12
12
  const internalToObjectOptions = require('../options').internalToObjectOptions;
13
13
  const util = require('util');
14
14
  const utils = require('../utils');
@@ -172,6 +172,14 @@ class CoreDocumentArray extends CoreMongooseArray {
172
172
  }));
173
173
  }
174
174
 
175
+ slice() {
176
+ const arr = super.slice.apply(this, arguments);
177
+ arr[arrayParentSymbol] = this[arrayParentSymbol];
178
+ arr[arrayPathSymbol] = this[arrayPathSymbol];
179
+
180
+ return arr;
181
+ }
182
+
175
183
  /**
176
184
  * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
177
185
  *
@@ -320,18 +328,18 @@ if (util.inspect.custom) {
320
328
 
321
329
  function _updateParentPopulated(arr) {
322
330
  const parent = arr[arrayParentSymbol];
323
- if (parent.$__.populated != null) {
324
- const populatedPaths = Object.keys(parent.$__.populated).
325
- filter(p => p.startsWith(arr[arrayPathSymbol] + '.'));
331
+ if (!parent || parent.$__.populated == null) return;
326
332
 
327
- for (const path of populatedPaths) {
328
- const remnant = path.slice((arr[arrayPathSymbol] + '.').length);
329
- if (!Array.isArray(parent.$__.populated[path].value)) {
330
- continue;
331
- }
333
+ const populatedPaths = Object.keys(parent.$__.populated).
334
+ filter(p => p.startsWith(arr[arrayPathSymbol] + '.'));
332
335
 
333
- parent.$__.populated[path].value = arr.map(val => val.populated(remnant));
336
+ for (const path of populatedPaths) {
337
+ const remnant = path.slice((arr[arrayPathSymbol] + '.').length);
338
+ if (!Array.isArray(parent.$__.populated[path].value)) {
339
+ continue;
334
340
  }
341
+
342
+ parent.$__.populated[path].value = arr.map(val => val.populated(remnant));
335
343
  }
336
344
  }
337
345
 
package/lib/utils.js CHANGED
@@ -193,6 +193,11 @@ exports.clone = function clone(obj, options, isArrayChild) {
193
193
  }
194
194
 
195
195
  if (isMongooseObject(obj)) {
196
+ // Single nested subdocs should apply getters later in `applyGetters()`
197
+ // when calling `toObject()`. See gh-7442, gh-8295
198
+ if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
199
+ options = Object.assign({}, options, { getters: false });
200
+ }
196
201
  if (options && options.json && typeof obj.toJSON === 'function') {
197
202
  return obj.toJSON(options);
198
203
  }
@@ -232,6 +237,13 @@ exports.clone = function clone(obj, options, isArrayChild) {
232
237
  return obj.clone();
233
238
  }
234
239
 
240
+ // If we're cloning this object to go into a MongoDB command,
241
+ // and there's a `toBSON()` function, assume this object will be
242
+ // stored as a primitive in MongoDB and doesn't need to be cloned.
243
+ if (options && options.bson && typeof obj.toBSON === 'function') {
244
+ return obj;
245
+ }
246
+
235
247
  if (obj.valueOf != null) {
236
248
  return obj.valueOf();
237
249
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "5.7.8",
4
+ "version": "5.7.12",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "bson": "~1.1.1",
23
23
  "kareem": "2.3.1",
24
- "mongodb": "3.3.3",
24
+ "mongodb": "3.3.4",
25
25
  "mongoose-legacy-pluralize": "1.0.2",
26
26
  "mpath": "0.6.0",
27
27
  "mquery": "3.2.2",
@@ -73,7 +73,7 @@
73
73
  "lint": "eslint .",
74
74
  "release": "git pull && git push origin master --tags && npm publish",
75
75
  "release-legacy": "git pull origin 4.x && git push origin 4.x --tags && npm publish --tag legacy",
76
- "test": "mocha --exit test/*.test.js test/**/*.test.js",
76
+ "test": "mocha --exit",
77
77
  "test-cov": "nyc --reporter=html --reporter=text npm test"
78
78
  },
79
79
  "main": "./index.js",
@@ -89,6 +89,14 @@
89
89
  },
90
90
  "homepage": "https://mongoosejs.com",
91
91
  "browser": "./browser.js",
92
+ "mocha": {
93
+ "extension": [
94
+ "test.js"
95
+ ],
96
+ "watch-files": [
97
+ "test/**/*.js"
98
+ ]
99
+ },
92
100
  "eslintConfig": {
93
101
  "extends": [
94
102
  "eslint:recommended"