I'm working on a social network kind of schema and trying to use the aggregation framework to alter the documents during the pipeline to summarize parts of it, specificially, to replace arrays of data with the size of the array.
For example, given these original documents, each of which holds an array of posts, each post has an array of likes:
{ "_id": 1, "posts": [
{
"content": "foobar", "likes": [ "11", "12", "13" ]
}
] }
{ "_id": 2, "posts": [
{
"content": "foobar2", "likes": [ "22", "23" ]
}
] }
{ "_id": 3, "posts": [
{
"content": "foobar3", "likes": [ "33" ]
}
] }
(that can be fed straight into mongoimport
)
In the real world there would be many elements in the posts
arrays, and more complicated elements in the likes
arrays. I need to return documents with a count of the likes, instead of the actual likes themselves, e.g.
{ _id: 1, posts: [
{ content: 'foobar', likeCount: 3 }
] },
{ _id: 2, posts: [
{ content: 'foobar2', likeCount: 2 }
] },
{ _id: 3, posts: [
{ content: 'foobar3', likeCount: 1 }
] }
The $project
aggregation stage seems suitable but I cannot get it to product required results. e.g.
db.getCollection('test').aggregate([
{
$project: {
"posts.likeCount": { $size: "$posts.likes" }
}
}
])
Gives:
{
"_id" : 2,
"posts" : [
{
"likeCount" : 1
}
]
}
{
"_id" : 3,
"posts" : [
{
"likeCount" : 1
}
]
}
{
"_id" : 1,
"posts" : [
{
"likeCount" : 1
}
]
}
The $posts.likes
usage in the $project
expression seems especially off, since $posts
is an array.
How can my output documents contain an aggregation (count) of this inner array, while still maintaining the structure of the original documents?
I tried $unwind
and $replaceRoot
stages to try to work toward a solution but ended up with less specific structures that lose the surrounding fields from the originals. I've been reading the MongoDB ref docs, and searching SO and elsewhere, but haven't dug up a solution yet.
You can try below aggregation query. $map
to iterate the posts
array and use $size
to calculate the count of likes
.
db.collection.aggregate({
"$project": {
"posts": {
"$map": {
"input": "$posts",
"as": "post",
"in": {
"content": "$$post.content",
"likeCount": {
"$size": "$$post.likes"
}
}
}
}
}
})