in MongoDB I want to group an array of documents that is nested in another document without it affecting the parent document.
Database:
db={
"users": [
{
"firstName": "David",
"lastName": "Mueller",
"messages": [
{
"text": "hello",
"type": "PERSONAL"
},
{
"text": "test",
"type": "DIRECT"
}
]
},
{
"firstName": "Mia",
"lastName": "Davidson",
"messages": [
{
"text": "hello world",
"type": "DIRECT"
},
{
"text": ":-)",
"type": "PERSONAL"
},
{
"text": "hi there",
"type": "DIRECT"
}
]
}
]
}
Desired result:
[
{
"firstName": "David",
"lastName": "Mueller",
"messages": [
{
"_id": "PERSONAL",
"count": 1
},
{
"_id": "DIRECT",
"count": 1
}
]
},
{
"firstName": "Mia",
"lastName": "Davidson",
"messages": [
{
"_id": "PERSONAL",
"count": 1
},
{
"_id": "DIRECT",
"count": 2
}
]
}
]
If I have an array of ids I already know how to do it using the internal pipeline of $lookup, but my question is how can I do that with an array of embedded documents.
This is an example of a working grouping on an array with ids using lookup. This is not the solution because the question is about an embedded document array and not an array of ids. This example is only provided to show that I can archive the desired result when ids instead of embedded documents are stored in an array.
Database for grouping with lookup:
db={
"users": [
{
"firstName": "David",
"lastName": "Mueller",
"messages": [
1,
2
]
},
{
"firstName": "Mia",
"lastName": "Davidson",
"messages": [
3,
4,
5
]
}
],
"messages": [
{
"_id": 1,
"text": "hello",
"type": "PERSONAL"
},
{
"_id": 2,
"text": "test",
"type": "DIRECT"
},
{
"_id": 3,
"text": "hello world",
"type": "DIRECT"
},
{
"_id": 4,
"text": ":-)",
"type": "PERSONAL"
},
{
"_id": 5,
"text": "hi there",
"type": "DIRECT"
}
]
}
Aggregation of grouping with lookup:
db.users.aggregate([
{
"$lookup": {
"from": "messages",
"localField": "messages",
"foreignField": "_id",
"as": "messages",
"pipeline": [
{
"$group": {
"_id": "$type",
"count": {
"$sum": 1
}
}
}
]
}
}
])
Result of grouping with lookup (which is the desired result):
[
{
"_id": ObjectId("5a934e000102030405000005"),
"firstName": "David",
"lastName": "Mueller",
"messages": [
{
"_id": "PERSONAL",
"count": 1
},
{
"_id": "DIRECT",
"count": 1
}
]
},
{
"_id": ObjectId("5a934e000102030405000006"),
"firstName": "Mia",
"lastName": "Davidson",
"messages": [
{
"_id": "PERSONAL",
"count": 1
},
{
"_id": "DIRECT",
"count": 2
}
]
}
]
This example in the MongoDB playground
Now back to the issue: I want to archive the same result but with an embedded document array as provided at the top.
I cannot find out how to do this (I tried AI, lot’s of google searches and other forums without success, you are my last resource before giving up), I know I can filter an embedded array using $addField and $fitler but not how I can group just the embedded array.
Please note that this is just a simple example my real data structure looks different and might also use other grouping functions like min, sum etc. but I just wanted to know a general way of archieving the same thing as when I use the lookup.
I appreciate any help with this and thank you 🙂
2
Answers
messages
_id
(presumably userID) and messagetype
; and set countType with$count
._id
only and use the firstdoc
(since it’s the same for non-message fields){type: ..., count: countType}
into a messages array._id
doc.messages
to the arraymessages
which was pushed in the previous step.doc
which has all the correct info_id
if you need it.)Mongo Playground
here is an alternative way of doing without group and unwind.
$setUnion: "$messages.type"
will give a list of the unique$messages.type
so now you are essentially looping it using$map
{_id, count}
. To calculate the count I’m filtering the initial messages array based on the type and getting the size of the filtered arrayplayground
The above query will make sense if you could understand this. This is how i came up with the logic actually 🙂