DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Virtuals in mongoose
Godwin Alexander
Godwin Alexander

Posted on

Virtuals in mongoose

What are Virtuals?

We may wish to have certain properties that we can call on our documents but don't want to save those properties in the database.
Examples of such properties are properties that

  • get the full name of a user,
  • get the number of a comments from the comments array of a user.

These properties are usually not required when the document is created, but occur as a result of some sort of processing carried out on the document.
As stated in the mongoose documentation, Virtuals are document properties that you can get and set but that do not get persisted to MongoDB.
Let's get to creating some virtuals of our own, Its an easy process in mongoose.
Suppose we have the following user model,

const userSchema = new mongoose.Schema({
    firstname: {
        type: String,
        required: true
    },
    lastname: {
        type: String
    }
})

const User = mongoose.model('user', userSchema)
Enter fullscreen mode Exit fullscreen mode

and we want access to a user's full name (i.e firstname + lastname). We might do something like

const user = await User.create({
     firstname: 'Godwin',
     lastname: 'Alexander'
})

console.log(`${user.firstname} ${user.lastname}`) 
Enter fullscreen mode Exit fullscreen mode

This would suffice for an operation that's as simple as getting the full name of a user, imagine if we had to carry out more sophisticated processing on the data model that requires 10+ lines of code in realtime. Having to write those 10+ lines of code over and over again becomes boring and cumbersome, thankfully there's a way out. All you have to do is declare a virtual in the data model as follows

userSchema.virtual('fullname')
.get(function() {
    return `${this.firstname} ${this.lastname}`
})
Enter fullscreen mode Exit fullscreen mode

This logic creates a virtual field on the schema called fullname which returns a string containing the firstname and lastname.
The fullname virtual is not stored in Mongodb, rather it is created during runtime and is attached to model.

const user = await User.create({
        firstname: 'money',
        lastname: 'man'
    })
console.log(`Welcome ${user.fullname}`) // money man
Enter fullscreen mode Exit fullscreen mode

As we can see from the above logic, virtuals also help with abstraction.

Warning: Since virtuals aren't stored in mongodb, you can't query with them. Doing something like this will fail

const users = await User.findOne({ fullname: 'Godwin Alexander' })
Enter fullscreen mode Exit fullscreen mode

Apart from the get method on virtuals, a set method is also available. Setters are useful for de-composing a single value into multiple values for storage.
Suppose we update our userSchema to look like the following code

const userSchema = new mongoose.Schema({
    firstname: {
        type: String,
        required: true
    },
    lastname: {
        type: String
    },
    initials: {
        type: String
    }
})
Enter fullscreen mode Exit fullscreen mode

Where initials is simply a string made up of the first letter of a user's first name and the first letter of the last name. We don't want to ask user to provide their initials, of course not. Mongoose provides a way to set such values, as long as they are specified in the schema.

userSchema.virtual('setInitials')
    .set(function(name) {
        const [first, last] = name.split(' ')
        this.initials = `${first[0]}${last[0]}`.toUpperCase()
    })
Enter fullscreen mode Exit fullscreen mode

Now, we can do this

const user = await User.create({
        firstname: 'mayweather',
        lastname: 'floyd'
    })

user.setInitials = user.fullname
console.log(user.initials) //MF
Enter fullscreen mode Exit fullscreen mode

That's all for this tutorial, the mongoose documentation however could give you more insight into virtuals and other amazing things which could be done with mongoose and Mongodb. See you in the next article.

Top comments (5)

Collapse
 
mfitrie profile image
mfit3

Thanks for this article πŸ™‚πŸ™‚

Collapse
 
muhammadzohaibawan profile image
Zohaib-Muhammad

What i have learnt from this : please add your points or make me correct if i am wrong thanks .

  1. virtuals are the properties that are described in models but not be persisted in database we use them for getting the values but not save those acquired values in database
  2. while defining we use getters & setters and instead of arrow functions we use normal function because arrow function does not have it's own "this keyword"
  3. we can not query virtual properties as it is not real time database persisted data to be queried
Collapse
 
srikanth209 profile image
srikanth

Excellent explanation of virtualsπŸ‘πŸ‘πŸ‘πŸ‘

Collapse
 
maheshmulik111 profile image
maheshmulik111

Helpful !!
Thanks :)

Collapse
 
oviecodes profile image
Godwin Alexander

Glad I could help.

Build Anything...


Use any Linode offering to create something for the DEV x Linode Hackathon 2022. A variety of prizes are up for grabs, inculding $1,000 USD. πŸ‘€

β†’ Join the Hackathon <-