Home > Software design >  Can I get the count of subdocuments that match a filter?
Can I get the count of subdocuments that match a filter?

Time:04-03

I have the following document

    [
      {
        "_id": "624713340a3d2901f2f5a9c0",
        "username": "fotis",
        "exercises": [
          {
            "_id": "624713530a3d2901f2f5a9c3",
            "description": "Sitting",
            "duration": 60,
            "date": "2022-03-24T00:00:00.000Z"
          },
          {
            "_id": "6247136a0a3d2901f2f5a9c6",
            "description": "Coding",
            "duration": 999,
            "date": "2022-03-31T00:00:00.000Z"
          },
          {
            "_id": "624713a00a3d2901f2f5a9ca",
            "description": "Sitting",
            "duration": 999,
            "date": "2022-03-30T00:00:00.000Z"
          }
        ],
        "__v": 3
      }
    ]

And I am trying to get the count of exercises returned with the following aggregation (I know it is way easier to do it in my code, but I am trying to understand how to use mongodb queries)

    db.collection.aggregate([
      {
        "$match": {
          "_id": "624713340a3d2901f2f5a9c0"
        }
      },
      {
        "$project": {
          "username": 1,
          "exercises": {
            "$slice": [
              {
                "$filter": {
                  "input": "$exercises",
                  "as": "exercise",
                  "cond": {
                    "$eq": [
                      "$$exercise.description",
                      "Sitting"
                    ]
                  }
                }
              },
              1
            ]
          },
          "count": {
            "$size": "exercises"
          }
        }
      }
    ])

When I try to access the exercises field using "$size": "exercises", I get an error query failed: (Location17124) Failed to optimize pipeline :: caused by :: The argument to $size must be an array, but was of type: string.

But when I access the subdocument exercises using "$size": "$exercises" I get the count of all the subdocuments contained in the document.

Note: I know that in this example I use $slice and I set the limit to 1, but in my code it is a variable.

CodePudding user response:

You are actually on the right track. You don't really need the $slice. You can just use $reduce to perform the filtering. The reason that your count is not working is that the filtering and the $size are in the same stage. In such case, it will take the pre-filtered array to do the count. You can resolve this by adding a $addFields stage.

db.collection.aggregate([
  {
    "$match": {
      "_id": "624713340a3d2901f2f5a9c0"
    }
  },
  {
    "$project": {
      "username": 1,
      "exercises": {
        "$filter": {
          "input": "$exercises",
          "as": "exercise",
          "cond": {
            "$eq": [
              "$$exercise.description",
              "Sitting"
            ]
          }
        }
      }
    }
  },
  {
    "$addFields": {
      "count": {
        $size: "$exercises"
      }
    }
  }
])

Here is the Mongo playground for your reference.

  • Related