Skip to content

Aggregation Fundamentals

$sort, $limit and $skip

Order and slice after grouping — and why $sort before $limit can use an index.

IntermediateAbout 4 minutes

Same idea as cursor sort / limit / skip, as stages. After $group, you must $sort if the ranking matters — groups come out unordered. $sort then $limit can be a top-k: the server does not have to fully sort every group when it only needs five.

db.products.aggregate([
  { $sort: { price: -1 } },
  { $limit: 5 },
  { $project: { _id: 0, name: 1, price: 1, category: 1 } }
])
Five most expensive products
db.orders.aggregate([
  { $group: { _id: "$userId", totalSpent: { $sum: "$amount" } } },
  { $sort: { totalSpent: -1 } },
  { $limit: 10 }
])
Top spenders — sort the groups, not the orders

$skip has the same cost as skip() on find — walking n results. An early $sort+$limit before a heavy $lookup (next module) means you join ten documents, not the collection. A $sort on a field with an index, before a $group that destroys that field, can still use the index; after $group you are sorting computed totals in memory.

Interview question

Where do $sort and $limit go relative to $group?

Think about it first.

Practice