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
Discriminators are a schema inheritance mechanism.
They let us enable multiple models to have overlapping schemas on top of the same MongoDB collection.
For example, we can use them as follows:
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 genericEvent = new Event({ time: Date.now(), url: 'mongodb.com' });
console.log(genericEvent)
const clickedEvent =
new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
console.log(clickedEvent)
}
run();
We created an Event
model from the eventSchema
.
It has the discriminatorKey
so that we get can discriminate between the 2 documents we create later.
To create the ClickedLinkEvent
model, we call Event.discriminator
to create a model by inheriting from the Event
schema.
We add the url
field to the ClickedLinkEvent
model.
Then when we add the url
to the Event
document and the ClickedLinkEvent
document, only the clickedEvent
object has the url
property.
We get:
{ _id: 5f6f78f17f83ca22408eb627, time: 2020-09-26T17:22:57.690Z }
as the value of genericEvent
and:
{
_id: 5f6f78f17f83ca22408eb628,
kind: 'ClickedLink',
time: 2020-09-26T17:22:57.697Z,
url: 'mongodb.com'
}
as the value of clickedEvent
.
Discriminators Save to the Model’s Collection
We can save different kinds of events all at once.
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 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 count = await Event.countDocuments();
console.log(count);
}
run();
We created the eventSchema
as an ordinary schema.
And the rest of the models are created from the Event.discriminator
method.
Then we created the models and saved them all.
And finally, we called Event.countDocuments
to get the number of items saved under the Event
model.
Then count
should be 3 since ClickedLinkEvent
and SignedUpEvent
both inherit from Event
itself.
Discriminator Keys
We can tell the difference between each type of models with the __t
property by default.
For instance, we can write:
async function run() {
const { createConnection, Types, Schema } = require('mongoose');
const db = createConnection('mongodb://localhost:27017/test');
const eventSchema = new Schema({ time: Date });
const Event = db.model('Event', eventSchema);
const ClickedLinkEvent = Event.discriminator('ClickedLink',
new Schema({ url: String }));
const SignedUpEvent = Event.discriminator('SignedUp',
new Schema({ user: String }));
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()]);
console.log(event1.__t);
console.log(event2.__t);
console.log(event3.__t);
}
run();
to get the type of data that’s saved from the console logs. We should get:
undefined
ClickedLink
SignedUp
logged.
We can add the discriminatorKey
in the options to change the discriminator key.
So 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 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()]);
console.log(event1.kind);
console.log(event2.kind);
console.log(event3.kind);
}
run();
to set the options
for each model and then access the kind
property instead of __t
and get the same result as before.
Conclusion
We can use discriminators to create new models by inheriting one model from another.