$filter, and how it differs from $match
$filter narrows an array in place. The document stays one document - which is exactly what makes it different from $match, which drops whole documents, and $unwind, which multiplies them.
Suppose:
{
userId: 101,
orders: [
{
product: "Laptop",
status: "completed"
},
{
product: "Mouse",
status: "pending"
}
]
}
You want to keep only completed orders.
{
$project: {
userId: 1,
completedOrders: {
$filter: {
input: "$orders",
as: "order",
cond: {
$eq: [
"$$order.status",
"completed"
]
}
}
}
}
}
Result:
{
userId: 101,
completedOrders: [
{
product: "Laptop",
status: "completed"
}
]
}
Mental model:
$filter
Take array
↓
Check each element
↓
Keep elements matching condition
$filter vs $match
Very important.
$match
Filters DOCUMENTS.
Document A → keep/remove
Document B → keep/remove
$filter
Filters ARRAY ELEMENTS inside a document.
items[]
↓
keep/remove individual items
Example:
Need completed orders only?
If each document is an order:
$match
If orders are inside:
user.orders[]
and you want to keep the user but filter their orders:
$filter
This distinction is extremely useful.
Try it
Each user in the sample data carries an embedded orders array:
db.users.aggregate([
{
$project: {
_id: 0,
name: 1,
completedOrders: {
$filter: {
input: "$orders",
as: "order",
cond: { $eq: ["$$order.status", "completed"] }
}
}
}
},
{ $limit: 4 }
])
Four users in, four users out - some with an empty array. That is the difference: $match would have removed those rows entirely.