Skip to content

Commit 33c2f4f

Browse files
authored
Merge pull request #14314 from Automattic/vkarpov15/gh-14285
fix(populate): call setter on virtual populated path with populated doc instead of undefined
2 parents 8ef0d9a + fc9df6b commit 33c2f4f

File tree

3 files changed

+95
-18
lines changed

3 files changed

+95
-18
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
3+
/**
4+
* Set a populated virtual value on a document's `$$populatedVirtuals` value
5+
*
6+
* @param {*} populatedVirtuals A document's `$$populatedVirtuals`
7+
* @param {*} name The virtual name
8+
* @param {*} v The result of the populate query
9+
* @param {*} options The populate options. This function handles `justOne` and `count` options.
10+
* @returns {Array<Document>|Document|Object|Array<Object>} the populated virtual value that was set
11+
*/
12+
13+
module.exports = function setPopulatedVirtualValue(populatedVirtuals, name, v, options) {
14+
if (options.justOne || options.count) {
15+
populatedVirtuals[name] = Array.isArray(v) ?
16+
v[0] :
17+
v;
18+
19+
if (typeof populatedVirtuals[name] !== 'object') {
20+
populatedVirtuals[name] = options.count ? v : null;
21+
}
22+
} else {
23+
populatedVirtuals[name] = Array.isArray(v) ?
24+
v :
25+
v == null ? [] : [v];
26+
27+
populatedVirtuals[name] = populatedVirtuals[name].filter(function(doc) {
28+
return doc && typeof doc === 'object';
29+
});
30+
}
31+
32+
return populatedVirtuals[name];
33+
};

lib/schema.js

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const handleReadPreferenceAliases = require('./helpers/query/handleReadPreferenc
2020
const idGetter = require('./helpers/schema/idGetter');
2121
const merge = require('./helpers/schema/merge');
2222
const mpath = require('mpath');
23+
const setPopulatedVirtualValue = require('./helpers/populate/setPopulatedVirtualValue');
2324
const setupTimestamps = require('./helpers/timestamps/setupTimestamps');
2425
const utils = require('./utils');
2526
const validateRef = require('./helpers/populate/validateRef');
@@ -2288,28 +2289,17 @@ Schema.prototype.virtual = function(name, options) {
22882289
virtual.options = options;
22892290

22902291
virtual.
2291-
set(function(_v) {
2292+
set(function(v) {
22922293
if (!this.$$populatedVirtuals) {
22932294
this.$$populatedVirtuals = {};
22942295
}
22952296

2296-
if (options.justOne || options.count) {
2297-
this.$$populatedVirtuals[name] = Array.isArray(_v) ?
2298-
_v[0] :
2299-
_v;
2300-
2301-
if (typeof this.$$populatedVirtuals[name] !== 'object') {
2302-
this.$$populatedVirtuals[name] = options.count ? _v : null;
2303-
}
2304-
} else {
2305-
this.$$populatedVirtuals[name] = Array.isArray(_v) ?
2306-
_v :
2307-
_v == null ? [] : [_v];
2308-
2309-
this.$$populatedVirtuals[name] = this.$$populatedVirtuals[name].filter(function(doc) {
2310-
return doc && typeof doc === 'object';
2311-
});
2312-
}
2297+
return setPopulatedVirtualValue(
2298+
this.$$populatedVirtuals,
2299+
name,
2300+
v,
2301+
options
2302+
);
23132303
});
23142304

23152305
if (typeof options.get === 'function') {

test/model.populate.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10868,4 +10868,58 @@ describe('model: populate:', function() {
1086810868
{ name: 'foo', prop: 'bar' }
1086910869
);
1087010870
});
10871+
10872+
it('calls setter on virtual populated path with populated doc (gh-14285)', async function() {
10873+
const userSchema = new Schema({
10874+
email: String,
10875+
name: 'String'
10876+
});
10877+
10878+
const User = db.model('User', userSchema);
10879+
10880+
const user = await User.create({
10881+
10882+
name: 'Admin'
10883+
});
10884+
10885+
const personSchema = new Schema({
10886+
userId: ObjectId,
10887+
userType: String
10888+
});
10889+
10890+
personSchema.
10891+
virtual('user', {
10892+
ref() {
10893+
return this.userType;
10894+
},
10895+
localField: 'userId',
10896+
foreignField: '_id',
10897+
justOne: true
10898+
}).
10899+
set(function(user) {
10900+
if (user) {
10901+
this.userId = user._id;
10902+
this.userType = user.constructor.modelName;
10903+
} else {
10904+
this.userId = null;
10905+
this.userType = null;
10906+
}
10907+
10908+
return user;
10909+
});
10910+
10911+
const Person = db.model('Person', personSchema);
10912+
10913+
const person = new Person({
10914+
userId: user._id,
10915+
userType: 'User'
10916+
});
10917+
10918+
await person.save();
10919+
10920+
const personFromDb = await Person.findById(person._id).populate('user');
10921+
assert.equal(personFromDb.user.name, 'Admin');
10922+
assert.equal(personFromDb.userType, 'User');
10923+
assert.equal(personFromDb.userId.toHexString(), user._id.toHexString());
10924+
});
1087110925
});

0 commit comments

Comments
 (0)