MongoDB match Stage

The $match stage filters the documents entering the pipeline using the same query syntax as find(), and it is usually the most important stage for performance.

Filtering with $match

$match accepts a query document identical in syntax to what you would pass to find(): field-value equality checks, comparison operators like $gt and $lte, logical operators like $and and $or, and array or existence checks. Only documents that satisfy the query pass through to the next stage; everything else is discarded immediately.

A Simple Equality Match

db.orders.aggregate([
  { $match: { status: "completed" } }
])

Using Query Operators Inside $match

Because $match reuses the standard query language, you can combine comparison and logical operators just as you would in a regular query, without needing any special aggregation syntax for this stage alone.

Combining Comparison and Logical Operators

db.orders.aggregate([
  { $match: {
      status: { $in: ["shipped", "delivered"] },
      total: { $gte: 100 },
      createdAt: { $gte: ISODate("2026-01-01") }
  } }
])

This example keeps only orders that are shipped or delivered, worth at least 100, and created on or after the start of 2026. All three conditions must hold, since top-level fields in a query document are implicitly combined with AND.

$match Early vs. $match Late

A $match stage placed at the very start of a pipeline can use an existing index, exactly like a normal find() query would. A $match stage placed after a $group or $project, by contrast, filters on computed fields and cannot use an index because it is now operating on the pipeline's intermediate output rather than the raw collection.

Filtering Before and After Grouping

db.sales.aggregate([
  { $match: { region: "EMEA" } },
  { $group: {
      _id: "$product",
      totalRevenue: { $sum: "$amount" }
  } },
  { $match: { totalRevenue: { $gt: 5000 } } }
])
  • Filters using the identical operators available to find(): $eq, $gt, $lt, $in, $exists, and more
  • An early $match can use indexes, dramatically reducing work in later stages
  • A later $match filters on computed values, useful for HAVING-style logic after $group
  • Multiple $match stages are allowed and often clearer than one large compound condition
PositionCan Use Index?Typical Use
First stageYesNarrow down the raw collection before heavy processing
After $groupNoFilter on aggregated totals, similar to SQL's HAVING clause
After $projectNoFilter on renamed or computed fields
Note: Always put your most selective $match as early as possible. It is the single biggest lever for aggregation performance since every later stage processes fewer documents.
Note: A $match after $group cannot benefit from any index, because it is filtering the in-memory results of the group operation rather than the original stored documents.