I need to retrieve the entire single object hierarchy from the database as a JSON. Actually the proposal about any other solution to achive this result would be highly appriciated. I decided to use MongoDB with its $lookup support.
So I have four collections:
- Users
{ "_id" : "2", "name" : "john" }
{ "_id" : "1", "name" : "Doe" }
- Posts
{"_id": "2","body": "hello", likes: []},
{"_id": "1","name": "hello 4", likes: [1,]},
- Comments
{"_id": "c2","body": "hello 3",postId: "1",likes: [1,2]},
{"_id": "c1","body": "hello 2",postId: "1",likes: [1,2]},
- Replies
{"_id": "r1","name": "hello 4",commentId: "c1",likes: [1]},
{"_id": "r3","name": "hello five",commentId: "c2",likes: [1,2]}
I basically need to retrieve all posts with all corresponding comments and comments.replies as part of my result . My aggregation:
const posts = await PostModel.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "postId",
as: "comments",
},
},
{
$unwind: "$comments",
},
{
$lookup: {
from: "replies",
localField: "comments._id",
foreignField: "commentId",
as: "comments.replies",
},
},
]).sort({
createdAt: -1,
});
The result is pretty weird. Some records are ok. But comments return an object . There are also some duplications on the post with _id=”1″.
[
{
"_id": "1",
"comments": {
"_id": "c2",
"body": "hello 3",
"likes": [
1,
2
],
"postId": "1",
"replies": [
{
"_id": "r3",
"commentId": "c2",
"likes": [
1,
2
],
"name": "hello five"
}
]
},
"likes": [
1
],
"name": "hello 4"
},
{
"_id": "1",
"comments": {
"_id": "c1",
"body": "hello 2",
"likes": [
1,
2
],
"postId": "1",
"replies": [
{
"_id": "r1",
"commentId": "c1",
"likes": [
1
],
"name": "hello 4"
}
]
},
"likes": [
1
],
"name": "hello 4"
}
]
Brief, This is my expected result.
- I want to get all posts, with all comments and replies associated with them .
- I want to append a count of likes
likesCount:{$size:["likes"]}
and since I have the user auth id(uid
) ready I want to check if the user liked the post , comment or reply based on ifisLiked: {$in:[ID(uid),"likes"]}
Advertisement
Answer
Since each post have multiple comments, after unwinding comments you need to group it together to form an array
Update I have updated the fetch approach a lil bit like the below.
db.posts.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "postId",
as: "comments",
},
},
{
$unwind: "$comments",
},
{
$lookup: {
from: "replies",
localField: "comments._id",
foreignField: "commentId",
as: "replies",
},
},
{
$unwind: "$replies",
},
{
"$addFields": {
"replies.countOflikes": {
$size: {
$ifNull: [
"$replies.likes",
[]
]
}
},
"replies.isLiked": {
$cond: {
if: {
$eq: [
{
$size: {
$filter: {
input: "$replies.likes",
as: "item",
cond: {
$eq: [
"$$item",
1//id of the user whom you wanna check if liked the reply
]
}
}
}
},
0
]
},
then: false,
else: true
}
}
}
},
{
$group: {
_id: "$comments._id",
postId: {
$first: "$_id"
},
body: {
$first: "$body"
},
"comments": {
$first: "$comments"
},
replies: {
$push: "$replies"
}
}
},
{
$addFields: {
"comments.replies": "$replies"
}
},
{
$group: {
_id: "$postId",
body: {
$first: "$body"
},
comments: {
$push: "$comments"
}
}
}
])
Summary of the change
- Unwinded both comments and it’s replies
- Added new fields for displaying
isLiked
andcountOfLikes
usingaddFields
stage - grouped twice to reform original structure of the data(first grouped by comments then posts)