Open In App

Mongoose Populate

Last Updated : 06 Feb, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Mongoose is a popular object-document model (ODM) library for Node.js that provides a straightforward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, and business logic hooks, making it a great choice for many Node.js projects. One of the key features of Mongoose is its support for “populating” references between documents. This allows you to link a document in one collection with a document in another collection, and easily retrieve the related documents using a single query. This is similar to the concept of “joins” in SQL databases.

Prerequisites:

  • MongoDB should be installed
  • NodeJs should be installed

We will first connect mongoose with our application:

const mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/gfg");

Populating References In Mongoose:

Creating References:

Populate is used to “look up” documents from other collections and merge them into the current document. This is especially useful for creating relationships between documents in different collections. To use the populate feature in Mongoose, you need to first define the relationship between your documents using the “ref” property in your schema. For example:

const UserSchema = new mongoose.Schema({
    name: String,
    age: Number,
    posts: [{ type: mongoose.Types.ObjectId, ref: "Post" }],
});

const PostSchema = new mongoose.Schema({
    title: String,
    author: { type: mongoose.Types.ObjectId, ref: "User" },
});

const Post = mongoose.model("Post", PostSchema);
const User = mongoose.model("User", UserSchema);

The above code is an example of two-way referencing. In this, we’ve defined a “User” model that has an array of “Post” objects, and a “Post” model that has a single “User” object as its “author”. 

Populating References:

To populate the references, you can use the .populate() method on a query chain. For example:

Post.findOne({ title: "This is my first post" })
    .populate("author")
    .exec((err, post) => {
        // Will have post.author populated
        if (!err) console.log(post);
        process.exit(0);
    });

Here we retrieve a single post by its title, and then use .populate() to fully populate the “author” field, and then use another call to .populate() to fully populate the “author” field of each comment on the post. The output of the above code:

 

Since we have stored ObjectIds of users, we can populate posts in the authors’ document.

User.findById("63b1332c8a41f608100eeffd")
    .populate("posts")
    .exec((err, user) => {
        // Will have post.author populated
        if (!err) console.log(user);
        process.exit(0);
    });

 

Customize Populating References:

The populate() method in Mongoose allows you to specify a number of options to customize the population process. Some of the options that you can use are:

  1. path: The path to the field that contains the foreign key reference.
  2. model: The model to use for the population. This is necessary if the path refers to a model that is not in the current file.
  3. select: A space-separated list of fields to select from the populated documents.
  4. match: A query condition for the documents being populated. Only documents that match the condition will be included in the population.
  5. options: A set of options to pass to the MongoDB driver’s find() function when executing the population query. This can be used to specify options such as sort, limit, and skip.

1. The match option in the populate() method to specify a query condition for the population process. The match option takes a Mongoose query object, which allows you to specify the conditions that the documents being populated must meet.

User.findById("63b1332c8a41f608100eeffd")
    .populate({
        path: "posts", match: {
            title: /^T/i
        }, select: "title"
    })
    .exec((err, user) => {
        // Will have post.author populated
        if (!err) console.log(user);
        process.exit(0);
    });

Here retrieve a single user by name, and then use the populate() method to retrieve all of the posts written by that user. However, the match option is used to specify that only posts with a title that starts with a “T” should be included in the population. The select option is also used to specify that only the “title” field of the posts should be included in the resulting documents.

 

2. The options field in the populate() method specifies a set of options to pass to the MongoDB driver’s find() function when executing the population query. In this case, the sort option is used to sort the posts by their “title” field in ascending order, and the limit option is used to limit the number of posts to 2.

User.findById("63b1332c8a41f608100eeffd")
    .populate({
        path: "posts", options: {
            sort: { title: 1 }, limit: 2
        }
    })
    .exec((err, user) => {
        // Will have post.author populated
        if (!err) console.log(user);
        process.exit(0);
    });

 

3. The select option can be used to include or exclude any field from the populated references. 

User.findById("63b1332c8a41f608100eeffd")
    .populate({ path: "posts", select: "title -_id" }) // Include title, exclude _id
    .exec((err, user) => {
        if (!err) console.log(user);
        process.exit(0);
    });

Adding a – will exclude the _id field from the query. 

 

Populating Multiple Levels:

To populate multiple levels of related documents in Mongoose, you can chain multiple calls to the populate() method in a query chain.

For example, let’s say you have the following schema:

const userSchema = new mongoose.Schema({
    name: String,
    posts: [{ type: mongoose.Types.ObjectId, ref: 'Post' }]
});

const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    author: { type: mongoose.Types.ObjectId, ref: 'User' },
    comments: [{ type: mongoose.Types.ObjectId, ref: 'Comment' }]
});

const commentSchema = new mongoose.Schema({
    text: String,
    author: { type: mongoose.Types.ObjectId, ref: 'User' }
});

This schema defines a “User” model that has an array of “Post” objects, a “Post” model that has an “author” field that is a single “User” object, and a “Comment” model that has an “author” field that is a single “User” object.

To populate the references between these documents, you can use the populate() method multiple times in a query chain. For example:

User.findOne({ name: 'John' }).populate({
    path: 'posts',
    populate: {
        path: 'comments',
        model: 'Comment',
        populate: {
            path: 'author',
            model: 'User'
        }
    }
}).exec((err, user) => {
    console.log(user);
});

We use another call to populate() to fully populate the “author” field of each comment with the corresponding “User” document.

 


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads