Pono
Pono

Reputation: 11776

Get total count of matching documents and count within that subset that satisfies certain criteria

Say I have the following 5 documents in a collection:

{"account_id": 11, "event": ISODate("2021-05-03T10:33:18.220Z")},
{"account_id": 11, "event": ISODate("2021-05-03T10:33:19.220Z")},
{"account_id": 11, "event": null},
{"account_id": 99, "event": ISODate("2021-05-03T10:33:20.220Z")},
{"account_id": 99, "event": null}

In a single aggregate query I'd like to retrieve:

  1. Total number of documents with acccount_id: 11 (3)
  2. Total number of documents with acccount_id: 11 AND event being not null (2)

Essentially, what I'm looking for as a result is this:

{ total: 3, with_event: 2 }

Below is the code I have that satisfies the second point from the above. It matches account_id and event type of 9 which is date time in Mongo. How do I extend this code to also satisfy point 1?

mongo.accounts.aggregate([
    {
        "$match": {
            "account_id": 11,
            "event": {
                "$type": 9
            }
        }
    },
    {
        "$group": {
            "_id": null,
            "count": {
                "$sum": 1
            }
        }
    }
])

Upvotes: 1

Views: 31

Answers (1)

turivishal
turivishal

Reputation: 36104

You can use $facet operator to separate both conditions and result,

  • $match condition for account_id
  • total, $group by null and count total document
  • with_event match for event and $group by null and count total document
  • $project to show required fields, get first element from facet result
mongo.accounts.aggregate([
  { $match: { account_id: 11 } },
  {
    $facet: {
      total: [
        {
          $group: {
            _id: null,
            count: { $sum: 1 }
          }
        }
      ],
      with_event: [
        { $match: { event: { $type: 9 } } },
        {
          $group: {
            _id: null,
            count: { $sum: 1 }
          }
        }
      ]
    }
  },
  {
    $project: {
      total: { $arrayElemAt: ["$total.count", 0] },
      with_event: { $arrayElemAt: ["$with_event.count", 0] }
    }
  }
])

Playground


Second approach:

  • $match condition for account_id
  • $group by null and get total document, and for event check condition if type is date then count 1 otherwise 0
mongo.accounts.aggregate([
  { $match: { account_id: 11 } },
  {
    $group: {
      _id: null,
      total: { $sum: 1 },
      with_event: {
        $sum: {
          $cond: [{ $eq: [{ $type: "$event" }, "date"] }, 1, 0]
        }
      }
    }
  }
])

Playground

Upvotes: 2

Related Questions