Categories
MongoDB

Using MongoDB with Mongoose — Joins with Various Options

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.

Removing Foreign Documents

If we remove foreign documents, then when we try to reference a linked foreign document, it’ll return null .

For instance, if we have:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const personSchema = Schema({
    _id: Schema.Types.ObjectId,
    name: String,
    age: Number,
    stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
  });
  const storySchema = Schema({
    author: { type: Schema.Types.ObjectId, ref: 'Person' },
    title: String,
    fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
  });
  const Story = connection.model('Story', storySchema);
  const Person = connection.model('Person', personSchema);
  const author = new Person({
    _id: new Types.ObjectId(),
    name: 'James Smith',
    age: 50
  });
  await author.save();
  const story1 = new Story({
    title: 'Mongoose Story',
    author: author._id
  });
  await story1.save();
  await Person.deleteMany({ name: 'James Smith' });
  const story = await Story.findOne({ title: 'Mongoose Story' }).populate('author');
  console.log(story)
}
run();

We created a Story document that is linked to an Author .

Then we deleted the Person with the name 'James Smith' .

Now when we retrieve the latest value of the story with the author with populate , we’ll see that story is null .

Field Selection

We can select a few fields instead of selecting all the fields when we call populate .

For example, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const personSchema = Schema({
    _id: Schema.Types.ObjectId,
    name: String,
    age: Number,
    stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
  });
  const storySchema = Schema({
    author: { type: Schema.Types.ObjectId, ref: 'Person' },
    title: String,
    fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
  });
  const Story = connection.model('Story', storySchema);
  const Person = connection.model('Person', personSchema);
  const author = new Person({
    _id: new Types.ObjectId(),
    name: 'James Smith',
    age: 50
  });
  await author.save();
  const story1 = new Story({
    title: 'Mongoose Story',
    author: author._id
  });
  await story1.save();
  const story = await Story.findOne({ title: 'Mongoose Story' })
    .populate('author', 'name')
    .exec();
  console.log(story)
}
run();

We save the author and story1 documents into our database.

Then we call findOne to get the story document with the author and the name properties.

Populating Multiple Paths

If we call populate multiple times, then only the last one will take effect.

Query Conditions and Other Options

The populate method can accept query conditions.

For example, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const personSchema = Schema({
    _id: Schema.Types.ObjectId,
    name: String,
    age: Number,
    stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
  });
  const storySchema = Schema({
    author: { type: Schema.Types.ObjectId, ref: 'Person' },
    title: String,
    fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
  });
  const Story = connection.model('Story', storySchema);
  const Person = connection.model('Person', personSchema);
  const author = new Person({
    _id: new Types.ObjectId(),
    name: 'James Smith',
    age: 50
  });
  await author.save();
  const fan = new Person({
    _id: new Types.ObjectId(),
    name: 'Fan Smith',
    age: 50
  });
  await fan.save();
  const story1 = new Story({
    title: 'Mongoose Story',
    author: author._id,
    fans: [fan._id]
  });
  await story1.save();
  const story = await Story.findOne({ title: 'Mongoose Story' })
    .populate({
      path: 'fans',
      match: { age: { $gte: 21 } },
      select: 'name -_id'
    })
    .exec();
  console.log(story.fans)
}
run();

We add a Person entry to the fans array.

Then when we call populate with an object that finds all the fans with age greater than or equal to 21, we should get our fan entry.

The select property has a string that lets us select the name field but not the _id as indicated by the - sign before the _id .

So the console log should show [{“name”:”Fan Smith”}] .

Conclusion

We can join documents with various options with Mongoose.

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 *