@depup/mongoose 9.8.1-depup.0 → 9.9.0-depup.0
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/README.md +2 -2
- package/changes.json +1 -1
- package/lib/connection.js +1 -9
- package/lib/document.js +223 -128
- package/lib/helpers/document/applyDefaults.js +71 -7
- package/lib/helpers/model/castBulkWrite.js +2 -2
- package/lib/helpers/parallelLimit.js +41 -16
- package/lib/helpers/schema/idGetter.js +3 -0
- package/lib/helpers/timestamps/setupTimestamps.js +1 -0
- package/lib/helpers/update/applyTimestampsToUpdate.js +4 -5
- package/lib/model.js +71 -52
- package/lib/schema.js +44 -2
- package/lib/schemaType.js +4 -3
- package/lib/stateMachine.js +22 -3
- package/package.json +3 -3
- package/types/inferhydrateddoctype.d.ts +19 -3
- package/types/inferrawdoctype.d.ts +19 -3
- package/types/schemaoptions.d.ts +1 -0
- package/types/schematypes.d.ts +1 -1
|
@@ -3,39 +3,101 @@
|
|
|
3
3
|
const isNestedProjection = require('../projection/isNestedProjection');
|
|
4
4
|
|
|
5
5
|
module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip, options) {
|
|
6
|
-
const
|
|
7
|
-
const plen = paths.length;
|
|
6
|
+
const schemaPaths = doc.$__schema.paths;
|
|
8
7
|
const skipParentChangeTracking = options?.skipParentChangeTracking;
|
|
8
|
+
const skipId = doc.$__.skipId;
|
|
9
|
+
// Whether any path has a function default that must run **after** setters,
|
|
10
|
+
// so callers doing a before-setters pass can skip the after-setters pass
|
|
11
|
+
// entirely when this comes back `false`.
|
|
12
|
+
let hasPostSetterDefaults = false;
|
|
9
13
|
|
|
10
|
-
for (
|
|
14
|
+
for (const p in schemaPaths) {
|
|
11
15
|
let def;
|
|
12
16
|
let curPath = '';
|
|
13
|
-
const p = paths[i];
|
|
14
17
|
|
|
15
|
-
if (p === '_id' &&
|
|
18
|
+
if (p === '_id' && skipId) {
|
|
16
19
|
continue;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
const type =
|
|
22
|
+
const type = schemaPaths[p];
|
|
20
23
|
// `getDefault()` returns undefined when `defaultValue` is undefined, in which
|
|
21
24
|
// case this loop never modifies the doc, so skip traversing the path entirely.
|
|
22
25
|
if (type.defaultValue === undefined) {
|
|
23
26
|
continue;
|
|
24
27
|
}
|
|
28
|
+
if (typeof type.defaultValue === 'function' && !type.defaultValue.$runBeforeSetters) {
|
|
29
|
+
hasPostSetterDefaults = true;
|
|
30
|
+
}
|
|
25
31
|
const path = type.splitPath();
|
|
26
32
|
const len = path.length;
|
|
27
33
|
if (path[len - 1] === '$*') {
|
|
28
34
|
continue;
|
|
29
35
|
}
|
|
36
|
+
|
|
30
37
|
let included = false;
|
|
31
38
|
let doc_ = doc._doc;
|
|
39
|
+
|
|
40
|
+
if (len === 1) {
|
|
41
|
+
if (exclude === true) {
|
|
42
|
+
if (p in fields) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
} else if (exclude === false && fields) {
|
|
46
|
+
const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
|
|
47
|
+
if ((p in fields && !isNestedProjection(fields[p])) || (hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[p])) {
|
|
48
|
+
included = true;
|
|
49
|
+
} else if (hasIncludedChildren != null && !hasIncludedChildren[p]) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (doc_[p] !== void 0) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isBeforeSetters != null) {
|
|
59
|
+
if (typeof type.defaultValue === 'function') {
|
|
60
|
+
if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
} else if (!isBeforeSetters) {
|
|
67
|
+
// Non-function defaults should always run **before** setters
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (pathsToSkip && pathsToSkip[p]) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (fields && exclude !== null && !(exclude === true || included)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
def = type.getDefault(doc, false);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
doc.invalidate(p, err);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (typeof def !== 'undefined') {
|
|
88
|
+
doc_[p] = def;
|
|
89
|
+
applyChangeTracking(doc, p, skipParentChangeTracking);
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
32
94
|
for (let j = 0; j < len; ++j) {
|
|
33
95
|
if (doc_ == null) {
|
|
34
96
|
break;
|
|
35
97
|
}
|
|
36
98
|
|
|
37
99
|
const piece = path[j];
|
|
38
|
-
curPath
|
|
100
|
+
curPath = curPath.length ? curPath + '.' + piece : piece;
|
|
39
101
|
|
|
40
102
|
if (exclude === true) {
|
|
41
103
|
if (curPath in fields) {
|
|
@@ -123,6 +185,8 @@ module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildre
|
|
|
123
185
|
}
|
|
124
186
|
}
|
|
125
187
|
}
|
|
188
|
+
|
|
189
|
+
return hasPostSetterDefaults;
|
|
126
190
|
};
|
|
127
191
|
|
|
128
192
|
/*!
|
|
@@ -127,7 +127,7 @@ module.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne,
|
|
|
127
127
|
applyTimestampsToUpdate(now, createdAt, updatedAt, update, {
|
|
128
128
|
timestamps: updateOne.timestamps,
|
|
129
129
|
overwriteImmutable: updateOne.overwriteImmutable
|
|
130
|
-
});
|
|
130
|
+
}, updateOne.upsert);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
if (doInitTimestamps) {
|
|
@@ -194,7 +194,7 @@ module.exports.castUpdateMany = function castUpdateMany(originalModel, updateMan
|
|
|
194
194
|
applyTimestampsToUpdate(now, createdAt, updatedAt, updateMany['update'], {
|
|
195
195
|
timestamps: updateMany.timestamps,
|
|
196
196
|
overwriteImmutable: updateMany.overwriteImmutable
|
|
197
|
-
});
|
|
197
|
+
}, updateMany.upsert);
|
|
198
198
|
}
|
|
199
199
|
if (doInitTimestamps) {
|
|
200
200
|
applyTimestampsToChildren(now, updateMany['update'], model.schema);
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module.exports = parallelLimit;
|
|
4
4
|
|
|
5
5
|
/*!
|
|
6
|
-
*
|
|
6
|
+
* Run `fn` over every entry of `params` with at most `limit` calls in flight at
|
|
7
|
+
* a time, resolving to the results in input order. Currently only used by
|
|
8
|
+
* `Model.insertMany()` to bound how many documents validate concurrently.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
async function parallelLimit(params, fn, limit) {
|
|
@@ -11,27 +13,50 @@ async function parallelLimit(params, fn, limit) {
|
|
|
11
13
|
throw new Error('Limit must be positive');
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
const length = params.length;
|
|
17
|
+
if (length === 0) {
|
|
15
18
|
return [];
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
const results =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const results = new Array(length);
|
|
22
|
+
// Fast path: there's nothing to bound, so kick everything off and wait once.
|
|
23
|
+
// Avoids the per-task `Set` + `Promise.race` bookkeeping of a sliding window.
|
|
24
|
+
if (limit >= length) {
|
|
25
|
+
for (let i = 0; i < length; ++i) {
|
|
26
|
+
results[i] = fn(params[i], i);
|
|
27
|
+
}
|
|
28
|
+
return Promise.all(results);
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
let nextIndex = 0;
|
|
32
|
+
let firstError = null;
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
function worker() {
|
|
35
|
+
if (nextIndex >= length || firstError !== null) {
|
|
36
|
+
return undefined;
|
|
33
37
|
}
|
|
38
|
+
const index = nextIndex++;
|
|
39
|
+
return Promise.resolve(fn(params[index], index)).then(
|
|
40
|
+
(val) => {
|
|
41
|
+
results[index] = val;
|
|
42
|
+
return worker();
|
|
43
|
+
},
|
|
44
|
+
(err) => {
|
|
45
|
+
if (firstError === null) {
|
|
46
|
+
firstError = err;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
);
|
|
34
50
|
}
|
|
35
51
|
|
|
36
|
-
|
|
52
|
+
const workers = new Array(Math.min(limit, length));
|
|
53
|
+
for (let i = 0; i < workers.length; ++i) {
|
|
54
|
+
workers[i] = worker();
|
|
55
|
+
}
|
|
56
|
+
await Promise.all(workers);
|
|
57
|
+
|
|
58
|
+
if (firstError !== null) {
|
|
59
|
+
throw firstError;
|
|
60
|
+
}
|
|
61
|
+
return results;
|
|
37
62
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* ignore
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const get = require('../get');
|
|
8
7
|
const utils = require('../../utils');
|
|
9
8
|
|
|
10
9
|
module.exports = applyTimestampsToUpdate;
|
|
@@ -13,10 +12,10 @@ module.exports = applyTimestampsToUpdate;
|
|
|
13
12
|
* ignore
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
|
-
function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options, isReplace) {
|
|
15
|
+
function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options, upsert, isReplace) {
|
|
17
16
|
const updates = currentUpdate;
|
|
18
17
|
let _updates = updates;
|
|
19
|
-
const timestamps =
|
|
18
|
+
const timestamps = options?.timestamps ?? true;
|
|
20
19
|
|
|
21
20
|
// Support skipping timestamps at the query level, see gh-6980
|
|
22
21
|
if (!timestamps || updates == null) {
|
|
@@ -81,7 +80,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
|
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
if (!skipCreatedAt && createdAt) {
|
|
84
|
-
const overwriteImmutable =
|
|
83
|
+
const overwriteImmutable = options?.overwriteImmutable;
|
|
85
84
|
const hasUserCreatedAt = currentUpdate[createdAt] != null
|
|
86
85
|
|| currentUpdate.$set?.[createdAt] != null
|
|
87
86
|
|| currentUpdate.$setOnInsert?.[createdAt] != null;
|
|
@@ -119,7 +118,7 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
|
|
|
119
118
|
}
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
if (!timestampSet) {
|
|
121
|
+
if (!timestampSet && upsert) {
|
|
123
122
|
updates.$setOnInsert = updates.$setOnInsert || {};
|
|
124
123
|
updates.$setOnInsert[createdAt] = now;
|
|
125
124
|
}
|
package/lib/model.js
CHANGED
|
@@ -413,8 +413,9 @@ Model.prototype.$__save = async function $__save(options) {
|
|
|
413
413
|
const saveOptions = _createSaveOptions(this, options);
|
|
414
414
|
|
|
415
415
|
if (this.$isNew) {
|
|
416
|
+
const hasOnlyPrimitiveValues = this.$__hasOnlyPrimitiveValues();
|
|
416
417
|
// send entire doc
|
|
417
|
-
const obj =
|
|
418
|
+
const obj = hasOnlyPrimitiveValues ?
|
|
418
419
|
this.$__toObjectShallow() :
|
|
419
420
|
this.toObject(saveToObjectOptions);
|
|
420
421
|
if ((obj || {})._id === void 0) {
|
|
@@ -428,11 +429,11 @@ Model.prototype.$__save = async function $__save(options) {
|
|
|
428
429
|
|
|
429
430
|
this.$__version(true, obj);
|
|
430
431
|
this.$__reset();
|
|
431
|
-
_setIsNew(this, false);
|
|
432
|
+
_setIsNew(this, false, hasOnlyPrimitiveValues);
|
|
432
433
|
// Make it possible to retry the insert
|
|
433
434
|
this.$__.inserting = true;
|
|
434
435
|
result = await this[modelCollectionSymbol].insertOne(obj, saveOptions).catch(err => {
|
|
435
|
-
_setIsNew(this, true);
|
|
436
|
+
_setIsNew(this, true, hasOnlyPrimitiveValues);
|
|
436
437
|
throw err;
|
|
437
438
|
});
|
|
438
439
|
} else {
|
|
@@ -3044,23 +3045,26 @@ Model.insertMany = async function insertMany(arr, options) {
|
|
|
3044
3045
|
|
|
3045
3046
|
async function _insertMany(arr, options) {
|
|
3046
3047
|
options = options || {};
|
|
3047
|
-
const
|
|
3048
|
-
const
|
|
3048
|
+
const hasInsertManyHooks = this._middleware.hasHooks('insertMany');
|
|
3049
|
+
const preFilter = hasInsertManyHooks ? buildMiddlewareFilter(options, 'pre') : null;
|
|
3050
|
+
const postFilter = hasInsertManyHooks ? buildMiddlewareFilter(options, 'post') : null;
|
|
3049
3051
|
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3052
|
+
if (hasInsertManyHooks) {
|
|
3053
|
+
try {
|
|
3054
|
+
[arr] = await this._middleware.execPre('insertMany', this, [arr], { filter: preFilter });
|
|
3055
|
+
} catch (error) {
|
|
3056
|
+
await this._middleware.execPost('insertMany', this, [arr], { error, filter: postFilter });
|
|
3057
|
+
}
|
|
3054
3058
|
}
|
|
3055
3059
|
const ThisModel = this;
|
|
3056
3060
|
const limit = options.limit || 1000;
|
|
3057
3061
|
const rawResult = !!options.rawResult;
|
|
3058
|
-
const ordered =
|
|
3059
|
-
const throwOnValidationError =
|
|
3062
|
+
const ordered = options.ordered == null ? true : !!options.ordered;
|
|
3063
|
+
const throwOnValidationError = !!options.throwOnValidationError;
|
|
3060
3064
|
const lean = !!options.lean;
|
|
3061
3065
|
|
|
3062
3066
|
const asyncLocalStorage = this.db.base.transactionAsyncLocalStorage?.getStore();
|
|
3063
|
-
if (
|
|
3067
|
+
if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) {
|
|
3064
3068
|
options = { ...options, session: asyncLocalStorage.session };
|
|
3065
3069
|
}
|
|
3066
3070
|
|
|
@@ -3069,9 +3073,9 @@ async function _insertMany(arr, options) {
|
|
|
3069
3073
|
}
|
|
3070
3074
|
|
|
3071
3075
|
const validationErrors = [];
|
|
3072
|
-
const validationErrorsToOriginalOrder = new Map();
|
|
3073
3076
|
const results = ordered ? null : new Array(arr.length);
|
|
3074
|
-
|
|
3077
|
+
const session = options.session;
|
|
3078
|
+
function validateDoc(doc, index) {
|
|
3075
3079
|
// If option `lean` is set to true bypass validation and hydration
|
|
3076
3080
|
if (lean) {
|
|
3077
3081
|
return doc;
|
|
@@ -3079,14 +3083,14 @@ async function _insertMany(arr, options) {
|
|
|
3079
3083
|
let createdNewDoc = false;
|
|
3080
3084
|
if (!(doc instanceof ThisModel)) {
|
|
3081
3085
|
if (doc != null && typeof doc !== 'object') {
|
|
3082
|
-
|
|
3086
|
+
return Promise.reject(new ObjectParameterError(doc, 'arr.' + index, 'insertMany'));
|
|
3083
3087
|
}
|
|
3084
3088
|
doc = new ThisModel(doc);
|
|
3085
3089
|
createdNewDoc = true;
|
|
3086
3090
|
}
|
|
3087
3091
|
|
|
3088
|
-
if (
|
|
3089
|
-
doc.$session(
|
|
3092
|
+
if (session != null) {
|
|
3093
|
+
doc.$session(session);
|
|
3090
3094
|
}
|
|
3091
3095
|
return doc.$validate(createdNewDoc ? { _skipParallelValidateCheck: true } : null)
|
|
3092
3096
|
.then(() => doc)
|
|
@@ -3094,7 +3098,6 @@ async function _insertMany(arr, options) {
|
|
|
3094
3098
|
if (ordered === false) {
|
|
3095
3099
|
error.index = index;
|
|
3096
3100
|
validationErrors.push(error);
|
|
3097
|
-
validationErrorsToOriginalOrder.set(error, index);
|
|
3098
3101
|
results[index] = error;
|
|
3099
3102
|
return;
|
|
3100
3103
|
}
|
|
@@ -3104,27 +3107,42 @@ async function _insertMany(arr, options) {
|
|
|
3104
3107
|
|
|
3105
3108
|
const docs = await parallelLimit(arr, validateDoc, limit);
|
|
3106
3109
|
|
|
3107
|
-
const
|
|
3108
|
-
const
|
|
3110
|
+
const validDocIndexToOriginalIndex = {};
|
|
3111
|
+
const docAttributes = [];
|
|
3112
|
+
const docObjects = lean ? docAttributes : [];
|
|
3113
|
+
const versionKeyPath = ThisModel.schema.options.versionKey
|
|
3114
|
+
? ThisModel.schema.paths[ThisModel.schema.options.versionKey]?.splitPath()
|
|
3115
|
+
: null;
|
|
3116
|
+
const timestamps = options.timestamps;
|
|
3109
3117
|
for (let i = 0; i < docs.length; ++i) {
|
|
3110
|
-
|
|
3111
|
-
|
|
3118
|
+
const doc = docs[i];
|
|
3119
|
+
if (doc == null) {
|
|
3120
|
+
continue;
|
|
3121
|
+
}
|
|
3122
|
+
validDocIndexToOriginalIndex[docAttributes.length] = i;
|
|
3123
|
+
docAttributes.push(doc);
|
|
3112
3124
|
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3125
|
+
// In lean mode `docObjects === docAttributes`, so the push above is enough.
|
|
3126
|
+
if (lean) {
|
|
3127
|
+
continue;
|
|
3128
|
+
}
|
|
3129
|
+
if (versionKeyPath) {
|
|
3130
|
+
doc.$__setValue(versionKeyPath, 0);
|
|
3131
|
+
}
|
|
3132
|
+
const shouldSetTimestamps = timestamps !== false && doc.initializeTimestamps && (!doc.$__ || doc.$__.timestamps !== false);
|
|
3133
|
+
if (shouldSetTimestamps) {
|
|
3134
|
+
doc.initializeTimestamps(timestamps);
|
|
3135
|
+
}
|
|
3136
|
+
docObjects.push(doc.$__hasOnlyPrimitiveValues() ? doc.$__toObjectShallow() : doc.toObject(internalToObjectOptions));
|
|
3119
3137
|
}
|
|
3120
3138
|
|
|
3121
3139
|
// Make sure validation errors are in the same order as the
|
|
3122
3140
|
// original documents, so if both doc1 and doc2 both fail validation,
|
|
3123
3141
|
// `Model.insertMany([doc1, doc2])` will always have doc1's validation
|
|
3124
3142
|
// error before doc2's. Re: gh-12791.
|
|
3125
|
-
if (validationErrors.length > 0) {
|
|
3143
|
+
if (validationErrors.length > 0 && ordered === false) {
|
|
3126
3144
|
validationErrors.sort((err1, err2) => {
|
|
3127
|
-
return
|
|
3145
|
+
return err1.index - err2.index;
|
|
3128
3146
|
});
|
|
3129
3147
|
}
|
|
3130
3148
|
|
|
@@ -3149,19 +3167,6 @@ async function _insertMany(arr, options) {
|
|
|
3149
3167
|
}
|
|
3150
3168
|
return [];
|
|
3151
3169
|
}
|
|
3152
|
-
const docObjects = lean ? docAttributes : docAttributes.map(function(doc) {
|
|
3153
|
-
if (doc.$__schema.options.versionKey) {
|
|
3154
|
-
doc[doc.$__schema.options.versionKey] = 0;
|
|
3155
|
-
}
|
|
3156
|
-
const shouldSetTimestamps = options?.timestamps !== false && doc.initializeTimestamps && (!doc.$__ || doc.$__.timestamps !== false);
|
|
3157
|
-
if (shouldSetTimestamps) {
|
|
3158
|
-
doc.initializeTimestamps(options?.timestamps);
|
|
3159
|
-
}
|
|
3160
|
-
if (doc.$__hasOnlyPrimitiveValues()) {
|
|
3161
|
-
return doc.$__toObjectShallow();
|
|
3162
|
-
}
|
|
3163
|
-
return doc.toObject(internalToObjectOptions);
|
|
3164
|
-
});
|
|
3165
3170
|
|
|
3166
3171
|
let res;
|
|
3167
3172
|
try {
|
|
@@ -3180,7 +3185,7 @@ async function _insertMany(arr, options) {
|
|
|
3180
3185
|
|
|
3181
3186
|
if (error.writeErrors != null) {
|
|
3182
3187
|
for (let i = 0; i < error.writeErrors.length; ++i) {
|
|
3183
|
-
const originalIndex = validDocIndexToOriginalIndex
|
|
3188
|
+
const originalIndex = validDocIndexToOriginalIndex[error.writeErrors[i].index];
|
|
3184
3189
|
error.writeErrors[i] = { ...error.writeErrors[i], index: originalIndex };
|
|
3185
3190
|
if (!ordered) {
|
|
3186
3191
|
results[originalIndex] = error.writeErrors[i];
|
|
@@ -3219,7 +3224,7 @@ async function _insertMany(arr, options) {
|
|
|
3219
3224
|
if (lean) {
|
|
3220
3225
|
return doc;
|
|
3221
3226
|
}
|
|
3222
|
-
doc.$__reset();
|
|
3227
|
+
doc.$__reset(true);
|
|
3223
3228
|
_setIsNew(doc, false);
|
|
3224
3229
|
return doc;
|
|
3225
3230
|
});
|
|
@@ -3228,13 +3233,18 @@ async function _insertMany(arr, options) {
|
|
|
3228
3233
|
decorateBulkWriteResult(error, validationErrors, results);
|
|
3229
3234
|
}
|
|
3230
3235
|
|
|
3231
|
-
|
|
3236
|
+
if (hasInsertManyHooks) {
|
|
3237
|
+
await this._middleware.execPost('insertMany', this, [arr], { error, filter: postFilter });
|
|
3238
|
+
} else {
|
|
3239
|
+
throw error;
|
|
3240
|
+
}
|
|
3232
3241
|
}
|
|
3233
3242
|
|
|
3234
3243
|
if (!lean) {
|
|
3235
3244
|
for (const attribute of docAttributes) {
|
|
3236
|
-
attribute.$
|
|
3237
|
-
|
|
3245
|
+
const hasOnlyPrimitiveValues = attribute.$__hasOnlyPrimitiveValues();
|
|
3246
|
+
attribute.$__reset(true, hasOnlyPrimitiveValues);
|
|
3247
|
+
_setIsNew(attribute, false, hasOnlyPrimitiveValues);
|
|
3238
3248
|
}
|
|
3239
3249
|
}
|
|
3240
3250
|
|
|
@@ -3276,19 +3286,28 @@ async function _insertMany(arr, options) {
|
|
|
3276
3286
|
});
|
|
3277
3287
|
}
|
|
3278
3288
|
|
|
3279
|
-
|
|
3280
|
-
|
|
3289
|
+
if (hasInsertManyHooks) {
|
|
3290
|
+
const [result] = await this._middleware.execPost('insertMany', this, [docAttributes], { filter: postFilter });
|
|
3291
|
+
return result;
|
|
3292
|
+
}
|
|
3293
|
+
return docAttributes;
|
|
3281
3294
|
}
|
|
3282
3295
|
|
|
3283
3296
|
/*!
|
|
3284
|
-
*
|
|
3297
|
+
* @param {Document} doc The document to set `$isNew` on.
|
|
3298
|
+
* @param {boolean} val The value to set `$isNew` to.
|
|
3299
|
+
* @param {boolean} [hasOnlyPrimitiveValues] Skip getting subdocs for performance if we know there are none.
|
|
3300
|
+
* @api private
|
|
3285
3301
|
*/
|
|
3286
3302
|
|
|
3287
|
-
function _setIsNew(doc, val) {
|
|
3303
|
+
function _setIsNew(doc, val, hasOnlyPrimitiveValues) {
|
|
3288
3304
|
doc.$isNew = val;
|
|
3289
3305
|
doc.$emit('isNew', val);
|
|
3290
3306
|
doc.constructor.emit('isNew', val);
|
|
3291
3307
|
|
|
3308
|
+
if (hasOnlyPrimitiveValues) {
|
|
3309
|
+
return;
|
|
3310
|
+
}
|
|
3292
3311
|
const subdocs = doc.$getAllSubdocs({ useCache: true });
|
|
3293
3312
|
for (const subdoc of subdocs) {
|
|
3294
3313
|
subdoc.$isNew = val;
|
package/lib/schema.js
CHANGED
|
@@ -727,7 +727,7 @@ Schema.prototype._getDocumentMiddleware = function _getDocumentMiddleware() {
|
|
|
727
727
|
|
|
728
728
|
Schema.prototype._defaultToObjectOptions = function(json) {
|
|
729
729
|
const path = json ? 'toJSON' : 'toObject';
|
|
730
|
-
if (this._defaultToObjectOptionsMap && this._defaultToObjectOptionsMap
|
|
730
|
+
if (this._defaultToObjectOptionsMap && path in this._defaultToObjectOptionsMap) {
|
|
731
731
|
return this._defaultToObjectOptionsMap[path];
|
|
732
732
|
}
|
|
733
733
|
|
|
@@ -738,7 +738,7 @@ Schema.prototype._defaultToObjectOptions = function(json) {
|
|
|
738
738
|
const defaultOptions = Object.assign({}, baseOptions, schemaOptions);
|
|
739
739
|
|
|
740
740
|
this._defaultToObjectOptionsMap = this._defaultToObjectOptionsMap || {};
|
|
741
|
-
this._defaultToObjectOptionsMap[path] = defaultOptions;
|
|
741
|
+
this._defaultToObjectOptionsMap[path] = utils.hasOwnKeys(defaultOptions) ? defaultOptions : null;
|
|
742
742
|
return defaultOptions;
|
|
743
743
|
};
|
|
744
744
|
|
|
@@ -1870,6 +1870,46 @@ Schema.prototype.requiredPaths = function requiredPaths(invalidate) {
|
|
|
1870
1870
|
return this._requiredpaths;
|
|
1871
1871
|
};
|
|
1872
1872
|
|
|
1873
|
+
/**
|
|
1874
|
+
* Returns an Array of path strings that have a `transform` option, either on the
|
|
1875
|
+
* path itself or on the path's `embeddedSchemaType` for arrays.
|
|
1876
|
+
*
|
|
1877
|
+
* #### Example:
|
|
1878
|
+
*
|
|
1879
|
+
* const s = new Schema({
|
|
1880
|
+
* name: { type: String, transform: v => v.toLowerCase() },
|
|
1881
|
+
* tags: [{ type: String, transform: v => v.trim() }],
|
|
1882
|
+
* age: { type: Number, required: true },
|
|
1883
|
+
* notes: String
|
|
1884
|
+
* });
|
|
1885
|
+
* s.pathsWithTransforms(); // [ 'name', 'tags' ]
|
|
1886
|
+
*
|
|
1887
|
+
* @api private
|
|
1888
|
+
* @param {boolean} invalidate Refresh the cache
|
|
1889
|
+
* @return {Array|null}
|
|
1890
|
+
*/
|
|
1891
|
+
|
|
1892
|
+
Schema.prototype.pathsWithTransforms = function pathsWithTransforms(invalidate) {
|
|
1893
|
+
if (this._pathsWithTransforms !== undefined && !invalidate) {
|
|
1894
|
+
return this._pathsWithTransforms;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
const paths = Object.keys(this.paths);
|
|
1898
|
+
const ret = [];
|
|
1899
|
+
|
|
1900
|
+
for (const path of paths) {
|
|
1901
|
+
const schematype = this.paths[path];
|
|
1902
|
+
const topLevelTransformFunction = schematype.options.transform ?? schematype.constructor?.defaultOptions?.transform;
|
|
1903
|
+
const embeddedSchemaTypeTransformFunction = schematype.embeddedSchemaType?.options?.transform
|
|
1904
|
+
?? schematype.embeddedSchemaType?.constructor?.defaultOptions?.transform;
|
|
1905
|
+
if (topLevelTransformFunction || embeddedSchemaTypeTransformFunction) {
|
|
1906
|
+
ret.push(path);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
this._pathsWithTransforms = ret.length ? ret : null;
|
|
1910
|
+
return this._pathsWithTransforms;
|
|
1911
|
+
};
|
|
1912
|
+
|
|
1873
1913
|
/**
|
|
1874
1914
|
* Returns indexes from fields and schema-level indexes (cached).
|
|
1875
1915
|
*
|
|
@@ -3097,6 +3137,8 @@ function isArrayFilter(piece) {
|
|
|
3097
3137
|
Schema.prototype._preCompile = function _preCompile() {
|
|
3098
3138
|
this.plugin(idGetter, { deduplicate: true });
|
|
3099
3139
|
_precomputeOptimisticConcurrency(this);
|
|
3140
|
+
// Cache paths that have transforms
|
|
3141
|
+
this.pathsWithTransforms();
|
|
3100
3142
|
};
|
|
3101
3143
|
|
|
3102
3144
|
/*!
|
package/lib/schemaType.js
CHANGED
|
@@ -1158,7 +1158,7 @@ SchemaType.prototype.required = function(required, message) {
|
|
|
1158
1158
|
return true;
|
|
1159
1159
|
}
|
|
1160
1160
|
|
|
1161
|
-
// `$cachedRequired` gets set in `
|
|
1161
|
+
// `$cachedRequired` gets set in `_getPathsToValidate()` so we
|
|
1162
1162
|
// don't call required functions multiple times in one validate call
|
|
1163
1163
|
// See gh-6801
|
|
1164
1164
|
if (cachedRequired != null && _this.path in cachedRequired) {
|
|
@@ -1292,7 +1292,8 @@ SchemaType.prototype.getDefault = function getDefault(parentDoc, init, options)
|
|
|
1292
1292
|
if (
|
|
1293
1293
|
this.defaultValue === Date.now ||
|
|
1294
1294
|
this.defaultValue === Array ||
|
|
1295
|
-
this.defaultValue.name
|
|
1295
|
+
this.defaultValue.name === 'ObjectId' ||
|
|
1296
|
+
this.defaultValue.name === 'ObjectID'
|
|
1296
1297
|
) {
|
|
1297
1298
|
ret = this.defaultValue.call(context);
|
|
1298
1299
|
} else {
|
|
@@ -1303,7 +1304,7 @@ SchemaType.prototype.getDefault = function getDefault(parentDoc, init, options)
|
|
|
1303
1304
|
}
|
|
1304
1305
|
|
|
1305
1306
|
if (ret != null) {
|
|
1306
|
-
if (typeof ret === 'object' && !this.options?.shared) {
|
|
1307
|
+
if (typeof ret === 'object' && ret != null && ret._bsontype !== 'ObjectId' && !this.options?.shared) {
|
|
1307
1308
|
ret = clone(ret);
|
|
1308
1309
|
}
|
|
1309
1310
|
|
package/lib/stateMachine.js
CHANGED
|
@@ -34,7 +34,6 @@ StateMachine.ctor = function() {
|
|
|
34
34
|
const states = [...arguments];
|
|
35
35
|
|
|
36
36
|
const ctor = function() {
|
|
37
|
-
StateMachine.apply(this, arguments);
|
|
38
37
|
this.paths = {};
|
|
39
38
|
this.states = {};
|
|
40
39
|
};
|
|
@@ -79,6 +78,28 @@ StateMachine.prototype._changeState = function _changeState(path, nextState) {
|
|
|
79
78
|
this.states[nextState][path] = true;
|
|
80
79
|
};
|
|
81
80
|
|
|
81
|
+
/*!
|
|
82
|
+
* ignore
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
StateMachine.prototype.clearAllExcept = function clearAllExcept(state) {
|
|
86
|
+
// State buckets are created lazily, so `states[state]` may not exist yet.
|
|
87
|
+
const bucket = this.states[state];
|
|
88
|
+
const keys = bucket == null ? [] : Object.keys(bucket);
|
|
89
|
+
if (keys.length === 0) {
|
|
90
|
+
this.paths = {};
|
|
91
|
+
this.states = {};
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.paths = {};
|
|
95
|
+
for (const path of keys) {
|
|
96
|
+
this.paths[path] = state;
|
|
97
|
+
}
|
|
98
|
+
this.states = {
|
|
99
|
+
[state]: this.states[state]
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
82
103
|
/*!
|
|
83
104
|
* ignore
|
|
84
105
|
*/
|
|
@@ -91,8 +112,6 @@ StateMachine.prototype.clear = function clear(state) {
|
|
|
91
112
|
if (keys.length === 0) {
|
|
92
113
|
return;
|
|
93
114
|
}
|
|
94
|
-
// Replace the bucket rather than deleting each key: repeated `delete` puts
|
|
95
|
-
// the object in dictionary mode, which is significantly slower.
|
|
96
115
|
this.states[state] = {};
|
|
97
116
|
let i = keys.length;
|
|
98
117
|
|