![]() |
VOOZH | about |
The populate() method in Mongoose is used to replace references stored as ObjectId in your MongoDB documents with the actual documents they reference from another collection. This method is invaluable when you want to retrieve related data from different collections in a single query.
Before you can use populate, you need to define relationships between your collections using the ref property in Mongoose schemas. The ref property allows one schema to refer to another schema.
We will first connect mongoose with our application:
const mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/gfg"); 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);In this example:
To populate the author field in a Post, you can use the populate() method. The author field in the Post document will be replaced with the actual User document.
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);
});Output:
In this example:
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);
});Output:
match for Query ConditionsThe 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);
});Output:
In this example:
options for PaginationThe 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);
});Output:
elect to Include/Exclude Fields The select option allows you to specify which fields should be included or excluded from the populated documents. You can exclude the _id field from posts if not needed.
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.
We can also populate multiple levels of references by chaining multiple calls to populate(). This is useful when you have nested references, like posts having comments and comments having authors.
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' }
});In this example:
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.
A virtual populate is used when you donβt store the reference directly in the schema but still want to populate it using virtual fields. Virtual fields are not stored in the database but can be populated as if they are.
Example: Virtual Populate
const commentSchema = new mongoose.Schema({
text: String,
postId: { type: mongoose.Schema.Types.ObjectId, ref: 'Post' }
});
commentSchema.virtual('post', {
ref: 'Post',
localField: 'postId',
foreignField: '_id',
justOne: true
});
const Comment = mongoose.model('Comment', commentSchema);
// Populate virtual field "post"
Comment.findById('someCommentId')
.populate('post')
.exec((err, comment) => {
if (!err) console.log(comment);
process.exit(0);
});