Skip to content

Commit

Permalink
fix(document): apply virtuals to subdocuments if parent schema has `v…
Browse files Browse the repository at this point in the history
…irtuals: true` for backwards compatibility

Fix #14771
Re: #14394
Re: #14623
  • Loading branch information
vkarpov15 committed Aug 1, 2024
1 parent 986f5eb commit eebf2d6
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
12 changes: 9 additions & 3 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -3801,7 +3801,7 @@ Document.prototype.$__handleReject = function handleReject(err) {
};

/**
* Internal helper for toObject() and toJSON() that doesn't manipulate options
* Internal common logic for toObject() and toJSON()
*
* @return {Object}
* @api private
Expand Down Expand Up @@ -3834,14 +3834,17 @@ Document.prototype.$toObject = function(options, json) {
}

const depopulate = options._calledWithOptions.depopulate
?? options._parentOptions?.depopulate
?? defaultOptions?.depopulate
?? options.depopulate
?? false;
// _isNested will only be true if this is not the top level document, we
// should never depopulate the top-level document
if (depopulate && options._isNested && this.$__.wasPopulated) {
return clone(this.$__.wasPopulated.value || this._doc._id, options);
}
if (depopulate) {
options.depopulate = true;
}

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

options._parentOptions = options;
const parentOptions = options._parentOptions;
// Parent options should only bubble down for subdocuments, not populated docs
options._parentOptions = this.$isSubdocument ? options : null;

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

const virtuals = options._calledWithOptions.virtuals
?? defaultOptions.virtuals
?? parentOptions?.virtuals
?? undefined;

if (virtuals || (getters && virtuals !== false)) {
Expand Down
32 changes: 32 additions & 0 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13750,6 +13750,38 @@ describe('document', function() {
/Test error in post deleteOne hook/
);
});

it('applies virtuals to subschemas if top-level schema has virtuals: true (gh-14771)', function() {
const userLabSchema = new mongoose.Schema({
capacityLevel: Number
});

userLabSchema.virtual('capacityLevelCeil').get(function() {
return Math.ceil(this.capacityLevel);
});

const labPlotSchema = new mongoose.Schema({
plotId: Number,
lab: userLabSchema
});

const userSchema = new mongoose.Schema({
username: String,
labPlots: [labPlotSchema]
}, { toObject: { virtuals: true }, toJSON: { virtuals: true } });

const User = db.model('User', userSchema);

const doc = new User({
username: 'test',
labPlots: [{
plotId: 1,
lab: { capacityLevel: 3.14 }
}]
});
assert.strictEqual(doc.toObject().labPlots[0].lab.capacityLevelCeil, 4);
assert.strictEqual(doc.toJSON().labPlots[0].lab.capacityLevelCeil, 4);
});
});

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

0 comments on commit eebf2d6

Please sign in to comment.