2

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 }

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! I also have some cases where votes in the document is null, would there be a way to filter them through the query? Otherwise I got an error: "MongoServerError: PlanExecutor error during aggregation :: caused by :: The argument to $size must be an array, but was of type: null"
I updated my answer. Can you check it again?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.