2

Let's assume i have following entries in my mongo collection:

[{a: 1}, {a: 2}, ... {a: 10}]

Now i want to write an aggregation that returns only a few items but also returns the total count.

If you write two aggregations they would look like:

[ // meta info about total number of docs that matched
 { $match: {} },
 { $count: 'totalCount' }
]

[ // get first 5
 { $match: {} },
 { $limit: 5 }
] // totalCount should remain 10

Is it possible to combine this into one aggregation to safe some computation time?

Johannes Merz
  • 3,252
  • 17
  • 33
  • Not sure how performant this will be, but you can use `db.col.aggregate([ {"$group":{ "_id":null, "count":{"$sum":1}, "data":{"$push":"$$ROOT"} }}, {"$unwind":"$data"}, { $limit: 5 } ])`.`$group` to calculate the total count while `$push`ing all the documents into array field followed by $unwind to go back to original structure. Sometimes its just better to do multiple queries. – s7vr Jan 25 '18 at 16:55
  • 1
    https://stackoverflow.com/a/48307554/1110423 – Alex Blex Jan 25 '18 at 17:10

1 Answers1

3

You can use $facet for this, it traverse the document once

db.ad.aggregate(
  [
    {
      $facet : {
        metaInfo : [
          { $match : { "a" : 1 } },
          { $group : { _id : null, count : {$sum : 1} } }
        ],
        actualData : [
          { $match : { "a" : 1 } },
          { $limit : 2 }
        ]
      }
    }    
  ]
)

collection (has 5 a's )

> db.ad.find()
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c751"), "a" : 1 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c752"), "a" : 2 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c753"), "a" : 1 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c754"), "a" : 4 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c755"), "a" : 1 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c756"), "a" : 6 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c757"), "a" : 1 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c758"), "a" : 8 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c759"), "a" : 1 }
{ "_id" : ObjectId("5a6a0f4076b4f3c119a8c75a"), "a" : 10 }
> 

pipeline returned meta and actual data, meta has all the matching count and actual data has $limit applied

{
    "metaInfo" : [
        {
            "_id" : null,
            "count" : 5
        }
    ],
    "actualData" : [
        {
            "_id" : ObjectId("5a6a0f4076b4f3c119a8c751"),
            "a" : 1
        },
        {
            "_id" : ObjectId("5a6a0f4076b4f3c119a8c753"),
            "a" : 1
        }
    ]
}
> 
Saravana
  • 12,647
  • 2
  • 39
  • 57
  • For anything a little more complicated this is doubling the work load. Surely there should be some meta data you can get out of the original aggregation. – nrmad Jun 29 '21 at 15:34