Mongoose schema virtual attribute always returns undefined

Viewed 802

I was trying to use mongoose schema virtual functions to implement flags, But I could not make them work The mongoose database is hosted on mongodb altas. I have tried deleting the whole collection and staring again.

Here is a simplified example:

Let us say I have a basic User Schema:

const mongoose = require('mongoose')
const UserSchema = new mongoose.Schema({
  name: String,
  email: String
}, {toObject: {virtuals: true, getters: true}})

UserSchema.virtual('status').get(() => {
   return this.name
})

const User = mongoose.model('User', UserSchema)

module.exports = {
    User,
    UserSchema
}

In app.js I have the following:

const mongoose = require("mongoose");
const express = require("express");
const {User} = require("./models/User")
const app = express();

app.use(express.urlencoded({extended: true}));
app.use(express.json());

mongoose.connect(process.env.DB_URL, {
    useNewUrlParser: true,
    useUnifiedTopology: true
});

User.findById("600ae8001931ad49eae40c03", (err, doc) => {
    console.log(doc.status)
})

const port = process.env.PORT || 5000;

app.listen(port, () => {
    console.log(`server at http://localhost:${port}`);
});

I made sure the id exists, but the result is always undefined. What am I doing wrong here?

3 Answers

based on mongoose documentation

Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document and the above examples will not work

so you can not use arrow function in get, do like this :

UserSchema.virtual('status').get(function() {
  return this.name
})

It's because of how you are declaring your function being passed to get. The way you are doing it is changing the this reference.

UserSchema.virtual('status').get(function() {
  return this.name
})

The above would maintain it

To future users peaple that looking for the answer, beside this answer - if it's not solve the problem and still get undefined -

the problem maybe because you didn't selec the attribute that used in the virtual middleware

Our Schema

const monthSchema = new mongoose.Schema(
  {
    name: String,
    numOfDays: Number,

  },
  { toJSON: { virtuals: true }, toObject: { virtuals: true } }
);

if you use numOfDays in virtual middleware to calculate the weeks like this:

MonthSchema.virtual('NumOfWeeks').get(function() {
  return this.numOfDays / 7
})

this you must be sure that numOfDays are selected

const user = await User.find().select('name'); // this.numOfDays will be undifined

const user = await User.find().select('-numOfDays'); // this.numOfDays will be undifined

const user = await User.find().select('name numOfDays'); // this.numOfDays will be not undifined 

I spent whole day to figure this, may be this will help someone

Related