Skip to content

Commit a206eef

Browse files
authored
Merge pull request #14774 from Automattic/vkarpov15/gh-14771
fix(document): apply virtuals to subdocuments if parent schema has `virtuals: true` for backwards compatibility
2 parents 6ea38ff + eebf2d6 commit a206eef

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

lib/document.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3801,7 +3801,7 @@ Document.prototype.$__handleReject = function handleReject(err) {
38013801
};
38023802

38033803
/**
3804-
* Internal helper for toObject() and toJSON() that doesn't manipulate options
3804+
* Internal common logic for toObject() and toJSON()
38053805
*
38063806
* @return {Object}
38073807
* @api private
@@ -3834,14 +3834,17 @@ Document.prototype.$toObject = function(options, json) {
38343834
}
38353835

38363836
const depopulate = options._calledWithOptions.depopulate
3837-
?? options._parentOptions?.depopulate
38383837
?? defaultOptions?.depopulate
3838+
?? options.depopulate
38393839
?? false;
38403840
// _isNested will only be true if this is not the top level document, we
38413841
// should never depopulate the top-level document
38423842
if (depopulate && options._isNested && this.$__.wasPopulated) {
38433843
return clone(this.$__.wasPopulated.value || this._doc._id, options);
38443844
}
3845+
if (depopulate) {
3846+
options.depopulate = true;
3847+
}
38453848

38463849
// merge default options with input options.
38473850
if (defaultOptions != null) {
@@ -3855,7 +3858,9 @@ Document.prototype.$toObject = function(options, json) {
38553858
options.json = json;
38563859
options.minimize = _minimize;
38573860

3858-
options._parentOptions = options;
3861+
const parentOptions = options._parentOptions;
3862+
// Parent options should only bubble down for subdocuments, not populated docs
3863+
options._parentOptions = this.$isSubdocument ? options : null;
38593864

38603865
options._skipSingleNestedGetters = false;
38613866
// remember the root transform function
@@ -3886,6 +3891,7 @@ Document.prototype.$toObject = function(options, json) {
38863891

38873892
const virtuals = options._calledWithOptions.virtuals
38883893
?? defaultOptions.virtuals
3894+
?? parentOptions?.virtuals
38893895
?? undefined;
38903896

38913897
if (virtuals || (getters && virtuals !== false)) {

test/document.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13750,6 +13750,38 @@ describe('document', function() {
1375013750
/Test error in post deleteOne hook/
1375113751
);
1375213752
});
13753+
13754+
it('applies virtuals to subschemas if top-level schema has virtuals: true (gh-14771)', function() {
13755+
const userLabSchema = new mongoose.Schema({
13756+
capacityLevel: Number
13757+
});
13758+
13759+
userLabSchema.virtual('capacityLevelCeil').get(function() {
13760+
return Math.ceil(this.capacityLevel);
13761+
});
13762+
13763+
const labPlotSchema = new mongoose.Schema({
13764+
plotId: Number,
13765+
lab: userLabSchema
13766+
});
13767+
13768+
const userSchema = new mongoose.Schema({
13769+
username: String,
13770+
labPlots: [labPlotSchema]
13771+
}, { toObject: { virtuals: true }, toJSON: { virtuals: true } });
13772+
13773+
const User = db.model('User', userSchema);
13774+
13775+
const doc = new User({
13776+
username: 'test',
13777+
labPlots: [{
13778+
plotId: 1,
13779+
lab: { capacityLevel: 3.14 }
13780+
}]
13781+
});
13782+
assert.strictEqual(doc.toObject().labPlots[0].lab.capacityLevelCeil, 4);
13783+
assert.strictEqual(doc.toJSON().labPlots[0].lab.capacityLevelCeil, 4);
13784+
});
1375313785
});
1375413786

1375513787
describe('Check if instance function that is supplied in schema option is available', function() {

0 commit comments

Comments
 (0)