Search code examples
javascriptinheritancemongoosediscriminator

Mongoose discriminators, instantiate documents


We're using inherited schemas with Mongoose's discriminator functionality, which we implemented according to the documentation.

Through our REST API we want to POST documents of the different types and handle the logic with a common controller function.

Suppose, we have a REST method for creating a document, which can be either of super type GenericEvent, or sub types ClickedLinkEvent, or SignedUpEvent (to follow the example from the docs linked above). What I'm currently doing is something like:

var GenericEventModel = require('GenericEventModel');
var ClickedLinkEventModel = require('ClickedLinkEvent');
var SignedUpEventModel = require('SignedUpEvent');

// REST logic for creating a document of a specific type
module.exports.createEvent = function(req, res, next) {
   var modelType = req.params.type; // as provided via REST parameter
   var Model = getModel(modelType); // get the appropriate model type
   new Model(req.body).save(next);
}

// TODO want to avoid this
function getMongooseModel(type) {
  switch (modelType) {
    case 'GenericEvent': return GenericEventModel;
    case 'ClickedLinkEvent': return ClickedLinkEventModel;
    case 'SignedUpEvent': return SignedUpEventModel;
    // and many more, needs to be extended, every time a new type is added …
  }
}

Having to manually curate the getMongooseModel function whenever a new model type is added seems rather error-prone, and I suspect other people who will be working on the code will simply forget about it.

So, my question: Is there an existing function in the Mongoose API which can give me the appropriate model for a given discriminator key by looking at all known sub schemas?


Solution

  • A better implementation should be to use an object instead of a switch.

    var modelMap = {
      'GenericEvent': GenericEventModel,
      'ClickedLinkEvent': ClickedLinkEventModel,
      'SignedUpEvent': SignedUpEventModel
    };
    
    function getMongooseModel(type) {
      return modelList[type];
    }
    

    After that in order to avoid errors you can use Mongoose#plugin(fn, [opts]) in order to populate the modelMap object.

    Or you can just use Mongoose#model(name, [schema], [collection], [skipInit]).

    Defines a model or retrieves it.

    Something like this should work:

    module.exports.createEvent = function(req, res, next) {
      var Model = mongoose.model(req.params.type);
      (new Model(req.body)).save(next);
    }
    

    With a proper declaration of your model class in mongoose, model name must match your request parameter of course.

    var GenericEventModel = mongoose.model('GenericEvent', GenericEventModelSchema);