Fork me on GitHub

Mongoose

Virtual attributes

Mongoose supports virtual attributes. Virtual attributes are attributes
that are convenient to have around but that do not get persisted to mongodb.

An example is helpful.

Example

Take the following schema:

var PersonSchema = new Schema({
    name: {
        first: String
      , last: String
    }
});
If you want the virtual field to be displayed on client side, then set {virtuals: true} for toObject and toJSON in schema options as below:
var PersonSchema = new Schema({
    name: {
        first: String
      , last: String
    }
}, {
  toObject: {
  virtuals: true
  },
  toJSON: {
  virtuals: true 
  }
});
then set,

var Person = mongoose.model('Person', PersonSchema);

var theSituation = new Person({
    name: { first: 'Michael', last: 'Sorrentino' }
});

Suppose you want to write theSituation's full name. You could do so via:

console.log(theSituation.name.first + ' ' + theSituation.name.last);

It is more convenient to define a virtual attribute, name.full, so you can instead write:

console.log(theSituation.name.full); 

To do so, you can declare a virtual attribute on the Schema, Person:

PersonSchema
.virtual('name.full')
.get(function () {
  return this.name.first + ' ' + this.name.last;
});

So when you get name.full, via

theSituation.name.full;

the implementation ends up invoking the getter function

function () {
  return this.name.first + ' ' + this.name.last;
}

and returning it.

It would also be nice to be able to set this.name.first and this.name.last by setting this.name.full. For example, it would be nice if we wanted to change theSituation's name.first and name.last to 'The' and 'Situation' respectively just by invoking:

theSituation.set('name.full', 'The Situation');

Mongoose allows you to do this, too, via virtual attribute setters. You can define a virtual attribute setter thusly:

PersonSchema
.virtual('name.full')
.get(function () {
  return this.name.first + ' ' + this.name.last;
})
.set(function (setFullNameTo) {
  var split = setFullNameTo.split(' ')
    , firstName = split[0]
    , lastName = split[1];

  this.set('name.first', firstName);
  this.set('name.last', lastName);
});

Then, when you invoke:

theSituation.set('name.full', 'The Situation');

and you save the document, then name.first and name.last will be changed in monbodb, but the mongodb document will not have persisted a name.full key or value to the database:

theSituation.save(function (err) {
  Person.findById(theSituation._id, function (err, found) {
    console.log(found.name.first); // 'The'
    console.log(found.name.last);  // 'Situation'
  });
});

If you want attributes that you can get and set but that are not themselves persisted to mongodb, virtual attributes is the Mongoose feature for you.