Reputation: 307
In MongoDB, I have documents (comments) that looks like this:
{
"_id": { "$oid": "61acd4474bbaa88d12c38199" },
"textContent": "This comment has 2 replies",
"replies": [
{ "$oid": "61acd57fe06fed9e70462139" },
{ "$oid": "61acd57fe06fed9e7046213a" },
],
"__v": 0
},
{
"_id": { "$oid": "61acd57fe06fed9e70462139" },
"textContent": "This comment has 0 replies",
"replies": [],
"__v": 0
},
{
"_id": { "$oid": "61acd57fe06fed9e7046213a" },
"textContent": "This comment has 0 replies",
"replies": [],
"__v": 0
}
All comments are stored in the same type of object, with an array, "replies", that contains IDs of other comments. It's like a nested tree structure, but the documents are stored separately in MongoDB.
When documents are sent from the server, I would like them to remain separate documents and only contain ID references to other comments -- not the comments themselves. In other words, the objects should look like they do on the server.
How do you request all of the comments above in a single query, but keep the comments as separate documents? Please note that the level of nesting could be deeper than in the above example. Also, we don't know the ID of the top comment beforehand.
I know that populate can be helpful when you want to query nested documents. My issue with populate is that you receive a large nested object instead of the individual documents.
this.populate({ path: 'replies' })
Output:
{ _id: "61acd4474bbaa88d12c38199",
textContent: "This comment has 2 replies",
replies: [
{ _id: "61acd57fe06fed9e70462139",
textContent: "This comment has 0 replies",
replies: []
},
{ _id: "61acd57fe06fed9e7046213a",
textContent: "This comment has 0 replies",
replies: []
}
]
}
Desired output:
[
{ _id: "61acd4474bbaa88d12c38199",
textContent: "This comment has 2 replies",
replies: [ "61acd57fe06fed9e70462139", "61acd57fe06fed9e7046213a" ]
},
{ _id: "61acd57fe06fed9e70462139",
textContent: "This comment has 0 replies",
replies: [ ]
},
{ _id: "61acd57fe06fed9e7046213a",
textContent: "This comment has 0 replies",
replies: [ ]
}
]
Upvotes: 0
Views: 52
Reputation: 9284
You can use an aggregation pipeline to get the desired output:
db.collection.aggregate([
{
"$match": {
"_id": {
"$oid": "61acd4474bbaa88d12c38199"
}
}
},
{
"$lookup": {
"from": "collection",
"localField": "replies",
"foreignField": "_id",
"as": "repliesPopulated"
}
},
{
"$addFields": {
"repliesPopulated": {
"$concatArrays": [
"$$ROOT.repliesPopulated",
[
{}
]
]
}
}
},
{
"$unwind": "$repliesPopulated"
},
{
"$replaceRoot": {
"newRoot": {
"$cond": {
"if": {
"$gt": [
"$repliesPopulated",
{}
]
},
"then": "$repliesPopulated",
"else": "$$ROOT"
}
}
}
},
{
"$unset": "repliesPopulated"
}
])
In this pipeline, we first match the required document. Then we populate the replies in the repliesPopulated
key. We then add an empty object in this array and unwind it. Finally, using $replaceRoot
we bring the populated comments to the top level.
Upvotes: 1