Kevin
Kevin

Reputation: 25269

mongoose discriminators behavior

I have a few questions about mongoose discriminators.

Do they support multiple levels of inheritance? Like Model C inherits Model B which inherits Model A? From reading the documentation it doesn't appear this is possible, it explains only one level of inheritance, e.g. I have a generic event and then I have specific event types under that.

Is it possible to query for all matching discriminator types in a query? E.g. have a base Event, and I have specific event like a ClickEvent and BrowseEvent. And maybe the base event has a user_id field and a timestamp. And I want both click and browse events that happened in the last 1 hour by a specific user. Is that possible? The documentation goes out of its way to explain that the various finder methods are discriminator aware, which it defines as meaning they automatically select the right kind object based on how it was queried. E.g. ClickEvent.findOne returns only click events. Ok fair enough, but does GenericEvent.findOne return all events, or just generic events?

Upvotes: 2

Views: 2390

Answers (2)

shontauro
shontauro

Reputation: 1822

This is an example about how to define different discriminators:

var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var Business = require('../models/business');
var BusinessProductCategory = require('../models/businessProductCategory');
var MasterProduct = require('../models/masterProduct');

var common = require('../middlewares/common');

var nameMinLength = [3, 'The value of path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).'];

var options = {discriminatorKey: 'kind'};

var businessProductSchema = mongoose.Schema({
    business: {type: Schema.Types.ObjectId, ref: 'Business'},
    productPrices: [{
        value: {type: Number, required: true},
        currency: {type: String, required: true},
        description: {type: String, required: true},
        createdAt: {type: Date, default: Date.now, required: true},
        updatedAt: {type: Date, default: Date.now, required: true}
    }],
    is_show_in_store: {type: Boolean, default: true, required: true},
    record_status: {type: String, enum: ['active', 'archived'], required: true, default: 'active'},
}, options);

businessProductSchema.post('save', common.handlerMongoErrors);
businessProductSchema.post('update', common.handlerMongoErrors);

var BusinessProduct = mongoose.model("BusinessProduct", businessProductSchema);

var ManagedProduct = BusinessProduct.discriminator('ManagedProduct', new mongoose.Schema({
    master_product: {type: Schema.Types.ObjectId, ref: 'MasterProduct'},
}), options);

var LocalProduct = BusinessProduct.discriminator('LocalProduct', new mongoose.Schema({
    name: {type: String, required: true, minlength: nameMinLength},
    description: {type: String},
    primary_category: {type: Schema.Types.ObjectId, ref: 'BusinessProductCategory'},
    brand: {type: String, required: true},
    created_at: {type: Date, required: true},
    updated_at: {type: Date, required: true},
}, options));

module.exports = {BusinessProduct: BusinessProduct, ManagedProduct: ManagedProduct, LocalProduct: LocalProduct};

You can query all the documents using the ParentModel in the find() method, if you use a child in the find() method so you will filter only the types of the child used to call the find.

Upvotes: 2

Kevin
Kevin

Reputation: 25269

So for lack of answers, I decided to stop being lazy and write a program to test out the behavior. So what I found is that if you try to have an inheritance chain it throws an error along the lines of Discriminator "Derived2" can only be a discriminator of the root model. So you can only have a Base schema and then Derived schemas from that Base. However, if you query using Base.findOne you'll find any derived type.

Upvotes: 6

Related Questions