TIL — Virtual Fields in Mongoose

Virtual fields are a powerful feature provided by Mongoose, a popular Object Document Mapper (ODM) for MongoDB. Virtual fields are document properties that are not persisted in the database but can be computed on the fly based on other properties of the document. They are particularly useful for simplifying complex operations, improving code readability, and optimizing application performance.

Adding Virtual Fields in Mongoose

To add a virtual field to a Mongoose schema, you can use the virtual() method on the schema object. This method accepts the name of the virtual field as its argument. You can then define a getter function that computes the value of the virtual field based on other fields in the document.

Consider a MongoDB collection called “books” with the following documents:

[
{ "_id": 1, "title": "Book One", "price": 20, "tax": 2 },
{ "_id": 2, "title": "Book Two", "price": 25, "tax": 2.5 },
{ "_id": 3, "title": "Book Three", "price": 30, "tax": 3 }
]

Let’s say we want to calculate the total price (price + tax) of each book without persisting this information in the database. We can create a Mongoose schema with a virtual field named “totalPrice” to achieve this:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true });

const bookSchema = new mongoose.Schema({
title: String,
price: Number,
tax: Number
});

bookSchema.virtual('totalPrice').get(function() {
return this.price + this.tax;
});

const Book = mongoose.model('Book', bookSchema);

(async () => {
try {
const books = await Book.find();
books.forEach(book => {
console.log(`Title: ${book.title}, Price: ${book.price}, Tax: ${book.tax}, Total Price: ${book.totalPrice}`);
});
} catch (err) {
console.error(err);
} finally {
await mongoose.connection.close();
}
})();

In this example, we create a virtual field called “totalPrice” that computes the sum of the “price” and “tax” fields. When we query the books and log their properties, we can see the total price of each book without it being stored in the database.

Note that virtual fields are not included in the output when converting a Mongoose document to JSON or plain JavaScript objects by default. To include virtual fields in the output, you must set the toObject and toJSON options on the schema:

bookSchema.set('toObject', { virtuals: true });
bookSchema.set('toJSON', { virtuals: true });