mongoose 8.20.0 → 9.0.0-rc1

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 (90) hide show
  1. package/eslint.config.mjs +198 -0
  2. package/lib/aggregate.js +17 -73
  3. package/lib/cast/bigint.js +1 -1
  4. package/lib/cast/double.js +1 -1
  5. package/lib/cast/uuid.js +5 -48
  6. package/lib/cast.js +2 -2
  7. package/lib/connection.js +0 -1
  8. package/lib/cursor/aggregationCursor.js +14 -24
  9. package/lib/cursor/queryCursor.js +7 -14
  10. package/lib/document.js +125 -121
  11. package/lib/drivers/node-mongodb-native/connection.js +3 -10
  12. package/lib/error/objectParameter.js +1 -2
  13. package/lib/error/validation.js +0 -8
  14. package/lib/helpers/clone.js +1 -1
  15. package/lib/helpers/common.js +1 -1
  16. package/lib/helpers/indexes/isIndexEqual.js +0 -1
  17. package/lib/helpers/model/applyDefaultsToPOJO.js +2 -2
  18. package/lib/helpers/model/applyHooks.js +43 -53
  19. package/lib/helpers/model/applyMethods.js +2 -2
  20. package/lib/helpers/model/applyStaticHooks.js +1 -48
  21. package/lib/helpers/model/castBulkWrite.js +1 -1
  22. package/lib/helpers/parallelLimit.js +18 -36
  23. package/lib/helpers/pluralize.js +3 -3
  24. package/lib/helpers/populate/assignRawDocsToIdStructure.js +1 -8
  25. package/lib/helpers/populate/createPopulateQueryFilter.js +1 -1
  26. package/lib/helpers/populate/getModelsMapForPopulate.js +17 -9
  27. package/lib/helpers/populate/getSchemaTypes.js +5 -5
  28. package/lib/helpers/query/cast$expr.js +8 -10
  29. package/lib/helpers/query/castFilterPath.js +1 -1
  30. package/lib/helpers/query/castUpdate.js +14 -12
  31. package/lib/helpers/query/getEmbeddedDiscriminatorPath.js +1 -1
  32. package/lib/helpers/schema/applyPlugins.js +1 -1
  33. package/lib/helpers/schema/getIndexes.js +1 -7
  34. package/lib/helpers/timestamps/setupTimestamps.js +3 -6
  35. package/lib/helpers/updateValidators.js +57 -111
  36. package/lib/model.js +419 -607
  37. package/lib/mongoose.js +41 -13
  38. package/lib/plugins/saveSubdocs.js +24 -51
  39. package/lib/plugins/sharding.js +5 -4
  40. package/lib/plugins/validateBeforeSave.js +3 -13
  41. package/lib/query.js +101 -145
  42. package/lib/queryHelpers.js +2 -2
  43. package/lib/schema/array.js +41 -84
  44. package/lib/schema/documentArray.js +57 -94
  45. package/lib/schema/documentArrayElement.js +16 -11
  46. package/lib/schema/string.js +1 -1
  47. package/lib/schema/subdocument.js +22 -28
  48. package/lib/schema/uuid.js +0 -21
  49. package/lib/schema.js +81 -39
  50. package/lib/schemaType.js +39 -57
  51. package/lib/types/array/index.js +2 -2
  52. package/lib/types/array/methods/index.js +4 -4
  53. package/lib/types/arraySubdocument.js +1 -1
  54. package/lib/types/buffer.js +10 -10
  55. package/lib/types/decimal128.js +1 -1
  56. package/lib/types/documentArray/index.js +1 -1
  57. package/lib/types/documentArray/methods/index.js +5 -3
  58. package/lib/types/double.js +1 -1
  59. package/lib/types/objectid.js +1 -1
  60. package/lib/types/subdocument.js +15 -43
  61. package/lib/types/uuid.js +1 -1
  62. package/lib/utils.js +1 -8
  63. package/lib/validOptions.js +3 -3
  64. package/package.json +11 -24
  65. package/types/connection.d.ts +20 -11
  66. package/types/document.d.ts +95 -26
  67. package/types/index.d.ts +143 -39
  68. package/types/inferhydrateddoctype.d.ts +115 -0
  69. package/types/inferrawdoctype.d.ts +99 -75
  70. package/types/inferschematype.d.ts +17 -3
  71. package/types/middlewares.d.ts +0 -2
  72. package/types/models.d.ts +131 -199
  73. package/types/mongooseoptions.d.ts +6 -5
  74. package/types/pipelinestage.d.ts +1 -1
  75. package/types/query.d.ts +71 -139
  76. package/types/schemaoptions.d.ts +1 -1
  77. package/types/schematypes.d.ts +14 -10
  78. package/types/types.d.ts +3 -4
  79. package/types/utility.d.ts +68 -48
  80. package/types/validation.d.ts +18 -14
  81. package/browser.js +0 -8
  82. package/dist/browser.umd.js +0 -2
  83. package/lib/browser.js +0 -141
  84. package/lib/browserDocument.js +0 -101
  85. package/lib/documentProvider.js +0 -30
  86. package/lib/drivers/browser/binary.js +0 -14
  87. package/lib/drivers/browser/decimal128.js +0 -7
  88. package/lib/drivers/browser/index.js +0 -13
  89. package/lib/drivers/browser/objectid.js +0 -29
  90. package/lib/helpers/promiseOrCallback.js +0 -54
package/lib/document.js CHANGED
@@ -30,7 +30,6 @@ const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDisc
30
30
  const getKeysInSchemaOrder = require('./helpers/schema/getKeysInSchemaOrder');
31
31
  const getSubdocumentStrictValue = require('./helpers/schema/getSubdocumentStrictValue');
32
32
  const handleSpreadDoc = require('./helpers/document/handleSpreadDoc');
33
- const immediate = require('./helpers/immediate');
34
33
  const isBsonType = require('./helpers/isBsonType');
35
34
  const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
36
35
  const isExclusive = require('./helpers/projection/isExclusive');
@@ -80,32 +79,33 @@ const VERSION_ALL = VERSION_WHERE | VERSION_INC;
80
79
  * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
81
80
  * @param {Object} [options] various configuration options for the document
82
81
  * @param {Boolean} [options.defaults=true] if `false`, skip applying default values to this document.
82
+ * @param {Boolean} [options.skipId=false] By default, Mongoose document if one is not provided and the document's schema does not override Mongoose's default `_id`. Set `skipId` to `true` to skip this generation step.
83
83
  * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#class-eventemitter
84
84
  * @event `init`: Emitted on a document after it has been retrieved from the db and fully hydrated by Mongoose.
85
85
  * @event `save`: Emitted when the document is successfully saved
86
86
  * @api private
87
87
  */
88
88
 
89
- function Document(obj, fields, skipId, options) {
90
- if (typeof skipId === 'object' && skipId != null) {
91
- options = skipId;
92
- skipId = options.skipId;
89
+ function Document(obj, fields, options) {
90
+ if (typeof options === 'boolean') {
91
+ throw new Error('The skipId parameter has been removed. Use { skipId: true } in the options parameter instead.');
93
92
  }
94
93
  options = Object.assign({}, options);
94
+ let skipId = options.skipId;
95
+
96
+ this.$__ = new InternalCache();
95
97
 
96
98
  // Support `browserDocument.js` syntax
97
99
  if (this.$__schema == null) {
98
100
  const _schema = utils.isObject(fields) && !fields.instanceOfSchema ?
99
101
  new Schema(fields) :
100
102
  fields;
103
+
101
104
  this.$__setSchema(_schema);
102
- fields = skipId;
103
- skipId = options;
104
- options = arguments[4] || {};
105
+ fields = options;
106
+ skipId = options.skipId;
105
107
  }
106
108
 
107
- this.$__ = new InternalCache();
108
-
109
109
  // Avoid setting `isNew` to `true`, because it is `true` by default
110
110
  if (options.isNew != null && options.isNew !== true) {
111
111
  this.$isNew = options.isNew;
@@ -733,6 +733,10 @@ Document.prototype.$__init = function(doc, opts) {
733
733
  function init(self, obj, doc, opts, prefix) {
734
734
  prefix = prefix || '';
735
735
 
736
+ if (typeof obj !== 'object' || Array.isArray(obj)) {
737
+ throw new ObjectExpectedError(self.$basePath, obj);
738
+ }
739
+
736
740
  if (obj.$__ != null) {
737
741
  obj = obj._doc;
738
742
  }
@@ -849,14 +853,25 @@ function init(self, obj, doc, opts, prefix) {
849
853
  * @instance
850
854
  */
851
855
 
852
- Document.prototype.updateOne = function updateOne(doc, options, callback) {
856
+ Document.prototype.updateOne = function updateOne(doc, options) {
853
857
  const query = this.constructor.updateOne({ _id: this._doc._id }, doc, options);
854
858
  const self = this;
855
- query.pre(function queryPreUpdateOne(cb) {
856
- self.constructor._middleware.execPre('updateOne', self, [self], cb);
859
+ query.pre(async function queryPreUpdateOne() {
860
+ const res = await self._execDocumentPreHooks('updateOne', self);
861
+ // `self` is passed to pre hooks as argument for backwards compatibility, but that
862
+ // isn't the actual arguments passed to the wrapped function.
863
+ if (res?.length !== 1 || res[0] !== self) {
864
+ throw new Error('Document updateOne pre hooks cannot overwrite arguments');
865
+ }
866
+ // Apply custom where conditions _after_ document updateOne middleware for
867
+ // consistency with save() - sharding plugin needs to set $where
868
+ if (self.$where != null) {
869
+ this.where(self.$where);
870
+ }
871
+ return res;
857
872
  });
858
- query.post(function queryPostUpdateOne(cb) {
859
- self.constructor._middleware.execPost('updateOne', self, [self], {}, cb);
873
+ query.post(function queryPostUpdateOne() {
874
+ return self._execDocumentPostHooks('updateOne');
860
875
  });
861
876
 
862
877
  if (this.$session() != null) {
@@ -865,10 +880,6 @@ Document.prototype.updateOne = function updateOne(doc, options, callback) {
865
880
  }
866
881
  }
867
882
 
868
- if (callback != null) {
869
- return query.exec(callback);
870
- }
871
-
872
883
  return query;
873
884
  };
874
885
 
@@ -2648,16 +2659,12 @@ Document.prototype.validate = async function validate(pathsToValidate, options)
2648
2659
  this.$__.validating = true;
2649
2660
  }
2650
2661
 
2651
- return new Promise((resolve, reject) => {
2652
- this.$__validate(pathsToValidate, options, (error) => {
2653
- this.$op = null;
2654
- this.$__.validating = null;
2655
- if (error != null) {
2656
- return reject(error);
2657
- }
2658
- resolve();
2659
- });
2660
- });
2662
+ try {
2663
+ await this.$__validate(pathsToValidate, options);
2664
+ } finally {
2665
+ this.$op = null;
2666
+ this.$__.validating = null;
2667
+ }
2661
2668
  };
2662
2669
 
2663
2670
  /**
@@ -2808,13 +2815,13 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
2808
2815
 
2809
2816
  // Optimization: if primitive path with no validators, or array of primitives
2810
2817
  // with no validators, skip validating this path entirely.
2811
- if (!_pathType.caster && _pathType.validators.length === 0 && !_pathType.$parentSchemaDocArray) {
2818
+ if (!_pathType.schema && !_pathType.embeddedSchemaType && _pathType.validators.length === 0 && !_pathType.$parentSchemaDocArray) {
2812
2819
  paths.delete(path);
2813
2820
  } else if (_pathType.$isMongooseArray &&
2814
2821
  !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2815
- !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2822
+ !_pathType.embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2816
2823
  _pathType.validators.length === 0 && // and arrays with top-level validators
2817
- _pathType.$embeddedSchemaType.validators.length === 0) {
2824
+ _pathType.embeddedSchemaType.validators.length === 0) {
2818
2825
  paths.delete(path);
2819
2826
  }
2820
2827
  }
@@ -2899,8 +2906,8 @@ function _addArrayPathsToValidate(doc, paths) {
2899
2906
  // on the array type, there's no need to run validation on the individual array elements.
2900
2907
  if (_pathType.$isMongooseArray &&
2901
2908
  !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2902
- !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2903
- _pathType.$embeddedSchemaType.validators.length === 0) {
2909
+ !_pathType.embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2910
+ _pathType.embeddedSchemaType.validators.length === 0) {
2904
2911
  continue;
2905
2912
  }
2906
2913
 
@@ -2926,16 +2933,32 @@ function _pushNestedArrayPaths(val, paths, path) {
2926
2933
  * ignore
2927
2934
  */
2928
2935
 
2929
- Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2936
+ Document.prototype._execDocumentPreHooks = async function _execDocumentPreHooks(opName, ...args) {
2937
+ return this.$__middleware.execPre(opName, this, [...args]);
2938
+ };
2939
+
2940
+ /*!
2941
+ * ignore
2942
+ */
2943
+
2944
+ Document.prototype._execDocumentPostHooks = async function _execDocumentPostHooks(opName, error) {
2945
+ return this.$__middleware.execPost(opName, this, [this], { error });
2946
+ };
2947
+
2948
+ /*!
2949
+ * ignore
2950
+ */
2951
+
2952
+ Document.prototype.$__validate = async function $__validate(pathsToValidate, options) {
2953
+ try {
2954
+ [options] = await this._execDocumentPreHooks('validate', options);
2955
+ } catch (error) {
2956
+ await this._execDocumentPostHooks('validate', error);
2957
+ return;
2958
+ }
2959
+
2930
2960
  if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) {
2931
2961
  pathsToValidate = [...this.$__.saveOptions.pathsToSave];
2932
- } else if (typeof pathsToValidate === 'function') {
2933
- callback = pathsToValidate;
2934
- options = null;
2935
- pathsToValidate = null;
2936
- } else if (typeof options === 'function') {
2937
- callback = options;
2938
- options = null;
2939
2962
  }
2940
2963
 
2941
2964
  const hasValidateModifiedOnlyOption = options &&
@@ -3033,110 +3056,90 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
3033
3056
  }
3034
3057
 
3035
3058
  if (paths.length === 0) {
3036
- return immediate(function() {
3037
- const error = _complete();
3038
- if (error) {
3039
- return _this.$__schema.s.hooks.execPost('validate:error', _this, [_this], { error: error }, function(error) {
3040
- callback(error);
3041
- });
3042
- }
3043
- callback(null, _this);
3044
- });
3059
+ const error = _complete();
3060
+ await this._execDocumentPostHooks('validate', error);
3061
+ return;
3045
3062
  }
3046
3063
 
3047
3064
  const validated = {};
3048
- let total = 0;
3049
3065
 
3050
3066
  let pathsToSave = this.$__.saveOptions?.pathsToSave;
3067
+ const promises = [];
3051
3068
  if (Array.isArray(pathsToSave)) {
3052
3069
  pathsToSave = new Set(pathsToSave);
3053
3070
  for (const path of paths) {
3054
3071
  if (!pathsToSave.has(path)) {
3055
3072
  continue;
3056
3073
  }
3057
- validatePath(path);
3074
+ promises.push(validatePath(path));
3058
3075
  }
3059
3076
  } else {
3060
3077
  for (const path of paths) {
3061
- validatePath(path);
3078
+ promises.push(validatePath(path));
3062
3079
  }
3063
3080
  }
3081
+ await Promise.all(promises);
3082
+ const error = _complete();
3083
+ await this._execDocumentPostHooks('validate', error);
3064
3084
 
3065
- function validatePath(path) {
3085
+ async function validatePath(path) {
3066
3086
  if (path == null || validated[path]) {
3067
3087
  return;
3068
3088
  }
3069
3089
 
3070
3090
  validated[path] = true;
3071
- total++;
3091
+ const schemaType = _this.$__schema.path(path);
3072
3092
 
3073
- immediate(function() {
3074
- const schemaType = _this.$__schema.path(path);
3093
+ if (!schemaType) {
3094
+ return;
3095
+ }
3075
3096
 
3076
- if (!schemaType) {
3077
- return --total || complete();
3078
- }
3097
+ // If user marked as invalid or there was a cast error, don't validate
3098
+ if (!_this.$isValid(path)) {
3099
+ return;
3100
+ }
3079
3101
 
3080
- // If user marked as invalid or there was a cast error, don't validate
3081
- if (!_this.$isValid(path)) {
3082
- --total || complete();
3083
- return;
3084
- }
3102
+ // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141)
3103
+ if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) {
3104
+ return;
3105
+ }
3085
3106
 
3086
- // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141)
3087
- if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) {
3088
- return --total || complete();
3089
- }
3107
+ let val = _this.$__getValue(path);
3090
3108
 
3091
- let val = _this.$__getValue(path);
3092
-
3093
- // If you `populate()` and get back a null value, required validators
3094
- // shouldn't fail (gh-8018). We should always fall back to the populated
3095
- // value.
3096
- let pop;
3097
- if ((pop = _this.$populated(path))) {
3098
- val = pop;
3099
- } else if (val != null && val.$__ != null && val.$__.wasPopulated) {
3100
- // Array paths, like `somearray.1`, do not show up as populated with `$populated()`,
3101
- // so in that case pull out the document's id
3102
- val = val._doc._id;
3103
- }
3104
- const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ?
3105
- _this.$__.pathsToScopes[path] :
3106
- _this;
3107
-
3108
- const doValidateOptions = {
3109
- ...doValidateOptionsByPath[path],
3110
- path: path,
3111
- validateAllPaths,
3112
- _nestedValidate: true
3113
- };
3114
-
3115
- schemaType.doValidate(val, function(err) {
3116
- if (err) {
3117
- const isSubdoc = schemaType.$isSingleNested ||
3118
- schemaType.$isArraySubdocument ||
3119
- schemaType.$isMongooseDocumentArray;
3120
- if (isSubdoc && err instanceof ValidationError) {
3121
- return --total || complete();
3122
- }
3123
- _this.invalidate(path, err, undefined, true);
3124
- }
3125
- --total || complete();
3126
- }, scope, doValidateOptions);
3127
- });
3128
- }
3109
+ // If you `populate()` and get back a null value, required validators
3110
+ // shouldn't fail (gh-8018). We should always fall back to the populated
3111
+ // value.
3112
+ let pop;
3113
+ if ((pop = _this.$populated(path))) {
3114
+ val = pop;
3115
+ } else if (val != null && val.$__ != null && val.$__.wasPopulated) {
3116
+ // Array paths, like `somearray.1`, do not show up as populated with `$populated()`,
3117
+ // so in that case pull out the document's id
3118
+ val = val._doc._id;
3119
+ }
3120
+ const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ?
3121
+ _this.$__.pathsToScopes[path] :
3122
+ _this;
3129
3123
 
3130
- function complete() {
3131
- const error = _complete();
3132
- if (error) {
3133
- return _this.$__schema.s.hooks.execPost('validate:error', _this, [_this], { error: error }, function(error) {
3134
- callback(error);
3135
- });
3124
+ const doValidateOptions = {
3125
+ ...doValidateOptionsByPath[path],
3126
+ path: path,
3127
+ validateAllPaths,
3128
+ _nestedValidate: true
3129
+ };
3130
+
3131
+ try {
3132
+ await schemaType.doValidate(val, scope, doValidateOptions);
3133
+ } catch (err) {
3134
+ const isSubdoc = schemaType.$isSingleNested ||
3135
+ schemaType.$isArraySubdocument ||
3136
+ schemaType.$isMongooseDocumentArray;
3137
+ if (isSubdoc && err instanceof ValidationError) {
3138
+ return;
3139
+ }
3140
+ _this.invalidate(path, err, undefined, true);
3136
3141
  }
3137
- callback(null, _this);
3138
3142
  }
3139
-
3140
3143
  };
3141
3144
 
3142
3145
  /*!
@@ -3681,6 +3684,7 @@ Document.prototype.$__setSchema = function(schema) {
3681
3684
  this.schema = schema;
3682
3685
  }
3683
3686
  this.$__schema = schema;
3687
+ this.$__middleware = schema._getDocumentMiddleware();
3684
3688
  this[documentSchemaSymbol] = schema;
3685
3689
  };
3686
3690
 
@@ -4274,9 +4278,9 @@ function applyGetters(self, json) {
4274
4278
  branch[part],
4275
4279
  self
4276
4280
  );
4277
- if (Array.isArray(branch[part]) && schema.paths[path].$embeddedSchemaType) {
4281
+ if (Array.isArray(branch[part]) && schema.paths[path].embeddedSchemaType) {
4278
4282
  for (let i = 0; i < branch[part].length; ++i) {
4279
- branch[part][i] = schema.paths[path].$embeddedSchemaType.applyGetters(
4283
+ branch[part][i] = schema.paths[path].embeddedSchemaType.applyGetters(
4280
4284
  branch[part][i],
4281
4285
  self
4282
4286
  );
@@ -4318,8 +4322,8 @@ function applySchemaTypeTransforms(self, json) {
4318
4322
  for (const path of paths) {
4319
4323
  const schematype = schema.paths[path];
4320
4324
  const topLevelTransformFunction = schematype.options.transform ?? schematype.constructor?.defaultOptions?.transform;
4321
- const embeddedSchemaTypeTransformFunction = schematype.$embeddedSchemaType?.options?.transform
4322
- ?? schematype.$embeddedSchemaType?.constructor?.defaultOptions?.transform;
4325
+ const embeddedSchemaTypeTransformFunction = schematype.embeddedSchemaType?.options?.transform
4326
+ ?? schematype.embeddedSchemaType?.constructor?.defaultOptions?.transform;
4323
4327
  if (typeof topLevelTransformFunction === 'function') {
4324
4328
  const val = self.$get(path);
4325
4329
  if (val === undefined) {
@@ -5046,7 +5050,7 @@ Document.prototype.$__delta = function $__delta() {
5046
5050
  }
5047
5051
 
5048
5052
  if (divergent.length) {
5049
- return new DivergentArrayError(divergent);
5053
+ throw new DivergentArrayError(divergent);
5050
5054
  }
5051
5055
 
5052
5056
  if (this.$__.version) {
@@ -55,7 +55,6 @@ Object.setPrototypeOf(NativeConnection.prototype, MongooseConnection.prototype);
55
55
  * @param {String} name The database name
56
56
  * @param {Object} [options]
57
57
  * @param {Boolean} [options.useCache=false] If true, cache results so calling `useDb()` multiple times with the same name only creates 1 connection object.
58
- * @param {Boolean} [options.noListener=false] If true, the new connection object won't listen to any events on the base connection. This is better for memory usage in cases where you're calling `useDb()` for every request.
59
58
  * @return {Connection} New Connection Object
60
59
  * @api public
61
60
  */
@@ -107,21 +106,15 @@ NativeConnection.prototype.useDb = function(name, options) {
107
106
 
108
107
  function wireup() {
109
108
  newConn.client = _this.client;
110
- const _opts = {};
111
- if (options.hasOwnProperty('noListener')) {
112
- _opts.noListener = options.noListener;
113
- }
114
- newConn.db = _this.client.db(name, _opts);
109
+ newConn.db = _this.client.db(name);
115
110
  newConn._lastHeartbeatAt = _this._lastHeartbeatAt;
116
111
  newConn.onOpen();
117
112
  }
118
113
 
119
114
  newConn.name = name;
120
115
 
121
- // push onto the otherDbs stack, this is used when state changes and when heartbeat is received
122
- if (options.noListener !== true) {
123
- this.otherDbs.push(newConn);
124
- }
116
+ // push onto the otherDbs stack, this is used when state changes
117
+ this.otherDbs.push(newConn);
125
118
  newConn.otherDbs.push(this);
126
119
 
127
120
  // push onto the relatedDbs cache, this is used when state changes
@@ -17,10 +17,9 @@ const MongooseError = require('./mongooseError');
17
17
  */
18
18
 
19
19
  class ObjectParameterError extends MongooseError {
20
-
21
20
  constructor(value, paramName, fnName) {
22
21
  super('Parameter "' + paramName + '" to ' + fnName +
23
- '() must be an object, got "' + value.toString() + '" (type ' + typeof value + ')');
22
+ '() must be an object, got "' + (value == null ? value : value.toString()) + '" (type ' + typeof value + ')');
24
23
  }
25
24
  }
26
25
 
@@ -44,14 +44,6 @@ class ValidationError extends MongooseError {
44
44
  return this.name + ': ' + combinePathErrors(this);
45
45
  }
46
46
 
47
- /**
48
- * inspect helper
49
- * @api private
50
- */
51
- inspect() {
52
- return Object.assign(new Error(this.message), this);
53
- }
54
-
55
47
  /**
56
48
  * add message
57
49
  * @param {String} path
@@ -11,7 +11,7 @@ const isObject = require('./isObject');
11
11
  const isPOJO = require('./isPOJO');
12
12
  const symbols = require('./symbols');
13
13
  const trustedSymbol = require('./query/trusted').trustedSymbol;
14
- const BSON = require('bson');
14
+ const BSON = require('mongodb/lib/bson');
15
15
 
16
16
  /**
17
17
  * Object clone with Mongoose natives support.
@@ -4,7 +4,7 @@
4
4
  * Module dependencies.
5
5
  */
6
6
 
7
- const Binary = require('bson').Binary;
7
+ const Binary = require('mongodb/lib/bson').Binary;
8
8
  const isBsonType = require('./isBsonType');
9
9
  const isMongooseObject = require('./isMongooseObject');
10
10
  const MongooseError = require('../error');
@@ -20,7 +20,6 @@ module.exports = function isIndexEqual(schemaIndexKeysObject, options, dbIndex)
20
20
  // key: { _fts: 'text', _ftsx: 1 },
21
21
  // name: 'name_text',
22
22
  // ns: 'test.tests',
23
- // background: true,
24
23
  // weights: { name: 1 },
25
24
  // default_language: 'english',
26
25
  // language_override: 'language',
@@ -23,7 +23,7 @@ module.exports = function applyDefaultsToPOJO(doc, schema) {
23
23
  if (j === len - 1) {
24
24
  if (typeof doc_[piece] !== 'undefined') {
25
25
  if (type.$isSingleNested) {
26
- applyDefaultsToPOJO(doc_[piece], type.caster.schema);
26
+ applyDefaultsToPOJO(doc_[piece], type.schema);
27
27
  } else if (type.$isMongooseDocumentArray && Array.isArray(doc_[piece])) {
28
28
  doc_[piece].forEach(el => applyDefaultsToPOJO(el, type.schema));
29
29
  }
@@ -36,7 +36,7 @@ module.exports = function applyDefaultsToPOJO(doc, schema) {
36
36
  doc_[piece] = def;
37
37
 
38
38
  if (type.$isSingleNested) {
39
- applyDefaultsToPOJO(def, type.caster.schema);
39
+ applyDefaultsToPOJO(def, type.schema);
40
40
  } else if (type.$isMongooseDocumentArray && Array.isArray(def)) {
41
41
  def.forEach(el => applyDefaultsToPOJO(el, type.schema));
42
42
  }
@@ -1,8 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const symbols = require('../../schema/symbols');
4
- const promiseOrCallback = require('../promiseOrCallback');
5
-
6
3
  /*!
7
4
  * ignore
8
5
  */
@@ -15,10 +12,10 @@ module.exports = applyHooks;
15
12
 
16
13
  applyHooks.middlewareFunctions = [
17
14
  'deleteOne',
18
- 'save',
19
- 'validate',
20
15
  'remove',
16
+ 'save',
21
17
  'updateOne',
18
+ 'validate',
22
19
  'init'
23
20
  ];
24
21
 
@@ -47,15 +44,15 @@ function applyHooks(model, schema, options) {
47
44
  contextParameter: true
48
45
  };
49
46
  const objToDecorate = options.decorateDoc ? model : model.prototype;
50
-
51
47
  model.$appliedHooks = true;
52
48
  for (const key of Object.keys(schema.paths)) {
53
- const type = schema.paths[key];
49
+ let type = schema.paths[key];
54
50
  let childModel = null;
55
- if (type.$isSingleNested) {
56
- childModel = type.caster;
57
- } else if (type.$isMongooseDocumentArray) {
58
- childModel = type.Constructor;
51
+
52
+ const result = findChildModel(type);
53
+ if (result) {
54
+ childModel = result.childModel;
55
+ type = result.type;
59
56
  } else {
60
57
  continue;
61
58
  }
@@ -64,7 +61,11 @@ function applyHooks(model, schema, options) {
64
61
  continue;
65
62
  }
66
63
 
67
- applyHooks(childModel, type.schema, { ...options, isChildSchema: true });
64
+ applyHooks(childModel, type.schema, {
65
+ ...options,
66
+ decorateDoc: false,
67
+ isChildSchema: true
68
+ });
68
69
  if (childModel.discriminators != null) {
69
70
  const keys = Object.keys(childModel.discriminators);
70
71
  for (const key of keys) {
@@ -78,39 +79,10 @@ function applyHooks(model, schema, options) {
78
79
  // promises and make it so that `doc.save.toString()` provides meaningful
79
80
  // information.
80
81
 
81
- const middleware = schema.s.hooks.
82
- filter(hook => {
83
- if (hook.name === 'updateOne' || hook.name === 'deleteOne') {
84
- return !!hook['document'];
85
- }
86
- if (hook.name === 'remove' || hook.name === 'init') {
87
- return hook['document'] == null || !!hook['document'];
88
- }
89
- if (hook.query != null || hook.document != null) {
90
- return hook.document !== false;
91
- }
92
- return true;
93
- }).
94
- filter(hook => {
95
- // If user has overwritten the method, don't apply built-in middleware
96
- if (schema.methods[hook.name]) {
97
- return !hook.fn[symbols.builtInMiddleware];
98
- }
99
-
100
- return true;
101
- });
82
+ const middleware = schema._getDocumentMiddleware();
102
83
 
103
84
  model._middleware = middleware;
104
85
 
105
- objToDecorate.$__originalValidate = objToDecorate.$__originalValidate || objToDecorate.$__validate;
106
-
107
- const internalMethodsToWrap = options && options.isChildSchema ? ['save', 'validate', 'deleteOne'] : ['save', 'validate'];
108
- for (const method of internalMethodsToWrap) {
109
- const toWrap = method === 'validate' ? '$__originalValidate' : `$__${method}`;
110
- const wrapped = middleware.
111
- createWrapper(method, objToDecorate[toWrap], null, kareemOptions);
112
- objToDecorate[`$__${method}`] = wrapped;
113
- }
114
86
  objToDecorate.$__init = middleware.
115
87
  createWrapperSync('init', objToDecorate.$__init, null, kareemOptions);
116
88
 
@@ -134,17 +106,35 @@ function applyHooks(model, schema, options) {
134
106
  continue;
135
107
  }
136
108
  const originalMethod = objToDecorate[method];
137
- objToDecorate[method] = function() {
138
- const args = Array.prototype.slice.call(arguments);
139
- const cb = args.slice(-1).pop();
140
- const argsWithoutCallback = typeof cb === 'function' ?
141
- args.slice(0, args.length - 1) : args;
142
- return promiseOrCallback(cb, callback => {
143
- return this[`$__${method}`].apply(this,
144
- argsWithoutCallback.concat([callback]));
145
- }, model.events);
146
- };
147
- objToDecorate[`$__${method}`] = middleware.
109
+ objToDecorate[`$__${method}`] = objToDecorate[method];
110
+ objToDecorate[method] = middleware.
148
111
  createWrapper(method, originalMethod, null, customMethodOptions);
149
112
  }
150
113
  }
114
+
115
+ /**
116
+ * Check if there is an embedded schematype in the given schematype. Handles drilling down into primitive
117
+ * arrays and maps in case of array of array of subdocs or map of subdocs.
118
+ *
119
+ * @param {SchemaType} curType
120
+ * @returns {{ childModel: Model | typeof Subdocument, curType: SchemaType } | null}
121
+ */
122
+
123
+ function findChildModel(curType) {
124
+ if (curType.$isSingleNested || curType.$isMongooseDocumentArray) {
125
+ return { childModel: curType.Constructor, type: curType };
126
+ }
127
+ if (curType.instance === 'Array') {
128
+ const embedded = curType.getEmbeddedSchemaType();
129
+ if (embedded) {
130
+ return findChildModel(embedded);
131
+ }
132
+ }
133
+ if (curType.instance === 'Map') {
134
+ const mapType = curType.getEmbeddedSchemaType();
135
+ if (mapType) {
136
+ return findChildModel(mapType);
137
+ }
138
+ }
139
+ return null;
140
+ }
@@ -60,8 +60,8 @@ module.exports = function applyMethods(model, schema) {
60
60
  model.$appliedMethods = true;
61
61
  for (const key of Object.keys(schema.paths)) {
62
62
  const type = schema.paths[key];
63
- if (type.$isSingleNested && !type.caster.$appliedMethods) {
64
- applyMethods(type.caster, type.schema);
63
+ if (type.$isSingleNested && !type.Constructor.$appliedMethods) {
64
+ applyMethods(type.Constructor, type.schema);
65
65
  }
66
66
  if (type.$isMongooseDocumentArray && !type.Constructor.$appliedMethods) {
67
67
  applyMethods(type.Constructor, type.schema);