skip to Main Content

I have two documents in two separate collections. Say, I have document one in post collection and the document is

_id: ObjectId('63e23a258224d2ef3fea4a95')
title: 'Post title'

and my other document in comment collection and the document is

_id: ObjectId('63e49e6ef73fe8e6fba1461b')
userId: ObjectId('xxx')
postId: ObjectId('63e23a258224d2ef3fea4a95')
comment: 'This is a comment'

In my comment collection there are lots of document that has same postId. I also have another collections called users. Now, I want to get something like

_id: ObjectId('63e23a258224d2ef3fea4a95')
title: 'Post title'
comments: [{
  _id: ObjectId('63e49e6ef73fe8e6fba1461b')
  userId: {
    name: 'John'
    age: 50
  }
  postId: ObjectId('63e23a258224d2ef3fea4a95')
  comment: 'This is a comment'
}, {
  _id: ObjectId('63e49e6ef73fe8e6fba1461c')
  userId: {
    name: 'John'
    age: 50
  }
  postId: ObjectId('63e23a258224d2ef3fea4a95')
  comment: 'This is a comment'
}]

How can I achieve this? I’m using nodejs and official mongodb package

I tried searching on Google using aggregate, group keywords. But didn’t find solution that I’m looking for.

2

Answers


  1. You can implement this functionality inside your backend routes:

    const {
      MongoClient
    } = require("mongodb");
    
    // Replace the uri string with your connection string.
    const uri =
      "mongodb+srv://<user>:<password>@<cluster-url>?retryWrites=true&w=majority";
    
    const client = new MongoClient(uri);
    
    async function run() {
      try {
        const database = client.db('YOUR_DB_NAME'); // Replace with your DB name
        const posts = database.collection('post');
        const comments = database.collection('comment');
        const users = database.collection('users');
    
        const _post = await posts.findOne({
          _id: '63e49e6ef73fe8e6fba1461b'
        });
        const _commentsOnPost = await comments.find({
          postId: "63e49e6ef73fe8e6fba1461b"
        });
        // You can later create a function for this process and take the ID as a parameter for the function.
    
        _commentsOnPost.forEach(_comment => {
          const _user = users.findOne({
            _id: _comment.userId
          });
          _comment.user = _user;
        })
    
        _post.comments = _commentsOnPost;
    
        console.log(_post);
      } finally {
        // Ensures that the client will close when you finish/error
        await client.close();
      }
    }
    
    run().catch(console.dir);
    Login or Signup to reply.
  2. This can be done using $lookup.

    db.posts.aggregate([
      {
        $match: {
          _id: "post_1"
        }
      },
      {
        $lookup: {
          from: "comments",
          localField: "_id",
          foreignField: "postId",
          as: "comments",
          pipeline: [
            {
              $lookup: {
                from: "users",
                localField: "userId",
                foreignField: "_id",
                as: "user"
              }
            },
            {
              $unwind: "$user"
            }
          ]
        }
      }
    ])
    

    You can try it out in this mongoDB playground.

    Be sure to create an index on comments.postId.

    It might be worth pointing out that, depending on your use case, this could return a lot of data due to the 1-Many relationship between posts -> comments so you might want to consider separating the post and comment DB calls so you can paginate the comments.

    If you don’t expect many comments (i.e. it’s a 1-Few relationship), you might want to consider modelling the comments as embedded documents instead of a separate collection which would make reads simpler.

    Some further information on 1-Many / 1-Few schema design can be found here and here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search