Virtuals vs Methods in Mongoose

Neither Instance methods, static methods, or virtuals are stored in the database. The difference between methods and virtuals is that virtuals are accessed like properties and methods are called like functions. There's no distinction between instance/static with virtuals because it makes no sense to have a virtual static property accessible on the class, but it does make sense to have some static utility or factory methods on the class.

var PersonSchema = new Schema({
  name: {
    first: String,
    last: String
  }
});

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

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

var person = new Person({
  name: {
    first: 'Alex',
    last: 'Ford'
  }
});

console.log(person.name.full);

// would print "Alex Ford" to the console

Whereas methods are called like normal functions.

PersonSchema.method('fullName', function () {
  return this.name.first + ' ' + this.name.last;
});

var person = new Person({
  name: {
    first: 'Alex',
    last: 'Ford'
  }
});

console.log(person.fullName());

// notice this time you call fullName like a function

You can also "set" virtual properties like you're used to with regular properties. Just call .get and .set to setup the functionality for both actions. Notice in the .get you return a value, whereas in the .set you accept a value and use it to set non-virtual properties on your document.

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

var person = new Person({
  name: {
    first: 'Alex',
    last: 'Ford'
  }
});

console.log(person.name.first);

// would log out "Alex"

person.name.full = 'Billy Bob';

// would set person.name.first and person.name.last appropriately

console.log(person.name.first);

// would log out "Billy"

You could technically use methods for everything and never use virtual properties, but virtual properties are elegant for certain things such as the examples I've shown here with person.name.full.


I was trying to figure out when to use Virtuals and when to use Instances and I would summarize it like this:

Statics - For methods (operations) that require no instance of a model to be run. Or, generic operations against the model.

e.g. finding a document, listing all documents.

Instances - For methods (operations) that do require an instance of the model to be present.

e.g. finding or retrieving data based on the current instance data. Like other documents associated with the instance. model.getRelatedDocs()

Virtuals - For reading properties of the model and making it clear at code level that those are properties/attributes and not methods. Also, for composed properties based on data that is already loaded in the instance. e.g. the trypical full name example.