Skip to content

Commit

Permalink
Merge pull request #14565 from Automattic/vkarpov15/gh-14394
Browse files Browse the repository at this point in the history
perf(document): avoid cloning options using spread operator for perf reasons
  • Loading branch information
vkarpov15 authored May 6, 2024
2 parents 3b00122 + 31596d5 commit 13db408
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 32 deletions.
37 changes: 18 additions & 19 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -3780,7 +3780,7 @@ Document.prototype.$__handleReject = function handleReject(err) {
*/

Document.prototype.$toObject = function(options, json) {
let defaultOptions = {
const defaultOptions = {
transform: true,
flattenDecimals: true
};
Expand All @@ -3793,7 +3793,7 @@ Document.prototype.$toObject = function(options, json) {
const schemaOptions = this.$__schema && this.$__schema.options || {};
// merge base default options with Schema's set default options if available.
// `clone` is necessary here because `utils.options` directly modifies the second input.
defaultOptions = { ...defaultOptions, ...baseOptions, ...schemaOptions[path] };
Object.assign(defaultOptions, baseOptions, schemaOptions[path]);

// If options do not exist or is not an object, set it to empty object
options = utils.isPOJO(options) ? { ...options } : {};
Expand Down Expand Up @@ -3830,21 +3830,18 @@ Document.prototype.$toObject = function(options, json) {
// `clone()` will recursively call `$toObject()` on embedded docs, so we
// need the original options the user passed in, plus `_isNested` and
// `_parentOptions` for checking whether we need to depopulate.
const cloneOptions = Object.assign({}, options, {
const cloneOptions = {
_isNested: true,
json: json,
minimize: _minimize,
flattenMaps: flattenMaps,
flattenObjectIds: flattenObjectIds,
_seen: (options && options._seen) || new Map()
});

if (utils.hasUserDefinedProperty(options, 'getters')) {
cloneOptions.getters = options.getters;
}
if (utils.hasUserDefinedProperty(options, 'virtuals')) {
cloneOptions.virtuals = options.virtuals;
}
_seen: (options && options._seen) || new Map(),
_calledWithOptions: options._calledWithOptions,
virtuals: options.virtuals,
getters: options.getters,
depopulate: options.depopulate
};

const depopulate = options.depopulate ||
(options._parentOptions && options._parentOptions.depopulate || false);
Expand All @@ -3855,33 +3852,35 @@ Document.prototype.$toObject = function(options, json) {
}

// merge default options with input options.
options = { ...defaultOptions, ...options };
for (const key of Object.keys(defaultOptions)) {
if (options[key] == null) {
options[key] = defaultOptions[key];
}
}
options._isNested = true;
options.json = json;
options.minimize = _minimize;

cloneOptions._parentOptions = options;
cloneOptions._skipSingleNestedGetters = false;

const gettersOptions = Object.assign({}, cloneOptions);
gettersOptions._skipSingleNestedGetters = true;

cloneOptions._skipSingleNestedGetters = false;
// remember the root transform function
// to save it from being overwritten by sub-transform functions
const originalTransform = options.transform;

let ret = clone(this._doc, cloneOptions) || {};

cloneOptions._skipSingleNestedGetters = true;
if (options.getters) {
applyGetters(this, ret, gettersOptions);
applyGetters(this, ret, cloneOptions);

if (options.minimize) {
ret = minimize(ret) || {};
}
}

if (options.virtuals || (options.getters && options.virtuals !== false)) {
applyVirtuals(this, ret, gettersOptions, options);
applyVirtuals(this, ret, cloneOptions, options);
}

if (options.versionKey === false && this.$__schema.options.versionKey) {
Expand Down
7 changes: 1 addition & 6 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,32 +722,27 @@ describe('document', function() {
lastName: String,
password: String
});

userSchema.virtual('fullName').get(function() {
return this.firstName + ' ' + this.lastName;
});

userSchema.set('toObject', { virtuals: false });

const postSchema = new Schema({
owner: { type: Schema.Types.ObjectId, ref: 'User' },
content: String
});

postSchema.virtual('capContent').get(function() {
return this.content.toUpperCase();
});

postSchema.set('toObject', { virtuals: true });

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

const user = new User({ firstName: 'Joe', lastName: 'Smith', password: 'password' });

const savedUser = await user.save();

const post = await Post.create({ owner: savedUser._id, content: 'lorem ipsum' });

const newPost = await Post.findById(post._id).populate('owner').exec();

const obj = newPost.toObject();
Expand Down
8 changes: 1 addition & 7 deletions test/model.populate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2976,33 +2976,27 @@ describe('model: populate:', function() {
return ret;
}
});

const Team = db.model('Test', teamSchema);

const userSchema = new Schema({
username: String
});

userSchema.set('toJSON', {
transform: function(doc, ret) {
return ret;
}
});

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

const user = new User({ username: 'Test' });

await user.save();

const team = new Team({ members: [{ user: user }] });

await team.save();

await team.populate('members.user');

assert.equal(calls, 0);
team.toJSON();

assert.equal(calls, 1);
});

Expand Down

0 comments on commit 13db408

Please sign in to comment.