The $count stage returns a single summary document with how many records reached that point in the pipeline—perfect for “how many shipped orders?” after a $match filter.
01
One number out
{ total: N } shape.
02
After $match
Count filtered docs.
03
Field name arg
String, not an object.
04
vs $group+$sum
Shorthand pattern.
05
Empty input
No doc if count is 0.
06
vs countDocuments
Pipeline vs query API.
Fundamentals
Definition and Usage
$count is an aggregation stage that replaces a verbose $group + $project pair. It counts every document flowing in from previous stages and emits one result row with your chosen field name.
💡
Beginner tip
Do not confuse this stage with the $countaccumulator inside $group (which counts array items). This page covers the pipeline stage only—syntax is { $count: "fieldName" }.
Use $count at the end of dashboards queries, pagination metadata (“total matching rows”), and validation checks. Pair with $match to count subsets without pulling every document into memory.
Foundation
📝 Syntax
$count takes a single string—the name of the output count field:
mongosh
{ $count: <string> }
Syntax Rules
String argument — not an object; the string becomes the output field name.
Valid names — non-empty, must not start with $, must not contain ..
Input — counts documents from all prior stages in the pipeline.
Output — exactly one document, e.g. { orderTotal: 15 }, unless input is empty.
Empty input — zero documents in → zero documents out (no { total: 0 }).
$match shrinks the stream; $count only sees documents that passed the filter. This is the most common production pattern for “total matching filters” in APIs.
Example 4 — Count distinct groups after $group
mongosh
// How many distinct status values exist?
db.orders.aggregate([
{ $group: { _id: "$status" } },
{ $count: "statusCount" }
])
// → [ { statusCount: 3 } ] (shipped, pending, cancelled)
// Count high-value orders per region, then count regions with any
db.orders.aggregate([
{ $match: { amount: { $gte: 80 } } },
{ $group: { _id: "$region", orders: { $sum: 1 } } },
{ $count: "regionsWithHighValue" }
])
// East + West both have orders ≥ $80 → statusCount-style: 2 regions
$count → pipeline tally; [] if zero
countDocuments() → always returns 0+
$collStats count → fast metadata estimate
How It Works
Know the empty-result quirk before building UIs. Use countDocuments() when you need a guaranteed number; use $count when the count must reflect complex pipeline logic.
Compare
📋 $count vs related counting tools
Method
Effect
Best for
$count stage
Count docs at a pipeline point; one summary doc
Filtered totals after $match / $lookup
countDocuments(filter)
Accurate query count; returns 0 if none
Simple filter counts in app code
estimatedDocumentCount()
Fast collection-wide estimate from metadata
Rough size badges, not exact
$collStats count
Metadata count in stats document
Monitoring alongside storage stats
$sum: 1 in $group
Same as $count but flexible _id grouping
Counts per category in one stage
🧠 How $count Works
1
Prior stages run
$match, $project, $group, etc. produce the document stream entering $count.
Input
2
Documents tallied
MongoDB counts every document in the stream—internally like $sum: 1 over all rows.
Tally
3
Summary emitted
One output document with your field name and the numeric total.
Output
=
🔢
Single count result
Downstream stages (rare) see one document—or none if input was empty.
Important
📝 Notes
This page covers the $count stage, not the $count accumulator used inside $group on arrays.
Empty pipeline input → no output document; plan for [] in your code.
$unwind before $count multiplies rows—counts inflated array elements, not parent docs.
For counts per category, use $group with $sum: 1 instead of a final $count.
$count scans matching documents—indexes on $match fields keep it fast.
$count is the concise way to answer “how many?” at the end of a pipeline. Filter with $match, then { $count: "yourField" } for a single summary document.
Remember the empty-result behavior and pick countDocuments() when you need a guaranteed zero. Next: $currentOp for in-flight operations.
Place $match before $count to limit scanned documents
Use descriptive output field names: activeUsers, pendingOrders
Handle empty aggregation results in API responses (default to 0 if needed)
Index fields used in preceding $match filters
Use $facet to return both rows and count in one query when needed
❌ Don’t
Confuse the $count stage with the $count array accumulator
Expect { total: 0 } when no documents match—result is empty
Use $count when you need counts broken out by category—use $group
Pass an object to $count—only a string field name is valid
Count huge collections without filters when only a subset matters
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about $count
Use these points whenever you tally pipeline results.
5
Core concepts
🔢01
One doc out
{ name: N }.
Basics
📝02
String arg
Output field name.
Syntax
🔍03
After $match
Filtered totals.
Pattern
⚠️04
Zero matches
Empty array [].
Edge case
🛠05
vs countDocuments
Pipeline vs query.
Choose
❓ Frequently Asked Questions
$count tallies how many documents reach that point in the aggregation pipeline and returns a single document like { myTotal: 42 }. The string argument is the output field name. It is a shortcut for $group with $sum: 1 plus $project to drop _id.
Usually at the end after filters: db.orders.aggregate([{ $match: { status: "shipped" } }, { $count: "shippedTotal" }]). You can also count intermediate results—e.g. after $group to count how many groups were formed.
The $count stage counts pipeline documents and outputs one summary row. The $count accumulator lives inside $group and counts array elements or similar per group bucket—they solve different problems.
If zero documents reach $count, MongoDB returns no result document at all—not { total: 0 }. Handle empty cursors in application code or use $group/$facet if you need an explicit zero.
countDocuments() runs a query count outside a pipeline. $count runs inside aggregate() after prior stages ($match, $lookup, etc.) so the count reflects filtered or transformed pipeline output.
The argument must be a non-empty string, cannot start with $, and cannot contain a dot (.). Example: { $count: "orderTotal" } outputs { orderTotal: N }.
Did you know?
You can return both paginated rows and a total count in one round trip with $facet: one sub-pipeline uses $skip/$limit for the page, another ends with $count for the total. See the official $count docs.