Search code examples
mongodbaggregation-frameworkprojection

Mongo Aggregation, replacing array inside results in array with count of that inner array


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.


Solution

  • 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"
              }
            }
          }
        }
      }
    })