mongoose
mongoose copied to clipboard
Dynamic virtual populate not working on subdocument with separate schema
Prerequisites
- [X] I have written a descriptive issue title
- [X] I have searched existing issues to ensure the bug has not already been reported
Mongoose version
6.5.4
Node.js version
16.15.0
MongoDB server version
4.2.1
Description
Seems to be related to #8277 and #8742
When using dynamic ref on child schema { ref: (doc) => doc.refType }, the passed in doc is the parent document, not the subdocument, causing dynamic ref path not correctly returned.
I tried both virtuals option in schema constructor options and schema virtual() method, tried both ref and refPath in virtual options, none works.
Steps to Reproduce
import mongoose from 'mongoose';
import { expect } from 'chai';
const nestedSchema = new mongoose.Schema({
targetType: String,
targetId: mongoose.Schema.Types.ObjectId,
}, {
virtuals: {
target: {
options: {
ref: (doc: any) => {
// doc should be the sub document
// but instead, it is the parent document
console.debug(doc);
return doc.targetType;
},
localField: 'targetId',
foreignField: '_id',
justOne: true,
}
}
}
});
const parentSchema = new mongoose.Schema({
nested: {
type: nestedSchema
},
});
const NestedDynamicVirtualPopulateTest = mongoose.model('NestedDynamicVirtualPopulateTest', parentSchema);
await NestedDynamicVirtualPopulateTest.collection.drop();
const target = new NestedDynamicVirtualPopulateTest({});
await target.save();
const parent = new NestedDynamicVirtualPopulateTest({
nested: {
targetType: (target.constructor as typeof NestedDynamicVirtualPopulateTest).modelName,
targetId: target._id,
}
});
await parent.save();
await parent.populate('nested.target');
// @ts-ignore
expect(parent.nested.target).to.exist;
Expected Behavior
Since the virtual is defined on the child schema, the dynamic ref path should be relative to the subdocument, not the parent document.

12363.ts:49:29 - error TS2551: Property 'target' does not exist on type '{ targetType?: {}; targetId?: { toString: any; _bsontype?: { toString: {}; _bsontype?: ObjectId; inspect?: {}; equals?: {}; _id?: ...; id?: { [x: number]: unknown; [toStringTag]?: unknown; [iterator]?: {}; toString: {}; ... 100 more ...; byteOffset?: unknown; }; toHexString?: {}; toJSON?: {}; generationTime?: unknow...'. Did you mean 'targetId'?
49 console.log(parent.nested.target);
~~~~~~
node_modules/mongoose/types/query.d.ts:619:29 - error TS2304: Cannot find name 'this'.
619 toConstructor(): typeof this;
~~~~
Found 2 errors.
import mongoose from 'mongoose';
const nestedSchema = new mongoose.Schema({
targetType: String,
targetId: mongoose.Schema.Types.ObjectId,
}, {
virtuals: {
target: {
options: {
ref: (doc: any) => {
// doc should be the sub document
// but instead, it is the parent document
console.debug(doc);
return doc.targetType;
},
localField: 'targetId',
foreignField: '_id',
justOne: true,
}
}
}
});
const parentSchema = new mongoose.Schema({
nested: {
type: nestedSchema
},
});
const NestedDynamicVirtualPopulateTest = mongoose.model('NestedDynamicVirtualPopulateTest', parentSchema);
async function run() {
await mongoose.connect('mongodb://localhost:27017');
await mongoose.connection.dropDatabase();
const target = new NestedDynamicVirtualPopulateTest({});
await target.save();
const parent = new NestedDynamicVirtualPopulateTest({
nested: {
targetType: (target.constructor as typeof NestedDynamicVirtualPopulateTest).modelName,
targetId: target._id,
}
});
await parent.save();
await parent.populate('nested.target');
console.log(parent);
console.log('=============');
console.log(parent.nested);
console.log('=================');
console.log(parent.nested.target);
console.log('======================')
}
run();
Hi @IslandRhythms, the code snippet in my initial comment was not properly typed, because I thought the type definition does not do much help demonstrating the issue and will make the snippet unnecessarily long. So I just put @ts-ignore before the parent.nested.target part to avoid typing errors :)
Hi @IslandRhythms, I think the "typescript" label on this issue is not appropriate. The typing error in your comment is not related to this issue. And this issue is not related to typescript.