0
0
MongodbHow-ToBeginner · 4 min read

How to Use Discriminator in Mongoose for Schema Inheritance

In Mongoose, discriminator allows you to create multiple models with a shared base schema but different specialized schemas. You define a base schema, then use Model.discriminator() to create child models that inherit the base schema and add their own fields.
📐

Syntax

The discriminator method is called on a base model to create a new model with an extended schema. It takes two main arguments: the name of the new model and the schema that extends the base schema.

  • Base Schema: The common fields shared by all models.
  • Discriminator Name: A string to identify the child model.
  • Discriminator Schema: The schema with additional fields specific to the child model.
javascript
const baseSchema = new mongoose.Schema({
  commonField: String
});

const BaseModel = mongoose.model('Base', baseSchema);

const childSchema = new mongoose.Schema({
  specificField: Number
});

const ChildModel = BaseModel.discriminator('Child', childSchema);
💻

Example

This example shows how to create a base Event model and two discriminators: ClickedLinkEvent and SignedUpEvent. Each child model has its own specific fields but shares the base schema fields.

javascript
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/testdb', { useNewUrlParser: true, useUnifiedTopology: true });

const eventSchema = new mongoose.Schema({
  timestamp: Date,
  userId: String
});

const Event = mongoose.model('Event', eventSchema);

const clickedLinkSchema = new mongoose.Schema({
  url: String
});

const signedUpSchema = new mongoose.Schema({
  plan: String
});

const ClickedLinkEvent = Event.discriminator('ClickedLink', clickedLinkSchema);
const SignedUpEvent = Event.discriminator('SignedUp', signedUpSchema);

async function run() {
  await mongoose.connection.dropDatabase();

  const click = new ClickedLinkEvent({
    timestamp: new Date(),
    userId: 'user123',
    url: 'https://example.com'
  });

  const signup = new SignedUpEvent({
    timestamp: new Date(),
    userId: 'user456',
    plan: 'Pro'
  });

  await click.save();
  await signup.save();

  const events = await Event.find();
  console.log(events);

  mongoose.connection.close();
}

run();
Output
[ { _id: ObjectId("..."), timestamp: 2024-06-01T00:00:00.000Z, userId: 'user123', url: 'https://example.com', __t: 'ClickedLink', __v: 0 }, { _id: ObjectId("..."), timestamp: 2024-06-01T00:00:00.000Z, userId: 'user456', plan: 'Pro', __t: 'SignedUp', __v: 0 } ]
⚠️

Common Pitfalls

  • Not defining a base model: You must create a base model before using discriminator.
  • Schema conflicts: Avoid overlapping field names in base and discriminator schemas.
  • Missing __t field: Mongoose adds a __t field to identify the discriminator type; do not remove or override it.
  • Using discriminators with different collections: Discriminators share the same MongoDB collection as the base model.
javascript
/* Wrong: Using discriminator without base model */
// const Child = mongoose.model('Child', new mongoose.Schema({ field: String }));
// const Wrong = Child.discriminator('Wrong', new mongoose.Schema({ extra: Number }));

/* Right: Define base model first */
const Base = mongoose.model('Base', new mongoose.Schema({ field: String }));
const Correct = Base.discriminator('Correct', new mongoose.Schema({ extra: Number }));
📊

Quick Reference

Discriminator Cheat Sheet:

StepAction
1Create base schema and model
2Define child schemas with extra fields
3Use BaseModel.discriminator(name, schema) to create child models
4Save and query using base or child models

Key Takeaways

Discriminator lets you create related models sharing a base schema with extra fields.
Always define a base model before creating discriminators.
Discriminators share the same MongoDB collection as the base model.
Mongoose adds a __t field to track the discriminator type automatically.
Avoid field name conflicts between base and discriminator schemas.