mongodbmongoose-populate

How to populate nested data in MongoDB using mongoose?


I have three schemas(BlogSchema, UserSchema, commentSchema) in MongoDB. How can I populate the nested data.

BlogSchema:

const blogSchema = new mongoose.Schema({
  title: { type: String, required: true, minLength: 5 },
  author: String,
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
  },
  comments: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Comment",
    },
  ],
});

UserSchema:

const userSchema = new mongoose.Schema({
  username: { type: String, required: true, unique: true, minLength: 4 },
  name: String,
  passwordHash: String,
  blogs: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Blog",
    },
  ],
});

CommentSchema

const commentSchema = new mongoose.Schema({
  comment: { type: String, minLength: 5 },
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
  },
  blog: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "Blog",
    required: true,
  },
});

I want my data, blog in the following format:

blog = {
"title" : ".....",
"author": "......",
"user" : user-Id,
comments: [{comment:"....",
user: "username"}]

One of the solution I tried is

 const blogs = await Blog.find({})
    .populate("user", { id: 1 })
    .populate("comments", { comment: 1, user: 1 });

But, the output it provides is as follows(not on required format):

blog = {
"title" : ".....",
"author": "......",
"user" : user-Id,
comments: [{comment:"....",
user: "user-Id"}]

How can I get the required result by populate or by any other method?


Solution

  • You will need to use nested populate to be able to populate the user objects within the comments objects if using Model.find() on the Blog. Here is an example:

    const blogs = await Blog.find()
    .populate({ path: 'user', model: userModel })
    .populate({
        path: 'comments',
        model: commentModel,
        populate: {
            path: 'user', model: userModel, select: 'username -_id',
        },
    });
    

    I have included model: modelName in each populate incase as you may need to register the model. This depends on your design of course and can be removed.

    You will notice I have used select: 'username -_id' inside the nested populate object. This means select username and exclude _id. You can add properties and exclude them as you see fit. If you do not want this then you can remove it and your user object will contain everything from the userSchema.