mongoose 6.4.7 → 6.5.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/lib/connection.js +22 -2
- package/lib/cursor/ChangeStream.js +39 -1
- package/lib/document.js +118 -148
- package/lib/error/validation.js +9 -0
- package/lib/helpers/document/applyDefaults.js +115 -0
- package/lib/helpers/document/cleanModifiedSubpaths.js +3 -3
- package/lib/helpers/document/compile.js +7 -6
- package/lib/helpers/firstKey.js +8 -0
- package/lib/helpers/model/applyDefaultsToPOJO.js +52 -0
- package/lib/helpers/model/castBulkWrite.js +1 -1
- package/lib/helpers/model/pushNestedArrayPaths.js +15 -0
- package/lib/internal.js +3 -1
- package/lib/model.js +200 -84
- package/lib/options/SchemaArrayOptions.js +19 -0
- package/lib/plugins/trackTransaction.js +2 -2
- package/lib/query.js +3 -0
- package/lib/schema/array.js +2 -1
- package/lib/schema/documentarray.js +12 -1
- package/lib/schema.js +20 -2
- package/lib/schematype.js +5 -1
- package/lib/statemachine.js +23 -9
- package/lib/utils.js +8 -0
- package/lib/validoptions.js +1 -0
- package/package.json +3 -3
- package/types/document.d.ts +7 -0
- package/types/index.d.ts +3 -2
- package/types/inferschematype.d.ts +3 -20
- package/types/models.d.ts +50 -46
- package/types/mongooseoptions.d.ts +6 -0
- package/types/schemaoptions.d.ts +15 -4
- package/types/utility.d.ts +19 -0
- package/types/virtuals.d.ts +14 -0
|
@@ -13,7 +13,7 @@ module.exports = function cleanModifiedSubpaths(doc, path, options) {
|
|
|
13
13
|
return deleted;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
for (const modifiedPath of Object.keys(doc.$__.activePaths.
|
|
16
|
+
for (const modifiedPath of Object.keys(doc.$__.activePaths.getStatePaths('modify'))) {
|
|
17
17
|
if (skipDocArrays) {
|
|
18
18
|
const schemaType = doc.$__schema.path(modifiedPath);
|
|
19
19
|
if (schemaType && schemaType.$isMongooseDocumentArray) {
|
|
@@ -21,13 +21,13 @@ module.exports = function cleanModifiedSubpaths(doc, path, options) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
if (modifiedPath.startsWith(path + '.')) {
|
|
24
|
-
|
|
24
|
+
doc.$__.activePaths.clearPath(modifiedPath);
|
|
25
25
|
++deleted;
|
|
26
26
|
|
|
27
27
|
if (doc.$isSubdocument) {
|
|
28
28
|
const owner = doc.ownerDocument();
|
|
29
29
|
const fullPath = doc.$__fullPath(modifiedPath);
|
|
30
|
-
|
|
30
|
+
owner.$__.activePaths.clearPath(fullPath);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -17,6 +17,13 @@ const isPOJO = utils.isPOJO;
|
|
|
17
17
|
exports.compile = compile;
|
|
18
18
|
exports.defineKey = defineKey;
|
|
19
19
|
|
|
20
|
+
const _isEmptyOptions = Object.freeze({
|
|
21
|
+
minimize: true,
|
|
22
|
+
virtuals: false,
|
|
23
|
+
getters: false,
|
|
24
|
+
transform: false
|
|
25
|
+
});
|
|
26
|
+
|
|
20
27
|
/*!
|
|
21
28
|
* Compiles schemas.
|
|
22
29
|
*/
|
|
@@ -130,12 +137,6 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
|
|
|
130
137
|
value: true
|
|
131
138
|
});
|
|
132
139
|
|
|
133
|
-
const _isEmptyOptions = Object.freeze({
|
|
134
|
-
minimize: true,
|
|
135
|
-
virtuals: false,
|
|
136
|
-
getters: false,
|
|
137
|
-
transform: false
|
|
138
|
-
});
|
|
139
140
|
Object.defineProperty(nested, '$isEmpty', {
|
|
140
141
|
enumerable: false,
|
|
141
142
|
configurable: true,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = function applyDefaultsToPOJO(doc, schema) {
|
|
4
|
+
const paths = Object.keys(schema.paths);
|
|
5
|
+
const plen = paths.length;
|
|
6
|
+
|
|
7
|
+
for (let i = 0; i < plen; ++i) {
|
|
8
|
+
let curPath = '';
|
|
9
|
+
const p = paths[i];
|
|
10
|
+
|
|
11
|
+
const type = schema.paths[p];
|
|
12
|
+
const path = type.splitPath();
|
|
13
|
+
const len = path.length;
|
|
14
|
+
let doc_ = doc;
|
|
15
|
+
for (let j = 0; j < len; ++j) {
|
|
16
|
+
if (doc_ == null) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const piece = path[j];
|
|
21
|
+
curPath += (!curPath.length ? '' : '.') + piece;
|
|
22
|
+
|
|
23
|
+
if (j === len - 1) {
|
|
24
|
+
if (typeof doc_[piece] !== 'undefined') {
|
|
25
|
+
if (type.$isSingleNested) {
|
|
26
|
+
applyDefaultsToPOJO(doc_[piece], type.caster.schema);
|
|
27
|
+
} else if (type.$isMongooseDocumentArray && Array.isArray(doc_[piece])) {
|
|
28
|
+
doc_[piece].forEach(el => applyDefaultsToPOJO(el, type.schema));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const def = type.getDefault(doc, false, { skipCast: true });
|
|
35
|
+
if (typeof def !== 'undefined') {
|
|
36
|
+
doc_[piece] = def;
|
|
37
|
+
|
|
38
|
+
if (type.$isSingleNested) {
|
|
39
|
+
applyDefaultsToPOJO(def, type.caster.schema);
|
|
40
|
+
} else if (type.$isMongooseDocumentArray && Array.isArray(def)) {
|
|
41
|
+
def.forEach(el => applyDefaultsToPOJO(el, type.schema));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
if (doc_[piece] == null) {
|
|
46
|
+
doc_[piece] = {};
|
|
47
|
+
}
|
|
48
|
+
doc_ = doc_[piece];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -20,7 +20,7 @@ module.exports = function castBulkWrite(originalModel, op, options) {
|
|
|
20
20
|
const model = decideModelByObject(originalModel, op['insertOne']['document']);
|
|
21
21
|
|
|
22
22
|
const doc = new model(op['insertOne']['document']);
|
|
23
|
-
if (model.schema.options.timestamps) {
|
|
23
|
+
if (model.schema.options.timestamps && options.timestamps !== false) {
|
|
24
24
|
doc.initializeTimestamps();
|
|
25
25
|
}
|
|
26
26
|
if (options.session != null) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = function pushNestedArrayPaths(paths, nestedArray, path) {
|
|
4
|
+
if (nestedArray == null) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
for (let i = 0; i < nestedArray.length; ++i) {
|
|
9
|
+
if (Array.isArray(nestedArray[i])) {
|
|
10
|
+
pushNestedArrayPaths(paths, nestedArray[i], path + '.' + i);
|
|
11
|
+
} else {
|
|
12
|
+
paths.push(path + '.' + i);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
package/lib/internal.js
CHANGED
|
@@ -13,8 +13,9 @@ function InternalCache() {
|
|
|
13
13
|
this.activePaths = new ActiveRoster();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
InternalCache.prototype.strictMode = true;
|
|
17
|
+
|
|
16
18
|
InternalCache.prototype.fullPath = undefined;
|
|
17
|
-
InternalCache.prototype.strictMode = undefined;
|
|
18
19
|
InternalCache.prototype.selected = undefined;
|
|
19
20
|
InternalCache.prototype.shardval = undefined;
|
|
20
21
|
InternalCache.prototype.saveError = undefined;
|
|
@@ -28,6 +29,7 @@ InternalCache.prototype._id = undefined;
|
|
|
28
29
|
InternalCache.prototype.ownerDocument = undefined;
|
|
29
30
|
InternalCache.prototype.populate = undefined; // what we want to populate in this doc
|
|
30
31
|
InternalCache.prototype.populated = undefined;// the _ids that have been populated
|
|
32
|
+
InternalCache.prototype.primitiveAtomics = undefined;
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
35
|
* If `false`, this document was not the result of population.
|
package/lib/model.js
CHANGED
|
@@ -22,6 +22,8 @@ const ServerSelectionError = require('./error/serverSelection');
|
|
|
22
22
|
const ValidationError = require('./error/validation');
|
|
23
23
|
const VersionError = require('./error/version');
|
|
24
24
|
const ParallelSaveError = require('./error/parallelSave');
|
|
25
|
+
const applyDefaultsHelper = require('./helpers/document/applyDefaults');
|
|
26
|
+
const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO');
|
|
25
27
|
const applyQueryMiddleware = require('./helpers/query/applyQueryMiddleware');
|
|
26
28
|
const applyHooks = require('./helpers/model/applyHooks');
|
|
27
29
|
const applyMethods = require('./helpers/model/applyMethods');
|
|
@@ -35,6 +37,7 @@ const castBulkWrite = require('./helpers/model/castBulkWrite');
|
|
|
35
37
|
const createPopulateQueryFilter = require('./helpers/populate/createPopulateQueryFilter');
|
|
36
38
|
const getDefaultBulkwriteResult = require('./helpers/getDefaultBulkwriteResult');
|
|
37
39
|
const discriminator = require('./helpers/model/discriminator');
|
|
40
|
+
const firstKey = require('./helpers/firstKey');
|
|
38
41
|
const each = require('./helpers/each');
|
|
39
42
|
const get = require('./helpers/get');
|
|
40
43
|
const getConstructorName = require('./helpers/getConstructorName');
|
|
@@ -54,6 +57,7 @@ const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
|
|
|
54
57
|
const modifiedPaths = require('./helpers/update/modifiedPaths');
|
|
55
58
|
const parallelLimit = require('./helpers/parallelLimit');
|
|
56
59
|
const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
|
|
60
|
+
const pushNestedArrayPaths = require('./helpers/model/pushNestedArrayPaths');
|
|
57
61
|
const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
|
|
58
62
|
const setDottedPath = require('./helpers/path/setDottedPath');
|
|
59
63
|
const util = require('util');
|
|
@@ -589,6 +593,7 @@ function operand(self, where, delta, data, val, op) {
|
|
|
589
593
|
case '$pullAll':
|
|
590
594
|
case '$push':
|
|
591
595
|
case '$addToSet':
|
|
596
|
+
case '$inc':
|
|
592
597
|
break;
|
|
593
598
|
default:
|
|
594
599
|
// nothing to do
|
|
@@ -764,15 +769,21 @@ Model.prototype.$__delta = function() {
|
|
|
764
769
|
value = value.toObject();
|
|
765
770
|
operand(this, where, delta, data, value);
|
|
766
771
|
} else {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
772
|
+
if (this.$__.primitiveAtomics && this.$__.primitiveAtomics[data.path] != null) {
|
|
773
|
+
const val = this.$__.primitiveAtomics[data.path];
|
|
774
|
+
const op = firstKey(val);
|
|
775
|
+
operand(this, where, delta, data, val[op], op);
|
|
776
|
+
} else {
|
|
777
|
+
value = utils.clone(value, {
|
|
778
|
+
depopulate: true,
|
|
779
|
+
transform: false,
|
|
780
|
+
virtuals: false,
|
|
781
|
+
getters: false,
|
|
782
|
+
omitUndefined: true,
|
|
783
|
+
_isNested: true
|
|
784
|
+
});
|
|
785
|
+
operand(this, where, delta, data, value);
|
|
786
|
+
}
|
|
776
787
|
}
|
|
777
788
|
}
|
|
778
789
|
|
|
@@ -3167,6 +3178,7 @@ Model.create = function create(doc, options, callback) {
|
|
|
3167
3178
|
*
|
|
3168
3179
|
* @param {Array} [pipeline]
|
|
3169
3180
|
* @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#watch)
|
|
3181
|
+
* @param {Boolean} [options.hydrate=false] if true and `fullDocument: 'updateLookup'` is set, Mongoose will automatically hydrate `fullDocument` into a fully fledged Mongoose document
|
|
3170
3182
|
* @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
|
|
3171
3183
|
* @api public
|
|
3172
3184
|
*/
|
|
@@ -3191,6 +3203,9 @@ Model.watch = function(pipeline, options) {
|
|
|
3191
3203
|
}
|
|
3192
3204
|
};
|
|
3193
3205
|
|
|
3206
|
+
options = options || {};
|
|
3207
|
+
options.model = this;
|
|
3208
|
+
|
|
3194
3209
|
return new ChangeStream(changeStreamThunk, pipeline, options);
|
|
3195
3210
|
};
|
|
3196
3211
|
|
|
@@ -3579,41 +3594,50 @@ Model.bulkWrite = function(ops, options, callback) {
|
|
|
3579
3594
|
*
|
|
3580
3595
|
* @param {Array<Document>} documents
|
|
3581
3596
|
* @param {Object} [options] options passed to the underlying `bulkWrite()`
|
|
3597
|
+
* @param {Boolean} [options.timestamps] defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
|
|
3582
3598
|
* @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](/docs/transactions.html).
|
|
3583
3599
|
* @param {String|number} [options.w=1] The [write concern](https://docs.mongodb.com/manual/reference/write-concern/). See [`Query#w()`](/docs/api.html#query_Query-w) for more information.
|
|
3584
3600
|
* @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
|
|
3585
3601
|
* @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
|
|
3586
3602
|
*
|
|
3587
3603
|
*/
|
|
3588
|
-
Model.bulkSave = function(documents, options) {
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true });
|
|
3592
|
-
|
|
3593
|
-
let bulkWriteResultPromise;
|
|
3594
|
-
return Promise.all(preSavePromises)
|
|
3595
|
-
.then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations, options))
|
|
3596
|
-
.then(() => documents.map(buildSuccessfulWriteHandlerPromise))
|
|
3597
|
-
.then(() => bulkWriteResultPromise)
|
|
3598
|
-
.catch((err) => {
|
|
3599
|
-
if (!(err && err.writeErrors && err.writeErrors.length)) {
|
|
3600
|
-
throw err;
|
|
3601
|
-
}
|
|
3602
|
-
return Promise.all(
|
|
3603
|
-
documents.map((document) => {
|
|
3604
|
-
const documentError = err.writeErrors.find(writeError => {
|
|
3605
|
-
const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
|
|
3606
|
-
return writeErrorDocumentId.toString() === document._id.toString();
|
|
3607
|
-
});
|
|
3604
|
+
Model.bulkSave = async function(documents, options) {
|
|
3605
|
+
options = options || {};
|
|
3608
3606
|
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3607
|
+
const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true, timestamps: options.timestamps });
|
|
3608
|
+
|
|
3609
|
+
if (options.timestamps != null) {
|
|
3610
|
+
for (const document of documents) {
|
|
3611
|
+
document.$__.saveOptions = document.$__.saveOptions || {};
|
|
3612
|
+
document.$__.saveOptions.timestamps = options.timestamps;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
await Promise.all(documents.map(buildPreSavePromise));
|
|
3617
|
+
|
|
3618
|
+
const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, options).then(
|
|
3619
|
+
(res) => ({ bulkWriteResult: res, bulkWriteError: null }),
|
|
3620
|
+
(err) => ({ bulkWriteResult: null, bulkWriteError: err })
|
|
3621
|
+
);
|
|
3622
|
+
|
|
3623
|
+
await Promise.all(
|
|
3624
|
+
documents.map(async(document) => {
|
|
3625
|
+
const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
|
|
3626
|
+
const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
|
|
3627
|
+
return writeErrorDocumentId.toString() === document._id.toString();
|
|
3615
3628
|
});
|
|
3616
|
-
|
|
3629
|
+
|
|
3630
|
+
if (documentError == null) {
|
|
3631
|
+
await handleSuccessfulWrite(document);
|
|
3632
|
+
}
|
|
3633
|
+
})
|
|
3634
|
+
);
|
|
3635
|
+
|
|
3636
|
+
if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) {
|
|
3637
|
+
throw bulkWriteError;
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
return bulkWriteResult;
|
|
3617
3641
|
};
|
|
3618
3642
|
|
|
3619
3643
|
function buildPreSavePromise(document) {
|
|
@@ -3628,26 +3652,137 @@ function buildPreSavePromise(document) {
|
|
|
3628
3652
|
});
|
|
3629
3653
|
}
|
|
3630
3654
|
|
|
3631
|
-
function
|
|
3655
|
+
function handleSuccessfulWrite(document) {
|
|
3632
3656
|
return new Promise((resolve, reject) => {
|
|
3633
|
-
|
|
3657
|
+
if (document.$isNew) {
|
|
3658
|
+
_setIsNew(document, false);
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
document.$__reset();
|
|
3662
|
+
document.schema.s.hooks.execPost('save', document, {}, (err) => {
|
|
3663
|
+
if (err) {
|
|
3664
|
+
reject(err);
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
resolve();
|
|
3668
|
+
});
|
|
3669
|
+
|
|
3634
3670
|
});
|
|
3635
3671
|
}
|
|
3636
3672
|
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3673
|
+
/**
|
|
3674
|
+
* Apply defaults to the given document or POJO.
|
|
3675
|
+
*
|
|
3676
|
+
* @param {Object|Document} obj object or document to apply defaults on
|
|
3677
|
+
* @returns {Object|Document}
|
|
3678
|
+
* @api public
|
|
3679
|
+
*/
|
|
3680
|
+
|
|
3681
|
+
Model.applyDefaults = function applyDefaults(doc) {
|
|
3682
|
+
if (doc.$__ != null) {
|
|
3683
|
+
applyDefaultsHelper(doc, doc.$__.fields, doc.$__.exclude);
|
|
3684
|
+
|
|
3685
|
+
for (const subdoc of doc.$getAllSubdocs()) {
|
|
3686
|
+
applyDefaults(subdoc, subdoc.$__.fields, subdoc.$__.exclude);
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
return doc;
|
|
3640
3690
|
}
|
|
3641
3691
|
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3692
|
+
applyDefaultsToPOJO(doc, this.schema);
|
|
3693
|
+
|
|
3694
|
+
return doc;
|
|
3695
|
+
};
|
|
3696
|
+
|
|
3697
|
+
/**
|
|
3698
|
+
* Cast the given POJO to the model's schema
|
|
3699
|
+
*
|
|
3700
|
+
* #### Example:
|
|
3701
|
+
* const Test = mongoose.model('Test', Schema({ num: Number }));
|
|
3702
|
+
*
|
|
3703
|
+
* const obj = Test.castObject({ num: '42' });
|
|
3704
|
+
* obj.num; // 42 as a number
|
|
3705
|
+
*
|
|
3706
|
+
* Test.castObject({ num: 'not a number' }); // Throws a ValidationError
|
|
3707
|
+
*
|
|
3708
|
+
* @param {Object} obj object or document to cast
|
|
3709
|
+
* @returns {Object} POJO casted to the model's schema
|
|
3710
|
+
* @throws {ValidationError} if casting failed for at least one path
|
|
3711
|
+
* @api public
|
|
3712
|
+
*/
|
|
3713
|
+
|
|
3714
|
+
Model.castObject = function castObject(obj) {
|
|
3715
|
+
const ret = {};
|
|
3716
|
+
|
|
3717
|
+
const schema = this.schema;
|
|
3718
|
+
const paths = Object.keys(schema.paths);
|
|
3719
|
+
|
|
3720
|
+
for (const path of paths) {
|
|
3721
|
+
const schemaType = schema.path(path);
|
|
3722
|
+
if (!schemaType || !schemaType.$isMongooseArray) {
|
|
3723
|
+
continue;
|
|
3647
3724
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3725
|
+
|
|
3726
|
+
const val = get(obj, path);
|
|
3727
|
+
pushNestedArrayPaths(paths, val, path);
|
|
3728
|
+
}
|
|
3729
|
+
|
|
3730
|
+
let error = null;
|
|
3731
|
+
|
|
3732
|
+
for (const path of paths) {
|
|
3733
|
+
const schemaType = schema.path(path);
|
|
3734
|
+
if (schemaType == null) {
|
|
3735
|
+
continue;
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3738
|
+
let val = get(obj, path, void 0);
|
|
3739
|
+
|
|
3740
|
+
if (val == null) {
|
|
3741
|
+
continue;
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
const pieces = path.indexOf('.') === -1 ? [path] : path.split('.');
|
|
3745
|
+
let cur = ret;
|
|
3746
|
+
for (let i = 0; i < pieces.length - 1; ++i) {
|
|
3747
|
+
if (cur[pieces[i]] == null) {
|
|
3748
|
+
cur[pieces[i]] = isNaN(pieces[i + 1]) ? {} : [];
|
|
3749
|
+
}
|
|
3750
|
+
cur = cur[pieces[i]];
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3753
|
+
if (schemaType.$isMongooseDocumentArray) {
|
|
3754
|
+
continue;
|
|
3755
|
+
}
|
|
3756
|
+
if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
|
|
3757
|
+
try {
|
|
3758
|
+
val = Model.castObject.call(schemaType.caster, val);
|
|
3759
|
+
} catch (err) {
|
|
3760
|
+
error = error || new ValidationError();
|
|
3761
|
+
error.addError(path, err);
|
|
3762
|
+
continue;
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3765
|
+
cur[pieces[pieces.length - 1]] = val;
|
|
3766
|
+
continue;
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
try {
|
|
3770
|
+
val = schemaType.cast(val);
|
|
3771
|
+
cur[pieces[pieces.length - 1]] = val;
|
|
3772
|
+
} catch (err) {
|
|
3773
|
+
error = error || new ValidationError();
|
|
3774
|
+
error.addError(path, err);
|
|
3775
|
+
|
|
3776
|
+
continue;
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
|
|
3780
|
+
if (error != null) {
|
|
3781
|
+
throw error;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
return ret;
|
|
3785
|
+
};
|
|
3651
3786
|
|
|
3652
3787
|
/**
|
|
3653
3788
|
* Build bulk write operations for `bulkSave()`.
|
|
@@ -3655,9 +3790,11 @@ function handleSuccessfulWrite(document, resolve, reject) {
|
|
|
3655
3790
|
* @param {Array<Document>} documents The array of documents to build write operations of
|
|
3656
3791
|
* @param {Object} options
|
|
3657
3792
|
* @param {Boolean} options.skipValidation defaults to `false`, when set to true, building the write operations will bypass validating the documents.
|
|
3793
|
+
* @param {Boolean} options.timestamps defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
|
|
3658
3794
|
* @return {Array<Promise>} Returns a array of all Promises the function executes to be awaited.
|
|
3659
3795
|
* @api private
|
|
3660
3796
|
*/
|
|
3797
|
+
|
|
3661
3798
|
Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, options) {
|
|
3662
3799
|
if (!Array.isArray(documents)) {
|
|
3663
3800
|
throw new Error(`bulkSave expects an array of documents to be passed, received \`${documents}\` instead`);
|
|
@@ -3678,9 +3815,9 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3678
3815
|
|
|
3679
3816
|
const isANewDocument = document.isNew;
|
|
3680
3817
|
if (isANewDocument) {
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3818
|
+
const writeOperation = { insertOne: { document } };
|
|
3819
|
+
utils.injectTimestampsOption(writeOperation.insertOne, options.timestamps);
|
|
3820
|
+
accumulator.push(writeOperation);
|
|
3684
3821
|
|
|
3685
3822
|
return accumulator;
|
|
3686
3823
|
}
|
|
@@ -3695,13 +3832,9 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3695
3832
|
_applyCustomWhere(document, where);
|
|
3696
3833
|
|
|
3697
3834
|
document.$__version(where, delta);
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
filter: where,
|
|
3702
|
-
update: changes
|
|
3703
|
-
}
|
|
3704
|
-
});
|
|
3835
|
+
const writeOperation = { updateOne: { filter: where, update: changes } };
|
|
3836
|
+
utils.injectTimestampsOption(writeOperation.updateOne, options.timestamps);
|
|
3837
|
+
accumulator.push(writeOperation);
|
|
3705
3838
|
|
|
3706
3839
|
return accumulator;
|
|
3707
3840
|
}
|
|
@@ -3720,6 +3853,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3720
3853
|
}
|
|
3721
3854
|
};
|
|
3722
3855
|
|
|
3856
|
+
|
|
3723
3857
|
/**
|
|
3724
3858
|
* Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
|
|
3725
3859
|
* The document returned has no paths marked as modified initially.
|
|
@@ -3731,11 +3865,13 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3731
3865
|
*
|
|
3732
3866
|
* @param {Object} obj
|
|
3733
3867
|
* @param {Object|String|String[]} [projection] optional projection containing which fields should be selected for this document
|
|
3868
|
+
* @param {Object} [options] optional options
|
|
3869
|
+
* @param {Boolean} [options.setters=false] if true, apply schema setters when hydrating
|
|
3734
3870
|
* @return {Document} document instance
|
|
3735
3871
|
* @api public
|
|
3736
3872
|
*/
|
|
3737
3873
|
|
|
3738
|
-
Model.hydrate = function(obj, projection) {
|
|
3874
|
+
Model.hydrate = function(obj, projection, options) {
|
|
3739
3875
|
_checkContext(this, 'hydrate');
|
|
3740
3876
|
|
|
3741
3877
|
if (projection != null) {
|
|
@@ -3746,7 +3882,7 @@ Model.hydrate = function(obj, projection) {
|
|
|
3746
3882
|
}
|
|
3747
3883
|
|
|
3748
3884
|
const document = require('./queryhelpers').createModel(this, obj, projection);
|
|
3749
|
-
document.$init(obj);
|
|
3885
|
+
document.$init(obj, options);
|
|
3750
3886
|
return document;
|
|
3751
3887
|
};
|
|
3752
3888
|
|
|
@@ -4245,7 +4381,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4245
4381
|
}
|
|
4246
4382
|
|
|
4247
4383
|
const val = get(obj, path);
|
|
4248
|
-
pushNestedArrayPaths(val, path);
|
|
4384
|
+
pushNestedArrayPaths(paths, val, path);
|
|
4249
4385
|
}
|
|
4250
4386
|
|
|
4251
4387
|
let remaining = paths.length;
|
|
@@ -4258,7 +4394,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4258
4394
|
continue;
|
|
4259
4395
|
}
|
|
4260
4396
|
|
|
4261
|
-
const pieces = path.split('.');
|
|
4397
|
+
const pieces = path.indexOf('.') === -1 ? [path] : path.split('.');
|
|
4262
4398
|
let cur = obj;
|
|
4263
4399
|
for (let i = 0; i < pieces.length - 1; ++i) {
|
|
4264
4400
|
cur = cur[pieces[i]];
|
|
@@ -4282,32 +4418,12 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4282
4418
|
schemaType.doValidate(val, err => {
|
|
4283
4419
|
if (err) {
|
|
4284
4420
|
error = error || new ValidationError();
|
|
4285
|
-
|
|
4286
|
-
for (const _err of Object.keys(err.errors)) {
|
|
4287
|
-
error.addError(`${path}.${err.errors[_err].path}`, _err);
|
|
4288
|
-
}
|
|
4289
|
-
} else {
|
|
4290
|
-
error.addError(err.path, err);
|
|
4291
|
-
}
|
|
4421
|
+
error.addError(path, err);
|
|
4292
4422
|
}
|
|
4293
4423
|
_checkDone();
|
|
4294
4424
|
}, context, { path: path });
|
|
4295
4425
|
}
|
|
4296
4426
|
|
|
4297
|
-
function pushNestedArrayPaths(nestedArray, path) {
|
|
4298
|
-
if (nestedArray == null) {
|
|
4299
|
-
return;
|
|
4300
|
-
}
|
|
4301
|
-
|
|
4302
|
-
for (let i = 0; i < nestedArray.length; ++i) {
|
|
4303
|
-
if (Array.isArray(nestedArray[i])) {
|
|
4304
|
-
pushNestedArrayPaths(nestedArray[i], path + '.' + i);
|
|
4305
|
-
} else {
|
|
4306
|
-
paths.push(path + '.' + i);
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
}
|
|
4310
|
-
|
|
4311
4427
|
function _checkDone() {
|
|
4312
4428
|
if (--remaining <= 0) {
|
|
4313
4429
|
return cb(error);
|
|
@@ -52,6 +52,25 @@ Object.defineProperty(SchemaArrayOptions.prototype, 'enum', opts);
|
|
|
52
52
|
|
|
53
53
|
Object.defineProperty(SchemaArrayOptions.prototype, 'of', opts);
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* If set to `false`, will always deactivate casting non-array values to arrays.
|
|
57
|
+
* If set to `true`, will cast non-array values to arrays if `init` and `SchemaArray.options.castNonArrays` are also `true`
|
|
58
|
+
*
|
|
59
|
+
* #### Example:
|
|
60
|
+
*
|
|
61
|
+
* const Model = db.model('Test', new Schema({ x1: { castNonArrays: false, type: [String] } }));
|
|
62
|
+
* const doc = new Model({ x1: "some non-array value" });
|
|
63
|
+
* await doc.validate(); // Errors with "CastError"
|
|
64
|
+
*
|
|
65
|
+
* @api public
|
|
66
|
+
* @property castNonArrays
|
|
67
|
+
* @memberOf SchemaArrayOptions
|
|
68
|
+
* @type {Boolean}
|
|
69
|
+
* @instance
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
Object.defineProperty(SchemaArrayOptions.prototype, 'castNonArrays', opts);
|
|
73
|
+
|
|
55
74
|
/*!
|
|
56
75
|
* ignore
|
|
57
76
|
*/
|
|
@@ -23,14 +23,14 @@ module.exports = function trackTransaction(schema) {
|
|
|
23
23
|
initialState.versionKey = this.get(this.$__schema.options.versionKey);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
initialState.modifiedPaths = new Set(Object.keys(this.$__.activePaths.
|
|
26
|
+
initialState.modifiedPaths = new Set(Object.keys(this.$__.activePaths.getStatePaths('modify')));
|
|
27
27
|
initialState.atomics = _getAtomics(this);
|
|
28
28
|
|
|
29
29
|
session[sessionNewDocuments].set(this, initialState);
|
|
30
30
|
} else {
|
|
31
31
|
const state = session[sessionNewDocuments].get(this);
|
|
32
32
|
|
|
33
|
-
for (const path of Object.keys(this.$__.activePaths.
|
|
33
|
+
for (const path of Object.keys(this.$__.activePaths.getStatePaths('modify'))) {
|
|
34
34
|
state.modifiedPaths.add(path);
|
|
35
35
|
}
|
|
36
36
|
state.atomics = _getAtomics(this, state.atomics);
|
package/lib/query.js
CHANGED
|
@@ -1632,6 +1632,9 @@ Query.prototype.setOptions = function(options, overwrite) {
|
|
|
1632
1632
|
this._mongooseOptions.defaults = options.defaults;
|
|
1633
1633
|
// deleting options.defaults will cause 7287 to fail
|
|
1634
1634
|
}
|
|
1635
|
+
if (options.lean == null && this.schema && 'lean' in this.schema.options) {
|
|
1636
|
+
this._mongooseOptions.lean = this.schema.options.lean;
|
|
1637
|
+
}
|
|
1635
1638
|
|
|
1636
1639
|
if (typeof options.limit === 'string') {
|
|
1637
1640
|
try {
|
package/lib/schema/array.js
CHANGED
|
@@ -393,7 +393,8 @@ SchemaArray.prototype.cast = function(value, doc, init, prev, options) {
|
|
|
393
393
|
return value;
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
|
|
396
|
+
const castNonArraysOption = this.options.castNonArrays != null ? this.options.castNonArrays : SchemaArray.options.castNonArrays;
|
|
397
|
+
if (init || castNonArraysOption) {
|
|
397
398
|
// gh-2442: if we're loading this from the db and its not an array, mark
|
|
398
399
|
// the whole array as modified.
|
|
399
400
|
if (!!doc && !!init) {
|