mongoose 6.2.1 → 6.2.4

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 (65) hide show
  1. package/.eslintrc.json +5 -1
  2. package/.lgtm.yml +3 -0
  3. package/CHANGELOG.md +54 -0
  4. package/dist/browser.umd.js +156 -152
  5. package/index.js +5 -1
  6. package/lib/aggregate.js +22 -27
  7. package/lib/browserDocument.js +1 -1
  8. package/lib/cast/number.js +2 -3
  9. package/lib/cast.js +7 -4
  10. package/lib/connection.js +43 -21
  11. package/lib/cursor/AggregationCursor.js +12 -7
  12. package/lib/cursor/QueryCursor.js +11 -6
  13. package/lib/document.js +58 -72
  14. package/lib/drivers/node-mongodb-native/collection.js +12 -4
  15. package/lib/drivers/node-mongodb-native/connection.js +11 -0
  16. package/lib/error/cast.js +3 -2
  17. package/lib/helpers/clone.js +11 -2
  18. package/lib/helpers/cursor/eachAsync.js +18 -15
  19. package/lib/helpers/document/cleanModifiedSubpaths.js +1 -0
  20. package/lib/helpers/document/compile.js +7 -4
  21. package/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js +14 -0
  22. package/lib/helpers/indexes/getRelatedIndexes.js +59 -0
  23. package/lib/helpers/isAsyncFunction.js +6 -7
  24. package/lib/helpers/populate/assignVals.js +4 -0
  25. package/lib/helpers/printJestWarning.js +2 -2
  26. package/lib/helpers/projection/applyProjection.js +77 -0
  27. package/lib/helpers/projection/hasIncludedChildren.js +36 -0
  28. package/lib/helpers/projection/isExclusive.js +5 -2
  29. package/lib/helpers/projection/isInclusive.js +5 -1
  30. package/lib/helpers/query/cast$expr.js +14 -19
  31. package/lib/helpers/query/hasDollarKeys.js +7 -3
  32. package/lib/helpers/query/isOperator.js +5 -2
  33. package/lib/helpers/schema/getIndexes.js +6 -2
  34. package/lib/index.js +14 -17
  35. package/lib/internal.js +9 -1
  36. package/lib/model.js +159 -153
  37. package/lib/options/SchemaTypeOptions.js +1 -1
  38. package/lib/plugins/trackTransaction.js +1 -1
  39. package/lib/query.js +159 -147
  40. package/lib/queryhelpers.js +8 -28
  41. package/lib/schema/SubdocumentPath.js +5 -4
  42. package/lib/schema/array.js +13 -6
  43. package/lib/schema/buffer.js +1 -1
  44. package/lib/schema/date.js +1 -1
  45. package/lib/schema/decimal128.js +1 -1
  46. package/lib/schema/documentarray.js +9 -7
  47. package/lib/schema/number.js +1 -1
  48. package/lib/schema/objectid.js +1 -1
  49. package/lib/schema/string.js +4 -4
  50. package/lib/schema.js +12 -8
  51. package/lib/schematype.js +12 -14
  52. package/lib/types/ArraySubdocument.js +1 -1
  53. package/lib/types/DocumentArray/index.js +1 -1
  54. package/lib/types/array/index.js +2 -2
  55. package/lib/types/array/methods/index.js +10 -11
  56. package/lib/types/buffer.js +3 -3
  57. package/lib/types/map.js +3 -4
  58. package/lib/utils.js +9 -3
  59. package/package.json +17 -21
  60. package/tsconfig.json +0 -2
  61. package/types/Connection.d.ts +212 -0
  62. package/types/Error.d.ts +129 -0
  63. package/types/PipelineStage.d.ts +272 -0
  64. package/types/index.d.ts +61 -602
  65. package/lib/types/array/ArrayWrapper.js +0 -981
package/lib/document.js CHANGED
@@ -16,6 +16,7 @@ const StrictModeError = require('./error/strict');
16
16
  const ValidationError = require('./error/validation');
17
17
  const ValidatorError = require('./error/validator');
18
18
  const VirtualType = require('./virtualtype');
19
+ const $__hasIncludedChildren = require('./helpers/projection/hasIncludedChildren');
19
20
  const promiseOrCallback = require('./helpers/promiseOrCallback');
20
21
  const cleanModifiedSubpaths = require('./helpers/document/cleanModifiedSubpaths');
21
22
  const compile = require('./helpers/document/compile').compile;
@@ -66,7 +67,7 @@ const specialProperties = utils.specialProperties;
66
67
  * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
67
68
  * @param {Object} [options] various configuration options for the document
68
69
  * @param {Boolean} [options.defaults=true] if `false`, skip applying default values to this document.
69
- * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
70
+ * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#events_class_events_eventemitter
70
71
  * @event `init`: Emitted on a document after it has been retrieved from the db and fully hydrated by Mongoose.
71
72
  * @event `save`: Emitted when the document is successfully saved
72
73
  * @api private
@@ -96,6 +97,7 @@ function Document(obj, fields, skipId, options) {
96
97
  if ('priorDoc' in options) {
97
98
  this.$__.priorDoc = options.priorDoc;
98
99
  }
100
+
99
101
  if (skipId) {
100
102
  this.$__.skipId = skipId;
101
103
  }
@@ -140,7 +142,7 @@ function Document(obj, fields, skipId, options) {
140
142
 
141
143
  const hasIncludedChildren = exclude === false && fields ?
142
144
  $__hasIncludedChildren(fields) :
143
- {};
145
+ null;
144
146
 
145
147
  if (this._doc == null) {
146
148
  this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren, false);
@@ -148,9 +150,7 @@ function Document(obj, fields, skipId, options) {
148
150
  // By default, defaults get applied **before** setting initial values
149
151
  // Re: gh-6155
150
152
  if (defaults) {
151
- $__applyDefaults(this, fields, exclude, hasIncludedChildren, true, {
152
- isNew: this.$isNew
153
- });
153
+ $__applyDefaults(this, fields, exclude, hasIncludedChildren, true, null);
154
154
  }
155
155
  }
156
156
  if (obj) {
@@ -174,9 +174,7 @@ function Document(obj, fields, skipId, options) {
174
174
  this.$__.skipDefaults = options.skipDefaults;
175
175
  }
176
176
  } else if (defaults) {
177
- $__applyDefaults(this, fields, exclude, hasIncludedChildren, false, options.skipDefaults, {
178
- isNew: this.$isNew
179
- });
177
+ $__applyDefaults(this, fields, exclude, hasIncludedChildren, false, options.skipDefaults);
180
178
  }
181
179
 
182
180
  this.$__._id = this._id;
@@ -417,33 +415,6 @@ Object.defineProperty(Document.prototype, '$op', {
417
415
  }
418
416
  });
419
417
 
420
- /*!
421
- * ignore
422
- */
423
-
424
- function $__hasIncludedChildren(fields) {
425
- const hasIncludedChildren = {};
426
- const keys = Object.keys(fields);
427
-
428
- for (const key of keys) {
429
- if (key.indexOf('.') === -1) {
430
- hasIncludedChildren[key] = 1;
431
- continue;
432
- }
433
- const parts = key.split('.');
434
- let c = parts[0];
435
-
436
- for (let i = 0; i < parts.length; ++i) {
437
- hasIncludedChildren[c] = 1;
438
- if (i + 1 < parts.length) {
439
- c = c + '.' + parts[i + 1];
440
- }
441
- }
442
- }
443
-
444
- return hasIncludedChildren;
445
- }
446
-
447
418
  /*!
448
419
  * ignore
449
420
  */
@@ -479,9 +450,10 @@ function $__applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSet
479
450
  break;
480
451
  }
481
452
  } else if (exclude === false && fields && !included) {
482
- if (curPath in fields || type.$isSingleNested && hasIncludedChildren[curPath]) {
453
+ const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
454
+ if (curPath in fields || (hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) {
483
455
  included = true;
484
- } else if (!hasIncludedChildren[curPath]) {
456
+ } else if (hasIncludedChildren != null && !hasIncludedChildren[curPath]) {
485
457
  break;
486
458
  }
487
459
  }
@@ -775,7 +747,7 @@ Document.prototype.$__init = function(doc, opts) {
775
747
 
776
748
  const hasIncludedChildren = this.$__.exclude === false && this.$__.fields ?
777
749
  $__hasIncludedChildren(this.$__.fields) :
778
- {};
750
+ null;
779
751
  $__applyDefaults(this, this.$__.fields, this.$__.exclude, hasIncludedChildren, false, this.$__.skipDefaults);
780
752
 
781
753
  return this;
@@ -914,9 +886,9 @@ Document.prototype.update = function update() {
914
886
  *
915
887
  * @see Model.updateOne #model_Model.updateOne
916
888
  * @param {Object} doc
917
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
889
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api.html#query_Query-setOptions)
918
890
  * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](/docs/api.html#query_Query-lean) and the [Mongoose lean tutorial](/docs/tutorials/lean.html).
919
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
891
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
920
892
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
921
893
  * @param {Function} callback
922
894
  * @return {Query}
@@ -1119,7 +1091,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1119
1091
 
1120
1092
  // `_skipMinimizeTopLevel` is because we may have deleted the top-level
1121
1093
  // nested key to ensure key order.
1122
- const _skipMinimizeTopLevel = get(options, '_skipMinimizeTopLevel', false);
1094
+ const _skipMinimizeTopLevel = options._skipMinimizeTopLevel || false;
1123
1095
  if (len === 0 && _skipMinimizeTopLevel) {
1124
1096
  delete options._skipMinimizeTopLevel;
1125
1097
  if (val) {
@@ -1252,7 +1224,6 @@ Document.prototype.$set = function $set(path, val, type, options) {
1252
1224
  } else {
1253
1225
  this.markModified(path);
1254
1226
  }
1255
- cleanModifiedSubpaths(this, path, { skipDocArrays: true });
1256
1227
  return this;
1257
1228
  }
1258
1229
  this.invalidate(path, new MongooseError.CastError('Object', val, path));
@@ -1263,7 +1234,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1263
1234
  const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
1264
1235
 
1265
1236
  // Might need to change path for top-level alias
1266
- if (typeof this.$__schema.aliases[parts[0]] == 'string') {
1237
+ if (typeof this.$__schema.aliases[parts[0]] === 'string') {
1267
1238
  parts[0] = this.$__schema.aliases[parts[0]];
1268
1239
  }
1269
1240
 
@@ -1407,8 +1378,9 @@ Document.prototype.$set = function $set(path, val, type, options) {
1407
1378
 
1408
1379
  let didPopulate = false;
1409
1380
  if (refMatches && val instanceof Document) {
1410
- this.$populated(path, val._id, { [populateModelSymbol]: val.constructor });
1411
- val.$__.wasPopulated = true;
1381
+ const unpopulatedValue = (schema && schema.$isSingleNested) ? schema.cast(val, this) : val._id;
1382
+ this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor });
1383
+ val.$__.wasPopulated = { value: unpopulatedValue };
1412
1384
  didPopulate = true;
1413
1385
  }
1414
1386
 
@@ -1422,7 +1394,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1422
1394
  this.$populated(path, val.map(function(v) { return v._id; }), popOpts);
1423
1395
 
1424
1396
  for (const doc of val) {
1425
- doc.$__.wasPopulated = true;
1397
+ doc.$__.wasPopulated = { value: doc._id };
1426
1398
  }
1427
1399
  didPopulate = true;
1428
1400
  }
@@ -1787,7 +1759,7 @@ Document.prototype.get = function(path, type, options) {
1787
1759
  }
1788
1760
 
1789
1761
  // Might need to change path for top-level alias
1790
- if (typeof this.$__schema.aliases[pieces[0]] == 'string') {
1762
+ if (typeof this.$__schema.aliases[pieces[0]] === 'string') {
1791
1763
  pieces[0] = this.$__schema.aliases[pieces[0]];
1792
1764
  }
1793
1765
 
@@ -2539,7 +2511,9 @@ function _getPathsToValidate(doc) {
2539
2511
  // To avoid potential performance issues, skip doc arrays whose children
2540
2512
  // are not required. `getPositionalPathType()` may be slow, so avoid
2541
2513
  // it unless we have a case of #6364
2542
- (!Array.isArray(_pathType) && _pathType.$isMongooseDocumentArray && !get(_pathType, 'schemaOptions.required'))) {
2514
+ (!Array.isArray(_pathType) &&
2515
+ _pathType.$isMongooseDocumentArray &&
2516
+ !(_pathType && _pathType.schemaOptions && _pathType.schemaOptions.required))) {
2543
2517
  continue;
2544
2518
  }
2545
2519
 
@@ -2616,7 +2590,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2616
2590
  (typeof options === 'object') &&
2617
2591
  ('validateModifiedOnly' in options);
2618
2592
 
2619
- const pathsToSkip = get(options, 'pathsToSkip', null);
2593
+ const pathsToSkip = (options && options.pathsToSkip) || null;
2620
2594
 
2621
2595
  let shouldValidateModifiedOnly;
2622
2596
  if (hasValidateModifiedOnlyOption) {
@@ -2877,19 +2851,21 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
2877
2851
  }
2878
2852
  const validating = {};
2879
2853
 
2880
- paths.forEach(function(path) {
2854
+ for (let i = 0, len = paths.length; i < len; ++i) {
2855
+ const path = paths[i];
2856
+
2881
2857
  if (validating[path]) {
2882
- return;
2858
+ continue;
2883
2859
  }
2884
2860
 
2885
2861
  validating[path] = true;
2886
2862
 
2887
2863
  const p = _this.$__schema.path(path);
2888
2864
  if (!p) {
2889
- return;
2865
+ continue;
2890
2866
  }
2891
2867
  if (!_this.$isValid(path)) {
2892
- return;
2868
+ continue;
2893
2869
  }
2894
2870
 
2895
2871
  const val = _this.$__getValue(path);
@@ -2903,11 +2879,11 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
2903
2879
  p.$isArraySubdocument ||
2904
2880
  p.$isMongooseDocumentArray;
2905
2881
  if (isSubdoc && err instanceof ValidationError) {
2906
- return;
2882
+ continue;
2907
2883
  }
2908
2884
  _this.invalidate(path, err, undefined, true);
2909
2885
  }
2910
- });
2886
+ }
2911
2887
 
2912
2888
  const err = _this.$__.validationError;
2913
2889
  _this.$__.validationError = undefined;
@@ -3066,7 +3042,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3066
3042
  *
3067
3043
  * @param {Object} [options] options optional options
3068
3044
  * @param {Session} [options.session=null] the [session](https://docs.mongodb.com/manual/reference/server-sessions/) associated with this save operation. If not specified, defaults to the [document's associated session](api.html#document_Document-$session).
3069
- * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
3045
+ * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
3070
3046
  * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
3071
3047
  * @param {Boolean} [options.validateModifiedOnly=false] If `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
3072
3048
  * @param {Number|String} [options.w] set the [write concern](https://docs.mongodb.com/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern)
@@ -3081,7 +3057,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3081
3057
  * @throws {DocumentNotFoundError} if this [save updates an existing document](api.html#document_Document-isNew) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating).
3082
3058
  * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
3083
3059
  * @api public
3084
- * @see middleware http://mongoosejs.com/docs/middleware.html
3060
+ * @see middleware https://mongoosejs.com/docs/middleware.html
3085
3061
  */
3086
3062
 
3087
3063
  /**
@@ -3131,7 +3107,7 @@ Document.prototype.$__reset = function reset() {
3131
3107
  return _this.$__getValue(i);
3132
3108
  })
3133
3109
  .filter(function(val) {
3134
- return val && val instanceof Array && utils.isMongooseDocumentArray(val) && val.length;
3110
+ return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length;
3135
3111
  })
3136
3112
  .forEach(function(array) {
3137
3113
  let i = array.length;
@@ -3334,7 +3310,7 @@ Document.prototype.$__getArrayPathsToValidate = function() {
3334
3310
  return this.$__getValue(i);
3335
3311
  }.bind(this))
3336
3312
  .filter(function(val) {
3337
- return val && val instanceof Array && utils.isMongooseDocumentArray(val) && val.length;
3313
+ return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length;
3338
3314
  }).reduce(function(seed, array) {
3339
3315
  return seed.concat(array);
3340
3316
  }, [])
@@ -3455,8 +3431,11 @@ Document.prototype.$toObject = function(options, json) {
3455
3431
  };
3456
3432
 
3457
3433
  const path = json ? 'toJSON' : 'toObject';
3458
- const baseOptions = get(this, 'constructor.base.options.' + path, {});
3459
- const schemaOptions = get(this, '$__schema.options', {});
3434
+ const baseOptions = this.constructor &&
3435
+ this.constructor.base &&
3436
+ this.constructor.base.options &&
3437
+ get(this.constructor.base.options, path) || {};
3438
+ const schemaOptions = this.$__schema && this.$__schema.options || {};
3460
3439
  // merge base default options with Schema's set default options if available.
3461
3440
  // `clone` is necessary here because `utils.options` directly modifies the second input.
3462
3441
  defaultOptions = utils.options(defaultOptions, clone(baseOptions));
@@ -3503,12 +3482,11 @@ Document.prototype.$toObject = function(options, json) {
3503
3482
  }
3504
3483
 
3505
3484
  const depopulate = options.depopulate ||
3506
- get(options, '_parentOptions.depopulate', false);
3485
+ (options._parentOptions && options._parentOptions.depopulate || false);
3507
3486
  // _isNested will only be true if this is not the top level document, we
3508
- // should never depopulate
3487
+ // should never depopulate the top-level document
3509
3488
  if (depopulate && options._isNested && this.$__.wasPopulated) {
3510
- // populated paths that we set to a document
3511
- return clone(this._id, cloneOptions);
3489
+ return clone(this.$__.wasPopulated.value || this._id, cloneOptions);
3512
3490
  }
3513
3491
 
3514
3492
  // merge default options with input options.
@@ -3583,7 +3561,7 @@ Document.prototype.$toObject = function(options, json) {
3583
3561
  /**
3584
3562
  * Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)).
3585
3563
  *
3586
- * Buffers are converted to instances of [mongodb.Binary](http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
3564
+ * Buffers are converted to instances of [mongodb.Binary](https://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
3587
3565
  *
3588
3566
  * ####Options:
3589
3567
  *
@@ -3722,7 +3700,7 @@ Document.prototype.$toObject = function(options, json) {
3722
3700
  * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
3723
3701
  * @param {Boolean} [options.useProjection=false] - If true, omits fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema.
3724
3702
  * @return {Object} js object
3725
- * @see mongodb.Binary http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
3703
+ * @see mongodb.Binary https://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
3726
3704
  * @api public
3727
3705
  * @memberOf Document
3728
3706
  * @instance
@@ -3780,7 +3758,9 @@ function applyVirtuals(self, json, options, toObjectOptions) {
3780
3758
  let assignPath;
3781
3759
  let cur = self._doc;
3782
3760
  let v;
3783
- const aliases = get(toObjectOptions, 'aliases', true);
3761
+ const aliases = typeof (toObjectOptions && toObjectOptions.aliases) === 'boolean'
3762
+ ? toObjectOptions.aliases
3763
+ : true;
3784
3764
 
3785
3765
  let virtualsToApply = null;
3786
3766
  if (Array.isArray(options.virtuals)) {
@@ -3974,15 +3954,20 @@ function omitDeselectedFields(self, json) {
3974
3954
  }
3975
3955
 
3976
3956
  /**
3977
- * The return value of this method is used in calls to JSON.stringify(doc).
3957
+ * The return value of this method is used in calls to [`JSON.stringify(doc)`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript#the-tojson-function).
3978
3958
  *
3979
3959
  * This method accepts the same options as [Document#toObject](#document_Document-toObject). To apply the options to every document of your schema by default, set your [schemas](#schema_Schema) `toJSON` option to the same argument.
3980
3960
  *
3981
- * schema.set('toJSON', { virtuals: true })
3961
+ * schema.set('toJSON', { virtuals: true });
3962
+ *
3963
+ * There is one difference between `toJSON()` and `toObject()` options.
3964
+ * When you call `toJSON()`, the [`flattenMaps` option](./document.html#document_Document-toObject) defaults to `true`, because `JSON.stringify()` doesn't convert maps to objects by default.
3965
+ * When you call `toObject()`, the `flattenMaps` option is `false` by default.
3982
3966
  *
3983
- * See [schema options](/docs/guide.html#toJSON) for details.
3967
+ * See [schema options](/docs/guide.html#toJSON) for more information on setting `toJSON` option defaults.
3984
3968
  *
3985
3969
  * @param {Object} options
3970
+ * @param {Boolean} [options.flattenMaps=true] if true, convert Maps to [POJOs](https://masteringjs.io/tutorials/fundamentals/pojo). Useful if you want to `JSON.stringify()` the result.
3986
3971
  * @return {Object}
3987
3972
  * @see Document#toObject #document_Document-toObject
3988
3973
  * @see JSON.stringify() in JavaScript https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html
@@ -4137,6 +4122,7 @@ Document.prototype.equals = function(doc) {
4137
4122
  * @param {Object} [match] Conditions for the population query
4138
4123
  * @param {Object} [options] Options for the population query (sort, etc)
4139
4124
  * @param {String} [options.path=null] The path to populate.
4125
+ * @param {string|PopulateOptions} [options.populate=null] Recursively populate paths in the populated documents. See [deep populate docs](/docs/populate.html#deep-populate).
4140
4126
  * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
4141
4127
  * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
4142
4128
  * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
@@ -4317,7 +4303,7 @@ Document.prototype.depopulate = function(path) {
4317
4303
 
4318
4304
  let populatedIds;
4319
4305
  const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : [];
4320
- const populated = get(this, '$__.populated', {});
4306
+ const populated = this.$__ && this.$__.populated || {};
4321
4307
 
4322
4308
  if (arguments.length === 0) {
4323
4309
  // Depopulate all
@@ -8,7 +8,6 @@ const MongooseCollection = require('../../collection');
8
8
  const MongooseError = require('../../error/mongooseError');
9
9
  const Collection = require('mongodb').Collection;
10
10
  const ObjectId = require('./objectid');
11
- const get = require('../../helpers/get');
12
11
  const getConstructorName = require('../../helpers/getConstructorName');
13
12
  const stream = require('stream');
14
13
  const util = require('util');
@@ -76,7 +75,11 @@ function iter(i) {
76
75
  const collection = this.collection;
77
76
  const args = Array.from(arguments);
78
77
  const _this = this;
79
- const debug = get(_this, 'conn.base.options.debug');
78
+ const debug = _this &&
79
+ _this.conn &&
80
+ _this.conn.base &&
81
+ _this.conn.base.options &&
82
+ _this.conn.base.options.debug;
80
83
  const lastArg = arguments[arguments.length - 1];
81
84
  const opId = new ObjectId();
82
85
 
@@ -84,7 +87,7 @@ function iter(i) {
84
87
  if (this.conn.$wasForceClosed) {
85
88
  const error = new MongooseError('Connection was force closed');
86
89
  if (args.length > 0 &&
87
- typeof args[args.length - 1] === 'function') {
90
+ typeof args[args.length - 1] === 'function') {
88
91
  args[args.length - 1](error);
89
92
  return;
90
93
  } else {
@@ -368,7 +371,12 @@ function format(obj, sub, color, shell) {
368
371
  formatDate(x, key, shell);
369
372
  } else if (_constructorName === 'ClientSession') {
370
373
  x[key] = inspectable('ClientSession("' +
371
- get(x[key], 'id.id.buffer', '').toString('hex') + '")');
374
+ (
375
+ x[key] &&
376
+ x[key].id &&
377
+ x[key].id.id &&
378
+ x[key].id.id.buffer || ''
379
+ ).toString('hex') + '")');
372
380
  } else if (Array.isArray(x[key])) {
373
381
  x[key] = x[key].map(map);
374
382
  } else if (error != null) {
@@ -137,6 +137,17 @@ NativeConnection.prototype.doClose = function(force, fn) {
137
137
  return this;
138
138
  }
139
139
 
140
+ let skipCloseClient = false;
141
+ if (force != null && typeof force === 'object') {
142
+ skipCloseClient = force.skipCloseClient;
143
+ force = force.force;
144
+ }
145
+
146
+ if (skipCloseClient) {
147
+ immediate(() => fn());
148
+ return this;
149
+ }
150
+
140
151
  this.client.close(force, (err, res) => {
141
152
  // Defer because the driver will wait at least 1ms before finishing closing
142
153
  // the pool, see https://github.com/mongodb-js/mongodb-core/blob/a8f8e4ce41936babc3b9112bf42d609779f03b39/lib/connection/pool.js#L1026-L1030.
package/lib/error/cast.js CHANGED
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
- const get = require('../helpers/get');
9
8
  const util = require('util');
10
9
 
11
10
  /**
@@ -111,7 +110,9 @@ function getValueType(value) {
111
110
  }
112
111
 
113
112
  function getMessageFormat(schemaType) {
114
- const messageFormat = get(schemaType, 'options.cast', null);
113
+ const messageFormat = schemaType &&
114
+ schemaType.options &&
115
+ schemaType.options.cast || null;
115
116
  if (typeof messageFormat === 'string') {
116
117
  return messageFormat;
117
118
  }
@@ -41,15 +41,24 @@ function clone(obj, options, isArrayChild) {
41
41
  if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
42
42
  options = Object.assign({}, options, { getters: false });
43
43
  }
44
+ const isSingleNested = obj.$isSingleNested;
44
45
 
45
46
  if (utils.isPOJO(obj) && obj.$__ != null && obj._doc != null) {
46
47
  return obj._doc;
47
48
  }
48
49
 
50
+ let ret;
49
51
  if (options && options.json && typeof obj.toJSON === 'function') {
50
- return obj.toJSON(options);
52
+ ret = obj.toJSON(options);
53
+ } else {
54
+ ret = obj.toObject(options);
51
55
  }
52
- return obj.toObject(options);
56
+
57
+ if (options && options.minimize && isSingleNested && Object.keys(ret).length === 0) {
58
+ return undefined;
59
+ }
60
+
61
+ return ret;
53
62
  }
54
63
 
55
64
  const objConstructor = obj.constructor;
@@ -27,16 +27,15 @@ module.exports = function eachAsync(next, fn, options, callback) {
27
27
  const enqueue = asyncQueue();
28
28
 
29
29
  return promiseOrCallback(callback, cb => {
30
+
30
31
  if (batchSize != null) {
31
32
  if (typeof batchSize !== 'number') {
32
33
  throw new TypeError('batchSize must be a number');
33
- }
34
- if (batchSize < 1) {
34
+ } else if (!Number.isInteger(batchSize)) {
35
+ throw new TypeError('batchSize must be an integer');
36
+ } else if (batchSize < 1) {
35
37
  throw new TypeError('batchSize must be at least 1');
36
38
  }
37
- if (batchSize !== Math.floor(batchSize)) {
38
- throw new TypeError('batchSize must be a positive integer');
39
- }
40
39
  }
41
40
 
42
41
  iterate(cb);
@@ -71,7 +70,7 @@ module.exports = function eachAsync(next, fn, options, callback) {
71
70
  drained = true;
72
71
  if (handleResultsInProgress <= 0) {
73
72
  finalCallback(null);
74
- } else if (batchSize != null && documentsBatch.length) {
73
+ } else if (batchSize && documentsBatch.length) {
75
74
  handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack);
76
75
  }
77
76
  return done();
@@ -83,20 +82,20 @@ module.exports = function eachAsync(next, fn, options, callback) {
83
82
  // make sure we know that we still have a result to handle re: #8422
84
83
  immediate(() => done());
85
84
 
86
- if (batchSize != null) {
85
+ if (batchSize) {
87
86
  documentsBatch.push(doc);
88
87
  }
89
88
 
90
89
  // If the current documents size is less than the provided patch size don't process the documents yet
91
- if (batchSize != null && documentsBatch.length !== batchSize) {
92
- setTimeout(() => enqueue(fetch), 0);
90
+ if (batchSize && documentsBatch.length !== batchSize) {
91
+ immediate(() => enqueue(fetch));
93
92
  return;
94
93
  }
95
94
 
96
- const docsToProcess = batchSize != null ? documentsBatch : doc;
95
+ const docsToProcess = batchSize ? documentsBatch : doc;
97
96
 
98
97
  function handleNextResultCallBack(err) {
99
- if (batchSize != null) {
98
+ if (batchSize) {
100
99
  handleResultsInProgress -= documentsBatch.length;
101
100
  documentsBatch = [];
102
101
  } else {
@@ -110,7 +109,7 @@ module.exports = function eachAsync(next, fn, options, callback) {
110
109
  return finalCallback(null);
111
110
  }
112
111
 
113
- setTimeout(() => enqueue(fetch), 0);
112
+ immediate(() => enqueue(fetch));
114
113
  }
115
114
 
116
115
  handleNextResult(docsToProcess, currentDocumentIndex++, handleNextResultCallBack);
@@ -139,7 +138,10 @@ function asyncQueue() {
139
138
  let id = 0;
140
139
 
141
140
  return function enqueue(fn) {
142
- if (_queue.length === 0 && inProgress == null) {
141
+ if (
142
+ inProgress === null &&
143
+ _queue.length === 0
144
+ ) {
143
145
  inProgress = id++;
144
146
  return fn(_step);
145
147
  }
@@ -147,11 +149,12 @@ function asyncQueue() {
147
149
  };
148
150
 
149
151
  function _step() {
150
- inProgress = null;
151
- if (_queue.length > 0) {
152
+ if (_queue.length !== 0) {
152
153
  inProgress = id++;
153
154
  const fn = _queue.shift();
154
155
  fn(_step);
156
+ } else {
157
+ inProgress = null;
155
158
  }
156
159
  }
157
160
  }
@@ -12,6 +12,7 @@ module.exports = function cleanModifiedSubpaths(doc, path, options) {
12
12
  if (!doc) {
13
13
  return deleted;
14
14
  }
15
+
15
16
  for (const modifiedPath of Object.keys(doc.$__.activePaths.states.modify)) {
16
17
  if (skipDocArrays) {
17
18
  const schemaType = doc.$__schema.path(modifiedPath);
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const documentSchemaSymbol = require('../../helpers/symbols').documentSchemaSymbol;
4
- const get = require('../../helpers/get');
5
4
  const internalToObjectOptions = require('../../options').internalToObjectOptions;
6
5
  const utils = require('../../utils');
7
6
 
@@ -93,7 +92,11 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
93
92
  writable: false,
94
93
  value: function() {
95
94
  return utils.clone(_this.get(path, null, {
96
- virtuals: get(this, 'schema.options.toObject.virtuals', null)
95
+ virtuals: this &&
96
+ this.schema &&
97
+ this.schema.options &&
98
+ this.schema.options.toObject &&
99
+ this.schema.options.toObject.virtuals || null
97
100
  }));
98
101
  }
99
102
  });
@@ -104,7 +107,7 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
104
107
  writable: false,
105
108
  value: function() {
106
109
  return _this.get(path, null, {
107
- virtuals: get(this, 'schema.options.toObject.virtuals', null)
110
+ virtuals: this && this.schema && this.schema.options && this.schema.options.toObject && this.schema.options.toObject.virtuals || null
108
111
  });
109
112
  }
110
113
  });
@@ -115,7 +118,7 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
115
118
  writable: false,
116
119
  value: function() {
117
120
  return _this.get(path, null, {
118
- virtuals: get(_this, 'schema.options.toJSON.virtuals', null)
121
+ virtuals: this && this.schema && this.schema.options && this.schema.options.toJSON && this.schema.options.toJSON.virtuals || null
119
122
  });
120
123
  }
121
124
  });
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ module.exports = function decorateDiscriminatorIndexOptions(schema, indexOptions) {
4
+ // If the model is a discriminator and has an index, add a
5
+ // partialFilterExpression by default so the index will only apply
6
+ // to that discriminator.
7
+ const discriminatorName = schema.discriminatorMapping && schema.discriminatorMapping.value;
8
+ if (discriminatorName && !('sparse' in indexOptions)) {
9
+ const discriminatorKey = schema.options.discriminatorKey;
10
+ indexOptions.partialFilterExpression = indexOptions.partialFilterExpression || {};
11
+ indexOptions.partialFilterExpression[discriminatorKey] = discriminatorName;
12
+ }
13
+ return indexOptions;
14
+ };