I have following schema:
// foo.js
...
const fooSchema = new Schema(
{
name: {
type: String
}
},
{
toJSON: {
virtuals: true
},
toObject: {
virtual: true
}
}
);
// sample virtual
fooSchema.virtual('sampleVirtual').get(function () {
return 'I am a virtual'
});
export default mongoose.model( 'foo', fooSchema );
and another schema where I have a ref on the above schema:
// hello.js
...
const helloWorldSchema = new Schema(
{
name: {
type: String,
required: true
},
myFoo: {
type: mongoose.Types.ObjectId,
ref: 'foo'
}
},
{
toJSON: {
virtuals: true
},
toObject: {
virtual: true
}
}
);
export default mongoose.model( 'HelloWorld', helloWorldSchema );
I am now using Mongoose' findOne()
method to query for a document of the helloWorldSchema
collection and want to populate the myFoo
document with it's virtual sampleVirtual
:
// get.js
const retVal = await helloWorldModel
.findOne({_id: <some-id> })
.populate({ path: 'myFoo', select: '_id name sampleVirtual' })
.exec();
console.log(retVal.myFoo.sampleVirtual) //undefined
Am I doing something wrong?
Best Valentin
Your code has some typos. The toJSON
and toObject
are schema options, and the options
is the second parameter of the mongoose.Schema
class constructor. The mongoose.Schema
class constructor signature is:
constructor(definition?: SchemaDefinition<LeanDocument<SchemaDefinitionType>>, options?: SchemaOptions);
E.g. ("mongoose": "^7.3.1")
import mongoose from 'mongoose';
import { config } from '../../config';
mongoose.set('debug', true);
const fooSchema = new mongoose.Schema(
{
name: String,
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);
fooSchema.virtual('sampleVirtual').get(function () {
return 'I am a virtual';
});
const Foo = mongoose.model('foo', fooSchema);
const helloWorldSchema = new mongoose.Schema(
{
name: String,
myFoo: { type: mongoose.Schema.Types.ObjectId, ref: 'foo' },
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);
const HelloWorld = mongoose.model('helloWorld', helloWorldSchema);
(async function main() {
try {
await mongoose.connect(config.MONGODB_URI);
// seed
const [foo1] = await Foo.create([{ name: 'a' }, { name: 'b' }]);
const [h1] = await HelloWorld.create([
{ name: 'x', myFoo: foo1._id },
{ name: 'y', myFoo: foo1._id },
]);
const retVal = await HelloWorld.findOne({ _id: h1._id })
.populate({ path: 'myFoo', select: '_id name sampleVirtual' })
.exec();
console.log(retVal);
} catch (error) {
console.error(error);
} finally {
await mongoose.connection.close();
}
})();
Debug logs:
Mongoose: foos.insertOne({ name: 'a', _id: ObjectId("649a7bdfbedaa852fa187538"), __v: 0 }, {})
Mongoose: foos.insertOne({ name: 'b', _id: ObjectId("649a7bdfbedaa852fa187539"), __v: 0 }, {})
Mongoose: helloworlds.insertOne({ name: 'x', myFoo: ObjectId("649a7bdfbedaa852fa187538"), _id: ObjectId("649a7bdfbedaa852fa18753c"), __v: 0}, {})
Mongoose: helloworlds.insertOne({ name: 'y', myFoo: ObjectId("649a7bdfbedaa852fa187538"), _id: ObjectId("649a7bdfbedaa852fa18753d"), __v: 0}, {})
Mongoose: helloworlds.findOne({ _id: ObjectId("649a7bdfbedaa852fa18753c") }, {})
Mongoose: foos.find({ _id: { '$in': [ ObjectId("649a7bdfbedaa852fa187538") ], [Symbol(mongoose#trustedSymbol)]: true }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined, projection: { _id: 1, name: 1, sampleVirtual: 1 }})
{
_id: new ObjectId("649a7bdfbedaa852fa18753c"),
name: 'x',
myFoo: {
_id: new ObjectId("649a7bdfbedaa852fa187538"),
name: 'a',
sampleVirtual: 'I am a virtual',
id: '649a7bdfbedaa852fa187538'
},
__v: 0,
id: '649a7bdfbedaa852fa18753c'
}
The virtual field sampleVirtual
is there.