Home > Software engineering >  MongoDB: nested array count original document
MongoDB: nested array count original document

Time:10-25

I have the following document structure which contains an array of votes:

    { _id: ObjectId("6350e2c1a15e0e656f4a7472"),
      category: 'business',
      votes: 
       [ { voteType: 'like',
           userId: ObjectId("62314007da34df3f32f7cfc0") },
         { voteType: 'like',
           userId: ObjectId("6356b5cbe2272ebf628451b") } ] }

What I would like to achieve is to add for each document the sum of votes for which voteType = like, while keeping the original document, such as:

    [ [{ _id: ObjectId("6350e2c1a15e0e656f4a7472"),
          category: 'business',
          votes: 
           [ { voteType: 'like',
               userId: ObjectId("62314007da34df3f32f7cfc0") },
             { voteType: 'like',
               userId: ObjectId("6356b5cbe2272ebf628451b") } ] }, {sum: 2, voteType: "like"} ], ...]

At the moment, the only workaround that I found is through an aggregation although I cannot manage to keep the original documents in the results:

     db.getCollection('MyDocument') .aggregate([   {
            $unwind: "$votes"   },   {
            $match: {
              "votes.voteType": "like",
            }   },   {
            $group: {
              _id: {
                name: "$_id",
                type: "$votes.voteType"
              },
              count: {
                $sum: 1
              }
            }   },
          { $sort : { "count" : -1 } },    {$limit : 5}
        ])

which gives me:

    { _id: { name: ObjectId("635004f1b96e494947caaa5e"), type: 'like' },
      count: 3 }
    { _id: { name: ObjectId("63500456b96e494947cbd448"), type: 'like' },
      count: 3 }
    { _id: { name: ObjectId("63500353b6c7eb0a01df268e"), type: 'like' },
      count: 2 }
    { _id: { name: ObjectId("634e315bb7d17339f8077c39"), type: 'like' },
      count: 1 }

CodePudding user response:

You can do it like this:

  • $cond with $isArray - to check if the votes property is of the type array.
  • $filter - to filter votes based on voteType property.
  • $size - to get the sized of the filtered array.
db.collection.aggregate([
  {
    "$set": {
      "count": {
        "$cond": {
          "if": {
            "$isArray": "$votes"
          },
          "then": {
            "$size": {
              "$filter": {
                "input": "$votes",
                "cond": {
                  "$eq": [
                    "$$this.voteType",
                    "like"
                  ]
                }
              }
            }
          },
          "else": 0
        }
      }
    }
  }
])

Working example

  • Related