mongoose 6.4.7 → 6.5.2
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 +23 -3
- package/lib/cursor/ChangeStream.js +39 -1
- package/lib/document.js +129 -154
- package/lib/error/index.js +1 -0
- 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/helpers/populate/markArraySubdocsPopulated.js +1 -0
- package/lib/helpers/projection/hasIncludedChildren.js +1 -0
- package/lib/helpers/promiseOrCallback.js +24 -15
- package/lib/helpers/update/applyTimestampsToChildren.js +6 -2
- package/lib/index.js +2 -1
- package/lib/internal.js +3 -1
- package/lib/model.js +237 -95
- package/lib/options/SchemaArrayOptions.js +19 -0
- package/lib/options/SchemaNumberOptions.js +2 -0
- package/lib/options/SchemaObjectIdOptions.js +1 -0
- package/lib/plugins/trackTransaction.js +2 -2
- package/lib/query.js +30 -11
- package/lib/schema/SubdocumentPath.js +10 -0
- package/lib/schema/array.js +2 -1
- package/lib/schema/documentarray.js +14 -1
- package/lib/schema/string.js +3 -0
- package/lib/schema.js +22 -3
- package/lib/schematype.js +5 -1
- package/lib/statemachine.js +23 -9
- package/lib/types/buffer.js +23 -21
- package/lib/types/map.js +2 -0
- package/lib/utils.js +8 -0
- package/lib/validoptions.js +1 -0
- package/package.json +14 -14
- package/{build-browser.js → scripts/build-browser.js} +1 -1
- package/types/connection.d.ts +7 -1
- package/types/document.d.ts +8 -1
- package/types/expressions.d.ts +1 -1
- package/types/index.d.ts +31 -28
- package/types/inferschematype.d.ts +3 -20
- package/types/models.d.ts +53 -49
- 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
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
|
|
|
@@ -3109,10 +3120,9 @@ Model.create = function create(doc, options, callback) {
|
|
|
3109
3120
|
}
|
|
3110
3121
|
const _done = (error, res) => {
|
|
3111
3122
|
const savedDocs = [];
|
|
3112
|
-
const
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
savedDocs.push(res[i].doc);
|
|
3123
|
+
for (const val of res) {
|
|
3124
|
+
if (val.doc) {
|
|
3125
|
+
savedDocs.push(val.doc);
|
|
3116
3126
|
}
|
|
3117
3127
|
}
|
|
3118
3128
|
|
|
@@ -3167,6 +3177,7 @@ Model.create = function create(doc, options, callback) {
|
|
|
3167
3177
|
*
|
|
3168
3178
|
* @param {Array} [pipeline]
|
|
3169
3179
|
* @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#watch)
|
|
3180
|
+
* @param {Boolean} [options.hydrate=false] if true and `fullDocument: 'updateLookup'` is set, Mongoose will automatically hydrate `fullDocument` into a fully fledged Mongoose document
|
|
3170
3181
|
* @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
|
|
3171
3182
|
* @api public
|
|
3172
3183
|
*/
|
|
@@ -3191,6 +3202,9 @@ Model.watch = function(pipeline, options) {
|
|
|
3191
3202
|
}
|
|
3192
3203
|
};
|
|
3193
3204
|
|
|
3205
|
+
options = options || {};
|
|
3206
|
+
options.model = this;
|
|
3207
|
+
|
|
3194
3208
|
return new ChangeStream(changeStreamThunk, pipeline, options);
|
|
3195
3209
|
};
|
|
3196
3210
|
|
|
@@ -3560,13 +3574,17 @@ Model.bulkWrite = function(ops, options, callback) {
|
|
|
3560
3574
|
return cb(null, getDefaultBulkwriteResult());
|
|
3561
3575
|
}
|
|
3562
3576
|
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3577
|
+
try {
|
|
3578
|
+
this.$__collection.bulkWrite(ops, options, (error, res) => {
|
|
3579
|
+
if (error) {
|
|
3580
|
+
return cb(error);
|
|
3581
|
+
}
|
|
3567
3582
|
|
|
3568
|
-
|
|
3569
|
-
|
|
3583
|
+
cb(null, res);
|
|
3584
|
+
});
|
|
3585
|
+
} catch (err) {
|
|
3586
|
+
return cb(err);
|
|
3587
|
+
}
|
|
3570
3588
|
});
|
|
3571
3589
|
}, this.events);
|
|
3572
3590
|
};
|
|
@@ -3579,41 +3597,50 @@ Model.bulkWrite = function(ops, options, callback) {
|
|
|
3579
3597
|
*
|
|
3580
3598
|
* @param {Array<Document>} documents
|
|
3581
3599
|
* @param {Object} [options] options passed to the underlying `bulkWrite()`
|
|
3600
|
+
* @param {Boolean} [options.timestamps] defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
|
|
3582
3601
|
* @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](/docs/transactions.html).
|
|
3583
3602
|
* @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
3603
|
* @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
|
|
3585
3604
|
* @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
|
|
3586
3605
|
*
|
|
3587
3606
|
*/
|
|
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
|
-
});
|
|
3607
|
+
Model.bulkSave = async function(documents, options) {
|
|
3608
|
+
options = options || {};
|
|
3608
3609
|
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3610
|
+
const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true, timestamps: options.timestamps });
|
|
3611
|
+
|
|
3612
|
+
if (options.timestamps != null) {
|
|
3613
|
+
for (const document of documents) {
|
|
3614
|
+
document.$__.saveOptions = document.$__.saveOptions || {};
|
|
3615
|
+
document.$__.saveOptions.timestamps = options.timestamps;
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
await Promise.all(documents.map(buildPreSavePromise));
|
|
3620
|
+
|
|
3621
|
+
const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, options).then(
|
|
3622
|
+
(res) => ({ bulkWriteResult: res, bulkWriteError: null }),
|
|
3623
|
+
(err) => ({ bulkWriteResult: null, bulkWriteError: err })
|
|
3624
|
+
);
|
|
3625
|
+
|
|
3626
|
+
await Promise.all(
|
|
3627
|
+
documents.map(async(document) => {
|
|
3628
|
+
const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
|
|
3629
|
+
const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
|
|
3630
|
+
return writeErrorDocumentId.toString() === document._id.toString();
|
|
3615
3631
|
});
|
|
3616
|
-
|
|
3632
|
+
|
|
3633
|
+
if (documentError == null) {
|
|
3634
|
+
await handleSuccessfulWrite(document);
|
|
3635
|
+
}
|
|
3636
|
+
})
|
|
3637
|
+
);
|
|
3638
|
+
|
|
3639
|
+
if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) {
|
|
3640
|
+
throw bulkWriteError;
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
return bulkWriteResult;
|
|
3617
3644
|
};
|
|
3618
3645
|
|
|
3619
3646
|
function buildPreSavePromise(document) {
|
|
@@ -3628,26 +3655,138 @@ function buildPreSavePromise(document) {
|
|
|
3628
3655
|
});
|
|
3629
3656
|
}
|
|
3630
3657
|
|
|
3631
|
-
function
|
|
3658
|
+
function handleSuccessfulWrite(document) {
|
|
3632
3659
|
return new Promise((resolve, reject) => {
|
|
3633
|
-
|
|
3660
|
+
if (document.$isNew) {
|
|
3661
|
+
_setIsNew(document, false);
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
document.$__reset();
|
|
3665
|
+
document.schema.s.hooks.execPost('save', document, {}, (err) => {
|
|
3666
|
+
if (err) {
|
|
3667
|
+
reject(err);
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
resolve();
|
|
3671
|
+
});
|
|
3672
|
+
|
|
3634
3673
|
});
|
|
3635
3674
|
}
|
|
3636
3675
|
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3676
|
+
/**
|
|
3677
|
+
* Apply defaults to the given document or POJO.
|
|
3678
|
+
*
|
|
3679
|
+
* @param {Object|Document} obj object or document to apply defaults on
|
|
3680
|
+
* @returns {Object|Document}
|
|
3681
|
+
* @api public
|
|
3682
|
+
*/
|
|
3683
|
+
|
|
3684
|
+
Model.applyDefaults = function applyDefaults(doc) {
|
|
3685
|
+
if (doc.$__ != null) {
|
|
3686
|
+
applyDefaultsHelper(doc, doc.$__.fields, doc.$__.exclude);
|
|
3687
|
+
|
|
3688
|
+
for (const subdoc of doc.$getAllSubdocs()) {
|
|
3689
|
+
applyDefaults(subdoc, subdoc.$__.fields, subdoc.$__.exclude);
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
return doc;
|
|
3640
3693
|
}
|
|
3641
3694
|
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3695
|
+
applyDefaultsToPOJO(doc, this.schema);
|
|
3696
|
+
|
|
3697
|
+
return doc;
|
|
3698
|
+
};
|
|
3699
|
+
|
|
3700
|
+
/**
|
|
3701
|
+
* Cast the given POJO to the model's schema
|
|
3702
|
+
*
|
|
3703
|
+
* #### Example:
|
|
3704
|
+
*
|
|
3705
|
+
* const Test = mongoose.model('Test', Schema({ num: Number }));
|
|
3706
|
+
*
|
|
3707
|
+
* const obj = Test.castObject({ num: '42' });
|
|
3708
|
+
* obj.num; // 42 as a number
|
|
3709
|
+
*
|
|
3710
|
+
* Test.castObject({ num: 'not a number' }); // Throws a ValidationError
|
|
3711
|
+
*
|
|
3712
|
+
* @param {Object} obj object or document to cast
|
|
3713
|
+
* @returns {Object} POJO casted to the model's schema
|
|
3714
|
+
* @throws {ValidationError} if casting failed for at least one path
|
|
3715
|
+
* @api public
|
|
3716
|
+
*/
|
|
3717
|
+
|
|
3718
|
+
Model.castObject = function castObject(obj) {
|
|
3719
|
+
const ret = {};
|
|
3720
|
+
|
|
3721
|
+
const schema = this.schema;
|
|
3722
|
+
const paths = Object.keys(schema.paths);
|
|
3723
|
+
|
|
3724
|
+
for (const path of paths) {
|
|
3725
|
+
const schemaType = schema.path(path);
|
|
3726
|
+
if (!schemaType || !schemaType.$isMongooseArray) {
|
|
3727
|
+
continue;
|
|
3647
3728
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3729
|
+
|
|
3730
|
+
const val = get(obj, path);
|
|
3731
|
+
pushNestedArrayPaths(paths, val, path);
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
let error = null;
|
|
3735
|
+
|
|
3736
|
+
for (const path of paths) {
|
|
3737
|
+
const schemaType = schema.path(path);
|
|
3738
|
+
if (schemaType == null) {
|
|
3739
|
+
continue;
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
let val = get(obj, path, void 0);
|
|
3743
|
+
|
|
3744
|
+
if (val == null) {
|
|
3745
|
+
continue;
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3748
|
+
const pieces = path.indexOf('.') === -1 ? [path] : path.split('.');
|
|
3749
|
+
let cur = ret;
|
|
3750
|
+
for (let i = 0; i < pieces.length - 1; ++i) {
|
|
3751
|
+
if (cur[pieces[i]] == null) {
|
|
3752
|
+
cur[pieces[i]] = isNaN(pieces[i + 1]) ? {} : [];
|
|
3753
|
+
}
|
|
3754
|
+
cur = cur[pieces[i]];
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3757
|
+
if (schemaType.$isMongooseDocumentArray) {
|
|
3758
|
+
continue;
|
|
3759
|
+
}
|
|
3760
|
+
if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
|
|
3761
|
+
try {
|
|
3762
|
+
val = Model.castObject.call(schemaType.caster, val);
|
|
3763
|
+
} catch (err) {
|
|
3764
|
+
error = error || new ValidationError();
|
|
3765
|
+
error.addError(path, err);
|
|
3766
|
+
continue;
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
cur[pieces[pieces.length - 1]] = val;
|
|
3770
|
+
continue;
|
|
3771
|
+
}
|
|
3772
|
+
|
|
3773
|
+
try {
|
|
3774
|
+
val = schemaType.cast(val);
|
|
3775
|
+
cur[pieces[pieces.length - 1]] = val;
|
|
3776
|
+
} catch (err) {
|
|
3777
|
+
error = error || new ValidationError();
|
|
3778
|
+
error.addError(path, err);
|
|
3779
|
+
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
if (error != null) {
|
|
3785
|
+
throw error;
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
return ret;
|
|
3789
|
+
};
|
|
3651
3790
|
|
|
3652
3791
|
/**
|
|
3653
3792
|
* Build bulk write operations for `bulkSave()`.
|
|
@@ -3655,9 +3794,11 @@ function handleSuccessfulWrite(document, resolve, reject) {
|
|
|
3655
3794
|
* @param {Array<Document>} documents The array of documents to build write operations of
|
|
3656
3795
|
* @param {Object} options
|
|
3657
3796
|
* @param {Boolean} options.skipValidation defaults to `false`, when set to true, building the write operations will bypass validating the documents.
|
|
3797
|
+
* @param {Boolean} options.timestamps defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
|
|
3658
3798
|
* @return {Array<Promise>} Returns a array of all Promises the function executes to be awaited.
|
|
3659
3799
|
* @api private
|
|
3660
3800
|
*/
|
|
3801
|
+
|
|
3661
3802
|
Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, options) {
|
|
3662
3803
|
if (!Array.isArray(documents)) {
|
|
3663
3804
|
throw new Error(`bulkSave expects an array of documents to be passed, received \`${documents}\` instead`);
|
|
@@ -3678,9 +3819,9 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3678
3819
|
|
|
3679
3820
|
const isANewDocument = document.isNew;
|
|
3680
3821
|
if (isANewDocument) {
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3822
|
+
const writeOperation = { insertOne: { document } };
|
|
3823
|
+
utils.injectTimestampsOption(writeOperation.insertOne, options.timestamps);
|
|
3824
|
+
accumulator.push(writeOperation);
|
|
3684
3825
|
|
|
3685
3826
|
return accumulator;
|
|
3686
3827
|
}
|
|
@@ -3695,13 +3836,9 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3695
3836
|
_applyCustomWhere(document, where);
|
|
3696
3837
|
|
|
3697
3838
|
document.$__version(where, delta);
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
filter: where,
|
|
3702
|
-
update: changes
|
|
3703
|
-
}
|
|
3704
|
-
});
|
|
3839
|
+
const writeOperation = { updateOne: { filter: where, update: changes } };
|
|
3840
|
+
utils.injectTimestampsOption(writeOperation.updateOne, options.timestamps);
|
|
3841
|
+
accumulator.push(writeOperation);
|
|
3705
3842
|
|
|
3706
3843
|
return accumulator;
|
|
3707
3844
|
}
|
|
@@ -3720,6 +3857,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3720
3857
|
}
|
|
3721
3858
|
};
|
|
3722
3859
|
|
|
3860
|
+
|
|
3723
3861
|
/**
|
|
3724
3862
|
* Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
|
|
3725
3863
|
* The document returned has no paths marked as modified initially.
|
|
@@ -3731,11 +3869,13 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3731
3869
|
*
|
|
3732
3870
|
* @param {Object} obj
|
|
3733
3871
|
* @param {Object|String|String[]} [projection] optional projection containing which fields should be selected for this document
|
|
3872
|
+
* @param {Object} [options] optional options
|
|
3873
|
+
* @param {Boolean} [options.setters=false] if true, apply schema setters when hydrating
|
|
3734
3874
|
* @return {Document} document instance
|
|
3735
3875
|
* @api public
|
|
3736
3876
|
*/
|
|
3737
3877
|
|
|
3738
|
-
Model.hydrate = function(obj, projection) {
|
|
3878
|
+
Model.hydrate = function(obj, projection, options) {
|
|
3739
3879
|
_checkContext(this, 'hydrate');
|
|
3740
3880
|
|
|
3741
3881
|
if (projection != null) {
|
|
@@ -3746,7 +3886,7 @@ Model.hydrate = function(obj, projection) {
|
|
|
3746
3886
|
}
|
|
3747
3887
|
|
|
3748
3888
|
const document = require('./queryhelpers').createModel(this, obj, projection);
|
|
3749
|
-
document.$init(obj);
|
|
3889
|
+
document.$init(obj, options);
|
|
3750
3890
|
return document;
|
|
3751
3891
|
};
|
|
3752
3892
|
|
|
@@ -3843,6 +3983,7 @@ Model.update = function update(conditions, doc, options, callback) {
|
|
|
3843
3983
|
* and `post('updateMany')` instead.
|
|
3844
3984
|
*
|
|
3845
3985
|
* #### Example:
|
|
3986
|
+
*
|
|
3846
3987
|
* const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true });
|
|
3847
3988
|
* res.matchedCount; // Number of documents matched
|
|
3848
3989
|
* res.modifiedCount; // Number of documents modified
|
|
@@ -3883,6 +4024,7 @@ Model.updateMany = function updateMany(conditions, doc, options, callback) {
|
|
|
3883
4024
|
* - Use `replaceOne()` if you want to overwrite an entire document rather than using atomic operators like `$set`.
|
|
3884
4025
|
*
|
|
3885
4026
|
* #### Example:
|
|
4027
|
+
*
|
|
3886
4028
|
* const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' });
|
|
3887
4029
|
* res.matchedCount; // Number of documents matched
|
|
3888
4030
|
* res.modifiedCount; // Number of documents modified
|
|
@@ -3920,6 +4062,7 @@ Model.updateOne = function updateOne(conditions, doc, options, callback) {
|
|
|
3920
4062
|
* given document (no atomic operators like `$set`).
|
|
3921
4063
|
*
|
|
3922
4064
|
* #### Example:
|
|
4065
|
+
*
|
|
3923
4066
|
* const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' });
|
|
3924
4067
|
* res.matchedCount; // Number of documents matched
|
|
3925
4068
|
* res.modifiedCount; // Number of documents modified
|
|
@@ -4049,7 +4192,7 @@ function _update(model, op, conditions, doc, options, callback) {
|
|
|
4049
4192
|
* @param {String} [opts.out.reduce] add the results to collectionName: if dups are detected, uses the reducer / finalize functions
|
|
4050
4193
|
* @param {String} [opts.out.merge] add the results to collectionName: if dups exist the new docs overwrite the old
|
|
4051
4194
|
* @param {Function} [callback] optional callback
|
|
4052
|
-
* @see https://www.mongodb.org/display/DOCS/MapReduce
|
|
4195
|
+
* @see MongoDB MapReduce https://www.mongodb.org/display/DOCS/MapReduce
|
|
4053
4196
|
* @return {Promise}
|
|
4054
4197
|
* @api public
|
|
4055
4198
|
*/
|
|
@@ -4245,7 +4388,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4245
4388
|
}
|
|
4246
4389
|
|
|
4247
4390
|
const val = get(obj, path);
|
|
4248
|
-
pushNestedArrayPaths(val, path);
|
|
4391
|
+
pushNestedArrayPaths(paths, val, path);
|
|
4249
4392
|
}
|
|
4250
4393
|
|
|
4251
4394
|
let remaining = paths.length;
|
|
@@ -4258,7 +4401,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4258
4401
|
continue;
|
|
4259
4402
|
}
|
|
4260
4403
|
|
|
4261
|
-
const pieces = path.split('.');
|
|
4404
|
+
const pieces = path.indexOf('.') === -1 ? [path] : path.split('.');
|
|
4262
4405
|
let cur = obj;
|
|
4263
4406
|
for (let i = 0; i < pieces.length - 1; ++i) {
|
|
4264
4407
|
cur = cur[pieces[i]];
|
|
@@ -4282,32 +4425,12 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4282
4425
|
schemaType.doValidate(val, err => {
|
|
4283
4426
|
if (err) {
|
|
4284
4427
|
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
|
-
}
|
|
4428
|
+
error.addError(path, err);
|
|
4292
4429
|
}
|
|
4293
4430
|
_checkDone();
|
|
4294
4431
|
}, context, { path: path });
|
|
4295
4432
|
}
|
|
4296
4433
|
|
|
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
4434
|
function _checkDone() {
|
|
4312
4435
|
if (--remaining <= 0) {
|
|
4313
4436
|
return cb(error);
|
|
@@ -4479,6 +4602,17 @@ function populate(model, docs, options, callback) {
|
|
|
4479
4602
|
mod.options.options.sort || void 0;
|
|
4480
4603
|
assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
|
|
4481
4604
|
|
|
4605
|
+
// Lean transform may delete `_id`, which would cause assignment
|
|
4606
|
+
// to fail. So delay running lean transform until _after_
|
|
4607
|
+
// `_assign()`
|
|
4608
|
+
if (mod.options &&
|
|
4609
|
+
mod.options.options &&
|
|
4610
|
+
mod.options.options.lean &&
|
|
4611
|
+
mod.options.options.lean.transform) {
|
|
4612
|
+
mod.options.options._leanTransform = mod.options.options.lean.transform;
|
|
4613
|
+
mod.options.options.lean = true;
|
|
4614
|
+
}
|
|
4615
|
+
|
|
4482
4616
|
if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
|
|
4483
4617
|
// Ensure that we set to 0 or empty array even
|
|
4484
4618
|
// if we don't actually execute a query to make sure there's a value
|
|
@@ -4553,6 +4687,14 @@ function populate(model, docs, options, callback) {
|
|
|
4553
4687
|
for (const arr of params) {
|
|
4554
4688
|
removeDeselectedForeignField(arr[0].foreignField, arr[0].options, vals);
|
|
4555
4689
|
}
|
|
4690
|
+
for (const arr of params) {
|
|
4691
|
+
const mod = arr[0];
|
|
4692
|
+
if (mod.options && mod.options.options && mod.options.options._leanTransform) {
|
|
4693
|
+
for (const doc of vals) {
|
|
4694
|
+
mod.options.options._leanTransform(doc);
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4556
4698
|
callback();
|
|
4557
4699
|
}
|
|
4558
4700
|
}
|
|
@@ -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
|
*/
|
|
@@ -50,6 +50,7 @@ Object.defineProperty(SchemaNumberOptions.prototype, 'max', opts);
|
|
|
50
50
|
* equal to one of the given values.
|
|
51
51
|
*
|
|
52
52
|
* #### Example:
|
|
53
|
+
*
|
|
53
54
|
* const schema = new Schema({
|
|
54
55
|
* favoritePrime: {
|
|
55
56
|
* type: Number,
|
|
@@ -71,6 +72,7 @@ Object.defineProperty(SchemaNumberOptions.prototype, 'enum', opts);
|
|
|
71
72
|
* Sets default [populate options](/docs/populate.html#query-conditions).
|
|
72
73
|
*
|
|
73
74
|
* #### Example:
|
|
75
|
+
*
|
|
74
76
|
* const schema = new Schema({
|
|
75
77
|
* child: {
|
|
76
78
|
* type: Number,
|
|
@@ -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);
|