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?

2
  • 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 $pushing all the documents into array field followed by $unwind to go back to original structure. Sometimes its just better to do multiple queries. Commented Jan 25, 2018 at 16:55
  • 1
    stackoverflow.com/a/48307554/1110423 Commented Jan 25, 2018 at 17:10

1 Answer 1

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
        }
    ]
}
> 
Sign up to request clarification or add additional context in comments.

1 Comment

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.