1

Lookup with three collections re-manage data. I got confuse which to use. Please guide on this

db.post.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "created_by",
      foreignField: "_id",
      as: "created_users"
    }
  },
  {
    $lookup: {
      from: "comments",
      let: {
        p_id: "$_id"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$post_id",
                "$$p_id"
              ]
            }
          }
        }
      ],
      as: "comments"
    }
  },
  {
    $lookup: {
      from: "users",
      localField: "comments.sender_id",
      foreignField: "_id",
      as: "commented_user"
    }
  }
])

There are three collection posts, user, and comments we would like to get who has comment for a post, Commented user should come under comments like this

[
  {
    "_id": ObjectId("5eeb02881982961ada625c7d"),
    "commented_user": [
      {
        "_id": ObjectId("5e4d0973babf2b74ca868f4d"),
        "first_name": "James",
        "last_name": "Smith",
        "timestamp": 1.582106995137e+12
      },
      {
        "_id": ObjectId("5e4d0973babf2b74ca868f6d"),
        "first_name": "Alex",
        "last_name": "Jimy",
        "timestamp": 1.582106995139e+12
      }
    ],
    "comments": [
      {
        "_id": ObjectId("5eeb08e26fb7f270e4077617"),
        "date": 1.592461538924e+12,
        "post_id": ObjectId("5eeb02881982961ada625c7d"),
        "sender_id": ObjectId("5e4d0973babf2b74ca868f4d"),
        "text": "Nice ",
        "commented_user": {
            "_id": ObjectId("5e4d0973babf2b74ca868f4d"),
            "first_name": "James",
            "last_name": "Smith",
            "timestamp": 1.582106995137e+12
        },
      },
      {
        "_id": ObjectId("5eeb08e26fb7f270e4077618"),
        "date": 1.592461538923e+12,
        "post_id": ObjectId("5eeb02881982961ada625c7d"),
        "sender_id": ObjectId("5e4d0973babf2b74ca868f6d"),
        "text": "Nice One",
        "commented_user": {
            "_id": ObjectId("5e4d0973babf2b74ca868f6d"),
            "first_name": "Alex",
            "last_name": "Jimy",
            "timestamp": 1.582106995137e+12
        },
      }
    ],
    "created_by": ObjectId("5e4e74eb380054797d9db623"),
    "created_users": [],
    "date": 1.589441206774e+12,
    "title": "Covid19"
  }
]

here is my attempt https://mongoplayground.net/p/UwRjj-er0K5

Please help on this thanks

2 Answers 2

1

You can do something like this:

The strategy is $unwinding the results so we can match per comment and then reconstruct the former structure.

db.post.aggregate([
 {
   $lookup: {
     from: "users",
     localField: "created_by",
     foreignField: "_id",
     as: "created_users"
   }
 },
 {
   $lookup: {
     from: "comments",
     let: {
       p_id: "$_id"
     },
     pipeline: [
       {
         $match: {
           $expr: {
             $eq: [
               "$post_id",
               "$$p_id"
             ]
           }
         }
       }
     ],
     as: "comments"
   }
 },
 {
   $unwind: "$comments"
 },
 {
   $lookup: {
     from: "users",
     localField: "comments.sender_id",
     foreignField: "_id",
     as: "commented_user"
   }
 },
 {
   $unwind: "$commented_user"
 },
 {
   $addFields: {
     comments: {
       $mergeObjects: [
         "$comments",
         {
           commented_user: "$commented_user"
         }
       ]
     }
   }
 },
 {
   $group: {
     _id: "$_id",
     comments: {
       $push: "$comments"
     },
     created_by: {
       $first: "$created_by"
     },
     created_users: {
       $first: "$created_users"
     },
     date: {
       $first: "$date"
     },
     title: {
       $first: "$title"
     },

   }
 }
])

Mongo Playground

Sign up to request clarification or add additional context in comments.

8 Comments

can we do without $group or using $project ? if please help with different playground
the $group is required to restore the comments structure to an array, but if you don't mind it being unwinded you can replace it with project yes.
Because if you don't group then $comments isn't an array: mongoplayground.net/p/8FTIbHv7fmQ
Yea, take a look here: mongoplayground.net/p/OA1s79voayX. instead of unwinding the comments i just selected the last one. (this assumes it's sorted by date in the collection)
|
1

Starting with your the code you have attempted, you can just move your second $lookup into the first $lookup's pipeline, then $unwind it

db.post.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "created_by",
      foreignField: "_id",
      as: "created_users"
    }
  },
  {
    $lookup: {
      from: "comments",
      let: { p_id: "$_id" },
      pipeline: [
        {
          $match: {
            $expr: { $eq: ["$post_id", "$$p_id"] }
          }
        },
        { // second level $lookup
          $lookup: {
            from: "users",
            localField: "sender_id",
            foreignField: "_id",
            as: "commented_user"
          }
        },
        { // $unwind to get single object instead of array
          $unwind: "$commented_user"
        }
      ],
      as: "comments"
    }
  },
])

Mongo Playground

Note for OP: There is discussion to get the latest comment instead. With this approach you can $sort and $limit in the outermost $lookup

db.post.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "created_by",
      foreignField: "_id",
      as: "created_users"
    }
  },
  {
    $lookup: {
      from: "comments",
      let: { p_id: "$_id" },
      pipeline: [
        {
          $match: {
            $expr: { $eq: ["$post_id", "$$p_id"] }
          }
        },
        {
          $sort: { date: -1 } // or { _id: -1 }
        },
        {
          $limit: 1 // limit to only one element
        },
        {
          $lookup: {
            from: "users",
            localField: "sender_id",
            foreignField: "_id",
            as: "commented_user"
          }
        },
        {
          $unwind: "$commented_user"
        }
      ],
      as: "comments"
    }
  },
  { // // $unwind to get single object instead of array
    $unwind: "$comments"
  }
])

Mongo Playground

1 Comment

Awesome @thammada Thank you so much for this explanation. Your answer is really useful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.