Skip to content

Commit 543b1c8

Browse files
committed
fix: avoid unnecessarily setting populate virtuals to [] if query didn't have populate()
Fix #79 Re: Automattic/mongoose#10816
1 parent cfe3ff9 commit 543b1c8

File tree

2 files changed

+99
-19
lines changed

2 files changed

+99
-19
lines changed

index.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,9 @@ function attachVirtualsToDoc(schema, doc, virtuals) {
218218
cur = cur[sp[j]];
219219
}
220220
let val = virtualType.applyGetters(cur[sp[sp.length - 1]], doc);
221-
if (isPopulateVirtual(virtualType) && val === undefined) {
222-
if (virtualType.options.justOne) {
223-
val = null;
224-
} else {
225-
val = [];
226-
}
227-
}
228221
cur[sp[sp.length - 1]] = val;
229222
}
230223
}
231224

232-
function isPopulateVirtual(virtualType) {
233-
return virtualType.options && (virtualType.options.ref || virtualType.options.refPath);
234-
}
235-
236225
module.exports.defaults = module.exports;
237226
module.exports.mongooseLeanVirtuals = module.exports;

test/index.test.js

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -776,35 +776,126 @@ describe('Discriminators work', () => {
776776
childSchema.virtual('uri').get(function() {
777777
// This `uri` virtual is in a subdocument, so in order to get the
778778
// parent's `uri` you need to use this plugin's `parent()` function.
779-
779+
780780
const parent = this instanceof mongoose.Document
781781
? this.parent()
782782
: mongooseLeanVirtuals.parent(this)
783783
;
784784
return `${parent.uri}/child/gh-64-child`;
785785
});
786-
786+
787787
const parentSchema = new mongoose.Schema({
788788
child: childSchema
789789
});
790790
parentSchema.virtual('uri').get(function() {
791791
return `/parent/gh-64-parent`;
792792
});
793-
793+
794794
parentSchema.plugin(mongooseLeanVirtuals);
795-
795+
796796
const Parent = mongoose.model('gh64', parentSchema);
797-
797+
798798
const doc = { child: {} };
799-
799+
800800
await Parent.create(doc);
801-
801+
802802
let result = await Parent
803803
.findOne()
804804
.lean({ virtuals: true });
805805
assert.equal(
806806
result.child.uri,
807807
'/parent/gh-64-parent/child/gh-64-child'
808-
);
808+
);
809+
});
810+
811+
it('populates empty array instead of undefined for empty virtuals with justOne: false (Automattic/mongoose#10816)', async function() {
812+
const matchSchema = new mongoose.Schema({
813+
timestamp: { type: Number }
814+
});
815+
816+
matchSchema.virtual('predictions', {
817+
ref: 'gh-67-Prediction',
818+
localField: '_id',
819+
foreignField: 'match_id',
820+
justOne: false
821+
});
822+
823+
matchSchema.plugin(mongooseLeanVirtuals);
824+
const Match = mongoose.model('gh-67-Match', matchSchema);
825+
826+
const predictionSchema = new mongoose.Schema({
827+
match_id: { type: mongoose.Schema.Types.ObjectId }
828+
});
829+
830+
mongoose.model('gh-67-Prediction', predictionSchema);
831+
832+
const match = await Match.create({ timestamp: 1577836800000 });
833+
834+
const doc = await Match.findById(match._id)
835+
.populate('predictions')
836+
.lean({ virtuals: true });
837+
838+
// Should be an empty array, not undefined
839+
assert.deepEqual(doc.predictions, []);
840+
});
841+
842+
it('toObject() matches lean() for unpopulated virtuals (gh-79)', async function() {
843+
const parentSchema = new mongoose.Schema({
844+
firstName: String,
845+
lastName: String,
846+
});
847+
848+
parentSchema.virtual('children', {
849+
foreignField: 'parentId',
850+
justOne: false,
851+
localField: '_id',
852+
ref: 'gh-71-children',
853+
});
854+
855+
parentSchema.plugin(mongooseLeanVirtuals);
856+
const ParentModel = mongoose.model('gh-71-parents', parentSchema);
857+
858+
const childSchema = new mongoose.Schema({
859+
firstName: String,
860+
lastName: String,
861+
nicknames: [String],
862+
parentId: mongoose.Schema.Types.ObjectId,
863+
});
864+
865+
childSchema.virtual('parent', {
866+
foreignField: '_id',
867+
justOne: true,
868+
localField: 'parentId',
869+
ref: 'gh-71-parents',
870+
});
871+
872+
childSchema.plugin(mongooseLeanVirtuals);
873+
const ChildModel = mongoose.model('gh-71-children', childSchema);
874+
875+
const parent = await ParentModel.create({
876+
firstName: 'John',
877+
lastName: 'Doe',
878+
});
879+
880+
const child = await ChildModel.create({
881+
firstName: 'Jane',
882+
lastName: 'Doe',
883+
// No parent id
884+
});
885+
886+
const objParent = parent.toObject({ virtuals: true });
887+
const leanParent = await ParentModel.findById(parent._id).lean({
888+
virtuals: true,
889+
});
890+
891+
const objChild = child.toObject({ virtuals: true });
892+
const leanChild = await ChildModel.findById(child._id).lean({ virtuals: true });
893+
894+
// Should behave identically to toObject - virtual refs should be undefined
895+
// when not populated, not [] or null
896+
assert.strictEqual(objParent.children, undefined);
897+
assert.strictEqual(leanParent.children, undefined);
898+
assert.strictEqual(objChild.parent, undefined);
899+
assert.strictEqual(leanChild.parent, undefined);
809900
});
810901
});

0 commit comments

Comments
 (0)