$match filters the document stream using the same query syntax as find(). Documents that do not match the condition are removed from the pipeline. It is the primary performance lever — place it as early as possible.
Basic Syntax
{ $match: { <query> } }
// Equality filter
{ $match: { status: "active" } }
// With operators
{ $match: { score: { $gte: 80 }, status: "published" } }
// Compound — AND (implicit)
{ $match: { category: "tech", views: { $gt: 1000 } } }
// Compound — OR
{ $match: { $or: [ { status: "active" }, { featured: true } ] } }
Using $expr in $match
$expr allows aggregation expression operators inside a $match condition — enabling comparisons between two fields in the same document.
// Find orders where discount > 20% of total { $match: { $expr: { $gt: ["$discount", { $multiply: ["$total", 0.2] }] } } } // Find documents where endDate field is after startDate { $match: { $expr: { $gt: ["$endDate", "$startDate"] } } } // $expr cannot use indexes on the compared fields in general, // but CAN use an index if the expression is { $eq: ["$field", value] }
$text Search in $match
// Full-text search — $text must be the FIRST stage and requires a text index
db.articles.aggregate([
{ $match: { $text: { $search: "mongodb aggregation" } } },
{ $sort: { score: { $meta: "textScore" } } },
{ $limit: 10 }
])
$match placed at the very beginning of a pipeline can use a collection index, drastically reducing the number of documents processed by subsequent stages. A $match placed after $group or $unwind runs on the transformed stream with no index — potentially filtering millions of intermediate documents.$match vs find() Filter
| Aspect | find() filter | $match in pipeline |
|---|---|---|
| Syntax | Identical query syntax | Identical query syntax |
| Index usage | Always (if available) | Only at first stage |
| $expr support | Yes | Yes |
| Field availability | Original document fields | Fields at that pipeline position |
| Can appear multiple times | N/A | Yes — filter at any stage |