Mongoose Virtuals

    Suppose you have a User model. Every user has an email, but you also want the email’s domain. For example, the domain portion of ‘test@gmail.com’ is ‘gmail.com’.

    Below is one way to implement the domain property using a virtual. You define virtuals on a schema using the Schema#virtual() function.

    The Schema#virtual() function returns a . Unlike normal document properties, virtuals do not have any underlying value and Mongoose does not do any type coercion on virtuals. However, virtuals do have getters and setters, which make them ideal for computed properties, like the domain example above.

    Virtual Setters

    1. const userSchema = mongoose.Schema({
    2. lastName: String
    3. });
    4. // Create a virtual property `fullName` with a getter and setter.
    5. userSchema.virtual('fullName').
    6. get(function() { return `${this.firstName} ${this.lastName}`; }).
    7. set(function(v) {
    8. // `firstName` and `lastName`.
    9. const firstName = v.substring(0, v.indexOf(' '));
    10. const lastName = v.substring(v.indexOf(' ') + 1);
    11. this.set({ firstName, lastName });
    12. });
    13. const User = mongoose.model('User', userSchema);
    14. const doc = new User();
    15. // Vanilla JavaScript assignment triggers the setter
    16. doc.fullName; // 'Jean-Luc Picard'
    17. doc.firstName; // 'Jean-Luc'
    18. doc.lastName; // 'Picard'

    By default, Mongoose does not include virtuals when you convert a document to JSON. For example, if you pass a document to , virtuals will not be included by default.

    To include virtuals in res.json(), you need to set the toJSON schema option to { virtuals: true }.

    Virtuals in console.log()

    By default, Mongoose does not include virtuals in output. To include virtuals in console.log(), you need to set the to { virtuals: true }, or use toObject() before printing the object.

    1. console.log(doc.toObject({ virtuals: true }));

    If you use lean() for performance, but still need virtuals, Mongoose has an officially supported mongoose-lean-virtuals plugin that decorates lean documents with virtuals.

    Limitations

    Mongoose virtuals are not stored in MongoDB, which means you can’t query based on Mongoose virtuals.

    1. // Will **not** find any results, because `domain` is not stored in
    2. // MongoDB.
    3. mongoose.set('debug', true)
    4. const doc = await User.findOne({ domain: 'gmail.com' }, null, { strictQuery: false });
    5. doc; // undefined

    If you want to query by a computed property, you should set the property using a or pre save middleware.

    • The ref option, which tells Mongoose which model to populate documents from.
    • The localField and foreignField options. Mongoose will populate documents from the model in ref whose foreignField matches this document’s localField.

    Further Reading