mongoose 9.1.6 → 9.2.1
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/lib/aggregate.js +26 -12
- package/lib/cursor/aggregationCursor.js +1 -1
- package/lib/cursor/queryCursor.js +1 -1
- package/lib/document.js +25 -11
- package/lib/helpers/buildMiddlewareFilter.js +24 -0
- package/lib/helpers/clone.js +18 -0
- package/lib/helpers/model/applyHooks.js +12 -1
- package/lib/helpers/query/castUpdate.js +2 -2
- package/lib/helpers/timestamps/setupTimestamps.js +45 -29
- package/lib/model.js +93 -41
- package/lib/mongoose.js +21 -1
- package/lib/options.js +2 -1
- package/lib/plugins/saveSubdocs.js +85 -67
- package/lib/plugins/sharding.js +33 -16
- package/lib/plugins/trackTransaction.js +26 -21
- package/lib/plugins/validateBeforeSave.js +37 -31
- package/lib/query.js +69 -31
- package/lib/queryHelpers.js +9 -4
- package/lib/schema.js +11 -12
- package/lib/types/subdocument.js +10 -6
- package/lib/validOptions.js +1 -0
- package/package.json +5 -19
- package/types/aggregate.d.ts +2 -0
- package/types/document.d.ts +15 -100
- package/types/index.d.ts +93 -10
- package/types/inferrawdoctype.d.ts +13 -3
- package/types/inferschematype.d.ts +6 -1
- package/types/middlewares.d.ts +7 -0
- package/types/models.d.ts +10 -55
- package/types/mongooseoptions.d.ts +10 -6
- package/types/query.d.ts +24 -3
- package/types/types.d.ts +2 -2
- package/types/utility.d.ts +1 -1
- package/types/virtuals.d.ts +2 -2
package/lib/aggregate.js
CHANGED
|
@@ -11,6 +11,7 @@ const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/ap
|
|
|
11
11
|
const clone = require('./helpers/clone');
|
|
12
12
|
const getConstructorName = require('./helpers/getConstructorName');
|
|
13
13
|
const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
|
|
14
|
+
const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
|
|
14
15
|
const stringifyFunctionOperators = require('./helpers/aggregate/stringifyFunctionOperators');
|
|
15
16
|
const utils = require('./utils');
|
|
16
17
|
const { modelSymbol } = require('./helpers/symbols');
|
|
@@ -718,7 +719,7 @@ Aggregate.prototype.read = function(pref, tags) {
|
|
|
718
719
|
*
|
|
719
720
|
* await Model.aggregate(pipeline).readConcern('majority');
|
|
720
721
|
*
|
|
721
|
-
* @param {
|
|
722
|
+
* @param {'local'|'available'|'majority'|'snapshot'|'linearizable'|'l'|'a'|'m'|'s'|'lz'} level one of the listed read concern level or their aliases
|
|
722
723
|
* @see mongodb https://www.mongodb.com/docs/manual/reference/read-concern/
|
|
723
724
|
* @return {Aggregate} this
|
|
724
725
|
* @api public
|
|
@@ -784,7 +785,7 @@ Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) {
|
|
|
784
785
|
*
|
|
785
786
|
* Model.aggregate(..).explain()
|
|
786
787
|
*
|
|
787
|
-
* @param {
|
|
788
|
+
* @param {'queryPlanner'|'executionStats'|'allPlansExecution'} [verbosity]
|
|
788
789
|
* @return {Promise}
|
|
789
790
|
*/
|
|
790
791
|
|
|
@@ -800,13 +801,20 @@ Aggregate.prototype.explain = async function explain(verbosity) {
|
|
|
800
801
|
|
|
801
802
|
prepareDiscriminatorPipeline(this._pipeline, this._model.schema);
|
|
802
803
|
|
|
804
|
+
const preFilter = buildMiddlewareFilter(this.options, 'pre');
|
|
805
|
+
const postFilter = buildMiddlewareFilter(this.options, 'post');
|
|
806
|
+
|
|
807
|
+
// Remove middleware option before passing to MongoDB
|
|
808
|
+
const options = this.options != null ? { ...this.options } : {};
|
|
809
|
+
delete options.middleware;
|
|
810
|
+
|
|
803
811
|
try {
|
|
804
|
-
await model.hooks.execPre('aggregate', this);
|
|
812
|
+
await model.hooks.execPre('aggregate', this, [], { filter: preFilter });
|
|
805
813
|
} catch (error) {
|
|
806
|
-
return await model.hooks.execPost('aggregate', this, [null], { error });
|
|
814
|
+
return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
|
|
807
815
|
}
|
|
808
816
|
|
|
809
|
-
const cursor = model.collection.aggregate(this._pipeline,
|
|
817
|
+
const cursor = await model.collection.aggregate(this._pipeline, options);
|
|
810
818
|
|
|
811
819
|
if (verbosity == null) {
|
|
812
820
|
verbosity = true;
|
|
@@ -816,10 +824,10 @@ Aggregate.prototype.explain = async function explain(verbosity) {
|
|
|
816
824
|
try {
|
|
817
825
|
result = await cursor.explain(verbosity);
|
|
818
826
|
} catch (error) {
|
|
819
|
-
return await model.hooks.execPost('aggregate', this, [null], { error });
|
|
827
|
+
return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
|
|
820
828
|
}
|
|
821
829
|
|
|
822
|
-
await model.hooks.execPost('aggregate', this, [result], { error: null });
|
|
830
|
+
await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter });
|
|
823
831
|
|
|
824
832
|
return result;
|
|
825
833
|
};
|
|
@@ -893,6 +901,9 @@ Aggregate.prototype.session = function(session) {
|
|
|
893
901
|
* @param {Boolean} [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation
|
|
894
902
|
* @param {Object} [options.collation] object see [`Aggregate.prototype.collation()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.collation())
|
|
895
903
|
* @param {ClientSession} [options.session] ClientSession see [`Aggregate.prototype.session()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.session())
|
|
904
|
+
* @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
|
|
905
|
+
* @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
|
|
906
|
+
* @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
|
|
896
907
|
* @see mongodb https://www.mongodb.com/docs/manual/reference/command/aggregate/
|
|
897
908
|
* @return {Aggregate} this
|
|
898
909
|
* @api public
|
|
@@ -1056,10 +1067,13 @@ Aggregate.prototype.exec = async function exec() {
|
|
|
1056
1067
|
prepareDiscriminatorPipeline(this._pipeline, this._model.schema);
|
|
1057
1068
|
stringifyFunctionOperators(this._pipeline);
|
|
1058
1069
|
|
|
1070
|
+
const preFilter = buildMiddlewareFilter(this.options, 'pre');
|
|
1071
|
+
const postFilter = buildMiddlewareFilter(this.options, 'post');
|
|
1072
|
+
|
|
1059
1073
|
try {
|
|
1060
|
-
await model.hooks.execPre('aggregate', this);
|
|
1074
|
+
await model.hooks.execPre('aggregate', this, [], { filter: preFilter });
|
|
1061
1075
|
} catch (error) {
|
|
1062
|
-
return await model.hooks.execPost('aggregate', this, [null], { error });
|
|
1076
|
+
return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
|
|
1063
1077
|
}
|
|
1064
1078
|
|
|
1065
1079
|
if (!this._pipeline.length) {
|
|
@@ -1067,17 +1081,17 @@ Aggregate.prototype.exec = async function exec() {
|
|
|
1067
1081
|
}
|
|
1068
1082
|
|
|
1069
1083
|
const options = clone(this.options || {});
|
|
1084
|
+
delete options.middleware;
|
|
1070
1085
|
|
|
1071
1086
|
let result;
|
|
1072
1087
|
try {
|
|
1073
1088
|
const cursor = await collection.aggregate(this._pipeline, options);
|
|
1074
1089
|
result = await cursor.toArray();
|
|
1075
1090
|
} catch (error) {
|
|
1076
|
-
return await model.hooks.execPost('aggregate', this, [null], { error });
|
|
1091
|
+
return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
|
|
1077
1092
|
}
|
|
1078
1093
|
|
|
1079
|
-
await model.hooks.execPost('aggregate', this, [result], { error: null });
|
|
1080
|
-
|
|
1094
|
+
await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter });
|
|
1081
1095
|
return result;
|
|
1082
1096
|
};
|
|
1083
1097
|
|
|
@@ -389,7 +389,7 @@ function _transformForAsyncIterator(doc) {
|
|
|
389
389
|
* Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/4.9/classes/AggregationCursor.html#addCursorFlag).
|
|
390
390
|
* Useful for setting the `noCursorTimeout` and `tailable` flags.
|
|
391
391
|
*
|
|
392
|
-
* @param {
|
|
392
|
+
* @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
|
|
393
393
|
* @param {Boolean} value
|
|
394
394
|
* @return {AggregationCursor} this
|
|
395
395
|
* @api public
|
|
@@ -371,7 +371,7 @@ QueryCursor.prototype.options;
|
|
|
371
371
|
* Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/4.9/classes/FindCursor.html#addCursorFlag).
|
|
372
372
|
* Useful for setting the `noCursorTimeout` and `tailable` flags.
|
|
373
373
|
*
|
|
374
|
-
* @param {
|
|
374
|
+
* @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
|
|
375
375
|
* @param {Boolean} value
|
|
376
376
|
* @return {AggregationCursor} this
|
|
377
377
|
* @api public
|
package/lib/document.js
CHANGED
|
@@ -41,6 +41,7 @@ const minimize = require('./helpers/minimize');
|
|
|
41
41
|
const mpath = require('mpath');
|
|
42
42
|
const parentPaths = require('./helpers/path/parentPaths');
|
|
43
43
|
const queryhelpers = require('./queryHelpers');
|
|
44
|
+
const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
|
|
44
45
|
const utils = require('./utils');
|
|
45
46
|
const isPromise = require('./helpers/isPromise');
|
|
46
47
|
|
|
@@ -860,8 +861,11 @@ function init(self, obj, doc, opts, prefix) {
|
|
|
860
861
|
* @param {Object} update
|
|
861
862
|
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
|
|
862
863
|
* @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and the [Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
|
|
863
|
-
* @param {Boolean|
|
|
864
|
+
* @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
|
|
864
865
|
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/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.
|
|
866
|
+
* @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
|
|
867
|
+
* @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
|
|
868
|
+
* @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
|
|
865
869
|
* @return {Query}
|
|
866
870
|
* @api public
|
|
867
871
|
* @memberOf Document
|
|
@@ -872,7 +876,7 @@ Document.prototype.updateOne = function updateOne(update, options) {
|
|
|
872
876
|
const query = this.constructor.updateOne();
|
|
873
877
|
const self = this;
|
|
874
878
|
query.pre(async function queryPreUpdateOne() {
|
|
875
|
-
const res = await self._execDocumentPreHooks('updateOne', self, update, options);
|
|
879
|
+
const res = await self._execDocumentPreHooks('updateOne', options, [self, update, options]);
|
|
876
880
|
// `self` is passed to pre hooks as argument for backwards compatibility, but that
|
|
877
881
|
// isn't the actual arguments passed to the wrapped function.
|
|
878
882
|
if (res[0] !== self || res[1] !== update || res[2] !== options) {
|
|
@@ -892,7 +896,7 @@ Document.prototype.updateOne = function updateOne(update, options) {
|
|
|
892
896
|
return res;
|
|
893
897
|
});
|
|
894
898
|
query.post(function queryPostUpdateOne() {
|
|
895
|
-
return self._execDocumentPostHooks('updateOne');
|
|
899
|
+
return self._execDocumentPostHooks('updateOne', options);
|
|
896
900
|
});
|
|
897
901
|
|
|
898
902
|
return query;
|
|
@@ -2646,6 +2650,9 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
|
|
|
2646
2650
|
* @param {Object} [options] internal options
|
|
2647
2651
|
* @param {Boolean} [options.validateModifiedOnly=false] if `true` mongoose validates only modified paths.
|
|
2648
2652
|
* @param {Array|string} [options.pathsToSkip] list of paths to skip. If set, Mongoose will validate every modified path that is not in this list.
|
|
2653
|
+
* @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
|
|
2654
|
+
* @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
|
|
2655
|
+
* @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
|
|
2649
2656
|
* @return {Promise} Returns a Promise.
|
|
2650
2657
|
* @api public
|
|
2651
2658
|
*/
|
|
@@ -2961,16 +2968,18 @@ function _pushNestedArrayPaths(val, paths, path) {
|
|
|
2961
2968
|
* ignore
|
|
2962
2969
|
*/
|
|
2963
2970
|
|
|
2964
|
-
Document.prototype._execDocumentPreHooks = async function _execDocumentPreHooks(opName,
|
|
2965
|
-
|
|
2971
|
+
Document.prototype._execDocumentPreHooks = async function _execDocumentPreHooks(opName, options, argsForHooks) {
|
|
2972
|
+
const filter = buildMiddlewareFilter(options, 'pre');
|
|
2973
|
+
return this.$__middleware.execPre(opName, this, argsForHooks || [], { filter });
|
|
2966
2974
|
};
|
|
2967
2975
|
|
|
2968
2976
|
/*!
|
|
2969
2977
|
* ignore
|
|
2970
2978
|
*/
|
|
2971
2979
|
|
|
2972
|
-
Document.prototype._execDocumentPostHooks = async function _execDocumentPostHooks(opName, error) {
|
|
2973
|
-
|
|
2980
|
+
Document.prototype._execDocumentPostHooks = async function _execDocumentPostHooks(opName, options, error) {
|
|
2981
|
+
const filter = buildMiddlewareFilter(options, 'post');
|
|
2982
|
+
return this.$__middleware.execPost(opName, this, [this], { error, filter });
|
|
2974
2983
|
};
|
|
2975
2984
|
|
|
2976
2985
|
/*!
|
|
@@ -2979,9 +2988,9 @@ Document.prototype._execDocumentPostHooks = async function _execDocumentPostHook
|
|
|
2979
2988
|
|
|
2980
2989
|
Document.prototype.$__validate = async function $__validate(pathsToValidate, options) {
|
|
2981
2990
|
try {
|
|
2982
|
-
[options] = await this._execDocumentPreHooks('validate', options);
|
|
2991
|
+
[options] = await this._execDocumentPreHooks('validate', options, [options]);
|
|
2983
2992
|
} catch (error) {
|
|
2984
|
-
await this._execDocumentPostHooks('validate', error);
|
|
2993
|
+
await this._execDocumentPostHooks('validate', options, error);
|
|
2985
2994
|
return;
|
|
2986
2995
|
}
|
|
2987
2996
|
|
|
@@ -3085,7 +3094,7 @@ Document.prototype.$__validate = async function $__validate(pathsToValidate, opt
|
|
|
3085
3094
|
|
|
3086
3095
|
if (paths.length === 0) {
|
|
3087
3096
|
const error = _complete();
|
|
3088
|
-
await this._execDocumentPostHooks('validate', error);
|
|
3097
|
+
await this._execDocumentPostHooks('validate', options, error);
|
|
3089
3098
|
return;
|
|
3090
3099
|
}
|
|
3091
3100
|
|
|
@@ -3108,7 +3117,7 @@ Document.prototype.$__validate = async function $__validate(pathsToValidate, opt
|
|
|
3108
3117
|
}
|
|
3109
3118
|
await Promise.all(promises);
|
|
3110
3119
|
const error = _complete();
|
|
3111
|
-
await this._execDocumentPostHooks('validate', error);
|
|
3120
|
+
await this._execDocumentPostHooks('validate', options, error);
|
|
3112
3121
|
|
|
3113
3122
|
async function validatePath(path) {
|
|
3114
3123
|
if (path == null || validated[path]) {
|
|
@@ -3233,6 +3242,9 @@ function _handlePathsToSkip(paths, pathsToSkip) {
|
|
|
3233
3242
|
* @param {Object} [options] options for validation
|
|
3234
3243
|
* @param {Boolean} [options.validateModifiedOnly=false] If `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
|
|
3235
3244
|
* @param {Array|string} [options.pathsToSkip] list of paths to skip. If set, Mongoose will validate every modified path that is not in this list.
|
|
3245
|
+
* @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
|
|
3246
|
+
* @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
|
|
3247
|
+
* @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
|
|
3236
3248
|
* @return {ValidationError|undefined} ValidationError if there are errors during validation, or undefined if there is no error.
|
|
3237
3249
|
* @api public
|
|
3238
3250
|
*/
|
|
@@ -4162,6 +4174,7 @@ Document.prototype.$__toObjectShallow = function $__toObjectShallow(schemaFields
|
|
|
4162
4174
|
* @param {Boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output
|
|
4163
4175
|
* @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
|
|
4164
4176
|
* @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
|
|
4177
|
+
* @param {Boolean} [options.flattenUUIDs=false] if true, convert any UUIDs in the result to 36-character UUID strings in 8-4-4-4-12 format.
|
|
4165
4178
|
* @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.
|
|
4166
4179
|
* @param {Boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toObject()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema.
|
|
4167
4180
|
* @return {Object} document as a plain old JavaScript object (POJO). This object may contain ObjectIds, Maps, Dates, mongodb.Binary, Buffers, and other non-POJO values.
|
|
@@ -4434,6 +4447,7 @@ function omitDeselectedFields(self, json) {
|
|
|
4434
4447
|
* @param {Object} options
|
|
4435
4448
|
* @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.
|
|
4436
4449
|
* @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
|
|
4450
|
+
* @param {Boolean} [options.flattenUUIDs=false] if true, convert any UUIDs in the result to 36-character UUID strings in 8-4-4-4-12 format.
|
|
4437
4451
|
* @param {Boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toJSON()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema.
|
|
4438
4452
|
* @return {Object}
|
|
4439
4453
|
* @see Document#toObject https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const symbols = require('../schema/symbols');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Filter predicate that returns true for built-in middleware (marked with `builtInMiddleware` symbol).
|
|
7
|
+
*/
|
|
8
|
+
const isBuiltInMiddleware = hook => hook.fn[symbols.builtInMiddleware];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds a filter for kareem's execPre/execPost based on middleware options.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} options - Options object that may contain `middleware` setting
|
|
14
|
+
* @param {String} phase - Either 'pre' or 'post'
|
|
15
|
+
* @returns {Function|null} - null runs all middleware, isBuiltInMiddleware skips user middleware
|
|
16
|
+
*/
|
|
17
|
+
function buildMiddlewareFilter(options, phase) {
|
|
18
|
+
const shouldRun = options?.middleware?.[phase] ?? options?.middleware ?? true;
|
|
19
|
+
return shouldRun ? null : isBuiltInMiddleware;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
buildMiddlewareFilter
|
|
24
|
+
};
|
package/lib/helpers/clone.js
CHANGED
|
@@ -12,6 +12,9 @@ const isPOJO = require('./isPOJO');
|
|
|
12
12
|
const symbols = require('./symbols');
|
|
13
13
|
const trustedSymbol = require('./query/trusted').trustedSymbol;
|
|
14
14
|
const BSON = require('mongodb/lib/bson');
|
|
15
|
+
const UUID = BSON.UUID;
|
|
16
|
+
|
|
17
|
+
const Binary = BSON.Binary;
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* Object clone with Mongoose natives support.
|
|
@@ -45,6 +48,14 @@ function clone(obj, options, isArrayChild) {
|
|
|
45
48
|
|
|
46
49
|
if (isMongooseObject(obj)) {
|
|
47
50
|
if (options) {
|
|
51
|
+
if (options.flattenUUIDs) {
|
|
52
|
+
if (obj instanceof Binary && obj._subtype === Binary.SUBTYPE_UUID) {
|
|
53
|
+
return obj.toString();
|
|
54
|
+
}
|
|
55
|
+
if (obj?.isMongooseBuffer && obj._subtype === Binary.SUBTYPE_UUID) {
|
|
56
|
+
return obj.toUUID().toString();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
48
59
|
if (options.retainDocuments && obj.$__ != null) {
|
|
49
60
|
const clonedDoc = obj.$clone();
|
|
50
61
|
if (obj.__index != null) {
|
|
@@ -111,6 +122,13 @@ function clone(obj, options, isArrayChild) {
|
|
|
111
122
|
return Decimal.fromString(obj.toString());
|
|
112
123
|
}
|
|
113
124
|
|
|
125
|
+
if (obj instanceof UUID) {
|
|
126
|
+
if (options?.flattenUUIDs) {
|
|
127
|
+
return obj.toJSON();
|
|
128
|
+
}
|
|
129
|
+
return new UUID(obj.buffer);
|
|
130
|
+
}
|
|
131
|
+
|
|
114
132
|
// object created with Object.create(null)
|
|
115
133
|
if (!objConstructor && isObject(obj)) {
|
|
116
134
|
return cloneObject(obj, options, isArrayChild);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { buildMiddlewareFilter } = require('../buildMiddlewareFilter');
|
|
4
|
+
|
|
3
5
|
/*!
|
|
4
6
|
* ignore
|
|
5
7
|
*/
|
|
@@ -84,7 +86,16 @@ function applyHooks(model, schema, options) {
|
|
|
84
86
|
model._middleware = middleware;
|
|
85
87
|
|
|
86
88
|
objToDecorate.$__init = middleware.
|
|
87
|
-
createWrapperSync('init', objToDecorate.$__init, null,
|
|
89
|
+
createWrapperSync('init', objToDecorate.$__init, null, {
|
|
90
|
+
...kareemOptions,
|
|
91
|
+
getOptions: (args) => {
|
|
92
|
+
const opts = args[1];
|
|
93
|
+
return {
|
|
94
|
+
pre: { filter: buildMiddlewareFilter(opts, 'pre') },
|
|
95
|
+
post: { filter: buildMiddlewareFilter(opts, 'post') }
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
88
99
|
|
|
89
100
|
// Support hooks for custom methods
|
|
90
101
|
const customMethods = Object.keys(schema.methods);
|
|
@@ -41,7 +41,7 @@ const mongodbUpdateOperators = new Set([
|
|
|
41
41
|
* @param {Schema} schema
|
|
42
42
|
* @param {Object} obj
|
|
43
43
|
* @param {Object} [options]
|
|
44
|
-
* @param {Boolean|
|
|
44
|
+
* @param {Boolean|'throw'} [options.strict] defaults to true
|
|
45
45
|
* @param {Query} context passed to setters
|
|
46
46
|
* @return {Boolean} true iff the update is non-empty
|
|
47
47
|
* @api private
|
|
@@ -205,7 +205,7 @@ function castPipelineOperator(op, val) {
|
|
|
205
205
|
* @param {Object} obj part of a query
|
|
206
206
|
* @param {String} op the atomic operator ($pull, $set, etc)
|
|
207
207
|
* @param {Object} [options]
|
|
208
|
-
* @param {Boolean|
|
|
208
|
+
* @param {Boolean|'throw'} [options.strict]
|
|
209
209
|
* @param {Query} context
|
|
210
210
|
* @param {Object} filter
|
|
211
211
|
* @param {String} pref path prefix (internal only)
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
|
|
4
4
|
const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
|
|
5
|
-
const get = require('../get');
|
|
6
5
|
const handleTimestampOption = require('../schema/handleTimestampOption');
|
|
7
6
|
const setDocumentTimestamps = require('./setDocumentTimestamps');
|
|
8
7
|
const symbols = require('../../schema/symbols');
|
|
@@ -42,14 +41,7 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
42
41
|
|
|
43
42
|
schema.add(schemaAdditions);
|
|
44
43
|
|
|
45
|
-
schema.pre('save',
|
|
46
|
-
const timestampOption = get(this, '$__.saveOptions.timestamps');
|
|
47
|
-
if (timestampOption === false) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
setDocumentTimestamps(this, timestampOption, currentTime, createdAt, updatedAt);
|
|
52
|
-
});
|
|
44
|
+
schema.pre('save', timestampsPreSave);
|
|
53
45
|
|
|
54
46
|
schema.methods.initializeTimestamps = function(timestampsOptions) {
|
|
55
47
|
if (timestampsOptions === false) {
|
|
@@ -85,8 +77,6 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
85
77
|
return this;
|
|
86
78
|
};
|
|
87
79
|
|
|
88
|
-
_setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
|
|
89
|
-
|
|
90
80
|
const opts = { query: true, model: false };
|
|
91
81
|
schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate);
|
|
92
82
|
schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate);
|
|
@@ -94,23 +84,49 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
94
84
|
schema.pre('update', opts, _setTimestampsOnUpdate);
|
|
95
85
|
schema.pre('updateOne', opts, _setTimestampsOnUpdate);
|
|
96
86
|
schema.pre('updateMany', opts, _setTimestampsOnUpdate);
|
|
87
|
+
};
|
|
97
88
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Replacing with null update should still trigger timestamps
|
|
103
|
-
if (replaceOps.has(this.op) && this.getUpdate() == null) {
|
|
104
|
-
this.setUpdate({});
|
|
105
|
-
}
|
|
106
|
-
applyTimestampsToUpdate(
|
|
107
|
-
now,
|
|
108
|
-
createdAt,
|
|
109
|
-
updatedAt,
|
|
110
|
-
this.getUpdate(),
|
|
111
|
-
this._mongooseOptions,
|
|
112
|
-
replaceOps.has(this.op)
|
|
113
|
-
);
|
|
114
|
-
applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
|
|
89
|
+
function timestampsPreSave() {
|
|
90
|
+
const timestampOption = this.$__?.saveOptions?.timestamps;
|
|
91
|
+
if (timestampOption === false) {
|
|
92
|
+
return;
|
|
115
93
|
}
|
|
116
|
-
|
|
94
|
+
|
|
95
|
+
const schema = this.$__schema;
|
|
96
|
+
const { createdAt, updatedAt } = schema.$timestamps;
|
|
97
|
+
const timestamps = schema.options.timestamps;
|
|
98
|
+
const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ?
|
|
99
|
+
timestamps.currentTime :
|
|
100
|
+
null;
|
|
101
|
+
|
|
102
|
+
setDocumentTimestamps(this, timestampOption, currentTime, createdAt, updatedAt);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function _setTimestampsOnUpdate() {
|
|
106
|
+
const schema = this.model.schema;
|
|
107
|
+
const { createdAt, updatedAt } = schema.$timestamps;
|
|
108
|
+
const timestamps = schema.options.timestamps;
|
|
109
|
+
const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ?
|
|
110
|
+
timestamps.currentTime :
|
|
111
|
+
null;
|
|
112
|
+
|
|
113
|
+
const now = currentTime != null ?
|
|
114
|
+
currentTime() :
|
|
115
|
+
this.model.base.now();
|
|
116
|
+
// Replacing with null update should still trigger timestamps
|
|
117
|
+
if (replaceOps.has(this.op) && this.getUpdate() == null) {
|
|
118
|
+
this.setUpdate({});
|
|
119
|
+
}
|
|
120
|
+
applyTimestampsToUpdate(
|
|
121
|
+
now,
|
|
122
|
+
createdAt,
|
|
123
|
+
updatedAt,
|
|
124
|
+
this.getUpdate(),
|
|
125
|
+
this._mongooseOptions,
|
|
126
|
+
replaceOps.has(this.op)
|
|
127
|
+
);
|
|
128
|
+
applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
timestampsPreSave[symbols.builtInMiddleware] = true;
|
|
132
|
+
_setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
|