Categories
MongoDB

Using MongoDB with Mongoose — Discriminators and Hooks

Spread the love

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Discriminators and Queries

Queries are smart enough to take into account discriminators.

For example, if we have:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const options = { discriminatorKey: 'kind' };
  const eventSchema = new Schema({ time: Date }, options);
  const Event = db.model('Event', eventSchema);

  const ClickedLinkEvent = Event.discriminator('ClickedLink',
    new Schema({ url: String }, options));

  const SignedUpEvent = Event.discriminator('SignedUp',
    new Schema({ user: String }, options));

  const event1 = new Event({ time: Date.now() });
  const event2 = new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
  const event3 = new SignedUpEvent({ time: Date.now(), user: 'mongodbuser' });
  await Promise.all([event1.save(), event2.save(), event3.save()]);
  const clickedLinkEvent =  await ClickedLinkEvent.find({});
  console.log(clickedLinkEvent);
}
run();

We called the ClickedLinkEvent.find method.

Therefore, we’ll get all the ClickedLinkEvent instances.

Discriminators Pre and Post Hooks

We can add pre and post hooks to schemas created with discriminators.

For example, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const options = { discriminatorKey: 'kind' };
  const eventSchema = new Schema({ time: Date }, options);
  const Event = db.model('Event', eventSchema);

  const clickedLinkSchema = new Schema({ url: String }, options)
  clickedLinkSchema.pre('validate', (next) => {
    console.log('validate click link');
    next();
  });
  const ClickedLinkEvent = Event.discriminator('ClickedLink',
    clickedLinkSchema);

  const event1 = new Event({ time: Date.now() });
  const event2 = new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
  await event2.validate();
}
run();

to add a pre hook for the validate operation to the clickedLinkSchema .

Handling Custom _id Fields

If an _id field is set on the base schema, then it’ll always override the discriminator’s _id field.

For example, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const options = { discriminatorKey: 'kind' };

  const eventSchema = new Schema({ _id: String, time: Date },
    options);
  const Event = db.model('BaseEvent', eventSchema);

  const clickedLinkSchema = new Schema({
    url: String,
    time: String
  }, options);

  const ClickedLinkEvent = Event.discriminator('ChildEventBad',
    clickedLinkSchema);

  const event1 = new ClickedLinkEvent({ _id: 'custom id', time: '12am' });
  console.log(typeof event1._id);
  console.log(typeof event1.time);
}
run();

And from the console log, we can see that both the _id and time fields of event1 are strings.

So the _id field is the same one as the eventSchema , but the ClickedLinkEvent field has the same type as the one in clickedLinkSchema .

Using Discriminators with Model.create()

We can use discriminators with the Model.create method.

For example, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const shapeSchema = new Schema({
    name: String
  }, { discriminatorKey: 'kind' });

  const Shape = db.model('Shape', shapeSchema);

  const Circle = Shape.discriminator('Circle',
    new Schema({ radius: Number }));
  const Square = Shape.discriminator('Square',
    new Schema({ side: Number }));

  const shapes = [
    { name: 'Test' },
    { kind: 'Circle', radius: 5 },
    { kind: 'Square', side: 10 }
  ];
  const [shape1, shape2, shape3] = await Shape.create(shapes);
  console.log(shape1 instanceof Shape);
  console.log(shape2 instanceof Circle);
  console.log(shape3 instanceof Square);
}
run();

We created 3 schemas for shapes with the discriminator method.

Then we called Shape.create with an array of different shape objects.

We specified the type with the kind property since we set that as the discriminator key.

Then in the console log, they should all log true since we specified the type of each entry.

If it’s not specified, then it has the base type.

Conclusion

We can add hooks to schemas created from discriminators.

_id fields are handled differently from other discriminator fields.

And we can use the create method with discriminators.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *