Skip to content

Commit 03ef4d8

Browse files
committed
fix(document): support getter setting virtual on manually populated doc when calling toJSON()
Fix #8295
1 parent d4648ee commit 03ef4d8

File tree

2 files changed

+21
-11
lines changed

2 files changed

+21
-11
lines changed

lib/document.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2836,6 +2836,13 @@ Document.prototype.$toObject = function(options, json) {
28362836
minimize: _minimize
28372837
});
28382838

2839+
if (utils.hasUserDefinedProperty(options, 'getters')) {
2840+
cloneOptions.getters = options.getters;
2841+
}
2842+
if (utils.hasUserDefinedProperty(options, 'virtuals')) {
2843+
cloneOptions.virtuals = options.virtuals;
2844+
}
2845+
28392846
const depopulate = options.depopulate ||
28402847
get(options, '_parentOptions.depopulate', false);
28412848
// _isNested will only be true if this is not the top level document, we
@@ -2852,6 +2859,10 @@ Document.prototype.$toObject = function(options, json) {
28522859
options.minimize = _minimize;
28532860

28542861
cloneOptions._parentOptions = options;
2862+
cloneOptions._skipSingleNestedGetters = true;
2863+
2864+
const gettersOptions = Object.assign({}, cloneOptions);
2865+
gettersOptions._skipSingleNestedGetters = false;
28552866

28562867
// remember the root transform function
28572868
// to save it from being overwritten by sub-transform functions
@@ -2860,16 +2871,15 @@ Document.prototype.$toObject = function(options, json) {
28602871
let ret = clone(this._doc, cloneOptions) || {};
28612872

28622873
if (options.getters) {
2863-
applyGetters(this, ret, cloneOptions);
2864-
// applyGetters for paths will add nested empty objects;
2865-
// if minimize is set, we need to remove them.
2874+
applyGetters(this, ret, gettersOptions);
2875+
28662876
if (options.minimize) {
28672877
ret = minimize(ret) || {};
28682878
}
28692879
}
28702880

2871-
if (options.virtuals || options.getters && options.virtuals !== false) {
2872-
applyVirtuals(this, ret, cloneOptions, options);
2881+
if (options.virtuals || (options.getters && options.virtuals !== false)) {
2882+
applyVirtuals(this, ret, gettersOptions, options);
28732883
}
28742884

28752885
if (options.versionKey === false && this.schema.options.versionKey) {
@@ -3180,12 +3190,7 @@ function applyGetters(self, json, options) {
31803190
v = cur[part];
31813191
if (ii === last) {
31823192
const val = self.get(path);
3183-
// Ignore single nested docs: getters will run because of `clone()`
3184-
// before `applyGetters()` in `$toObject()`. Quirk because single
3185-
// nested subdocs are hydrated docs in `_doc` as opposed to POJOs.
3186-
if (val != null && val.$__ == null) {
3187-
branch[part] = clone(val, options);
3188-
}
3193+
branch[part] = clone(val, options);
31893194
} else if (v == null) {
31903195
if (part in cur) {
31913196
branch[part] = v;

lib/utils.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ exports.clone = function clone(obj, options, isArrayChild) {
193193
}
194194

195195
if (isMongooseObject(obj)) {
196+
// Single nested subdocs should apply getters later in `applyGetters()`
197+
// when calling `toObject()`. See gh-7442, gh-8295
198+
if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
199+
options = Object.assign({}, options, { getters: false });
200+
}
196201
if (options && options.json && typeof obj.toJSON === 'function') {
197202
return obj.toJSON(options);
198203
}

0 commit comments

Comments
 (0)