diff --git a/lib/schema.js b/lib/schema.js index 2ed2bd3c97f..c501792c529 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -2217,11 +2217,15 @@ Schema.prototype.virtual = function(name, options) { } // Workaround for gh-8198: if virtual is under document array, make a fake - // virtual. See gh-8210 + // virtual. See gh-8210, gh-13189 const parts = name.split('.'); let cur = parts[0]; for (let i = 0; i < parts.length - 1; ++i) { - if (this.paths[cur] != null && this.paths[cur].$isMongooseDocumentArray) { + if (this.paths[cur] == null) { + continue; + } + + if (this.paths[cur].$isMongooseDocumentArray || this.paths[cur].$isSingleNested) { const remnant = parts.slice(i + 1).join('.'); this.paths[cur].schema.virtual(remnant, options); break; diff --git a/test/model.populate.test.js b/test/model.populate.test.js index 3e6cbf79848..b5d0795aa6c 100644 --- a/test/model.populate.test.js +++ b/test/model.populate.test.js @@ -7934,13 +7934,19 @@ describe('model: populate:', function() { assert.equal(res.nested.events[0].nestedLayer.users_$[0].name, 'test'); }); - it('accessing populate virtual prop (gh-8198)', async function() { + it('accessing populate virtual prop (gh-13189) (gh-8198)', async function() { const FooSchema = new Schema({ name: String, children: [{ barId: { type: Schema.Types.ObjectId, ref: 'Test' }, quantity: Number - }] + }], + child: new Schema({ + barId: { + type: 'ObjectId', + ref: 'Test' + } + }) }); FooSchema.virtual('children.bar', { ref: 'Test', @@ -7948,6 +7954,11 @@ describe('model: populate:', function() { foreignField: '_id', justOne: true }); + FooSchema.virtual('child.bars', { + ref: 'Test', + localField: 'child.barId', + foreignField: '_id' + }); const BarSchema = Schema({ name: String }); const Foo = db.model('Test1', FooSchema); const Bar = db.model('Test', BarSchema); @@ -7955,10 +7966,18 @@ describe('model: populate:', function() { const bar = await Bar.create({ name: 'bar' }); const foo = await Foo.create({ name: 'foo', - children: [{ barId: bar._id, quantity: 1 }] + children: [{ barId: bar._id, quantity: 1 }], + child: { + barId: bar._id + } }); - const foo2 = await Foo.findById(foo._id).populate('children.bar'); + const foo2 = await Foo.findById(foo._id).populate('children.bar child.bars'); assert.equal(foo2.children[0].bar.name, 'bar'); + assert.equal(foo2.child.bars[0].name, 'bar'); + + const asObject = foo2.toObject({ virtuals: true }); + assert.equal(asObject.children[0].bar.name, 'bar'); + assert.equal(asObject.child.bars[0].name, 'bar'); }); describe('gh-8247', function() {