Categories
MongoDB

Using MongoDB with Mongoose - Nested Document Discriminators and Plugins

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 Single Nested Documents

We can define discriminators on single nested documents.

For instance, we can write:

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

schema.path('shape').discriminator('Circle', Schema({ radius: String }));
  schema.path('shape').discriminator('Square', Schema({ side: Number }));

  const Model = db.model('Model', schema);
  const doc = new Model({ shape: { kind: 'Circle', radius: 5 } });
  console.log(doc)
}
run();

We call discriminator on the shape property with:

schema.path('shape').discriminator('Circle', Schema({ radius: String }));
schema.path('shape').discriminator('Square', Schema({ side: Number }));

We call the discriminator method to add the Circle and Square discriminators.

Then we use them by setting the kind property when we create the entry.

Plugins

We can add plugins to schemas.

Plugins are useful for adding reusable logic into multiple schemas.

For example, we can write:

const loadedAtPlugin = (schema, options) => {
  schema.virtual('loadedAt').
    get(function () { return this._loadedAt; }).
    set(function (v) { this._loadedAt = v; });

    schema.post(['find', 'findOne'], function (docs) {
    if (!Array.isArray(docs)) {
      docs = [docs];
    }
    const now = new Date();
    for (const doc of docs) {
      doc.loadedAt = now;
    }
  });
};

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const gameSchema = new Schema({ name: String });
  gameSchema.plugin(loadedAtPlugin);
  const Game = db.model('Game', gameSchema);

  const playerSchema = new Schema({ name: String });
  playerSchema.plugin(loadedAtPlugin);
  const Player = db.model('Player', playerSchema);

  const player = new Player({ name: 'foo' });
  const game = new Game({ name: 'bar' });
  await player.save()
  await game.save()
  const p = await Player.findOne({});
  const g = await Game.findOne({});
  console.log(p.loadedAt);
  console.log(g.loadedAt);
}
run();

We created th loadedAtPlugin to add the virtual loadedAt property to the retrieved objects after we call find or findOne .

We call schema.post in the plugin to listen to the find and findOne events.

Then we loop through all the documents and set the loadedAt property and set that to the current date and time.

In the run function, we add the plugin by calling the plugin method on each schema.

This has to be called before we define the model .

Otherwise, the plugin won’t be added.

Now we should see the timestamp when we access the loadedAt property after calling findOne .

Conclusion

We can add the discriminators to singly nested documents.

Also, we can add plugins to add reusable logic into our models.

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 *