Lọc sub-document trong mảng với $project + $filter
Sử dụng $filter trong stage $project của aggregation để chỉ giữ các phần tử mảng thỏa mãn điều kiện, thay vì trả về toàn bộ mảng.
1. Dữ liệu: mảng details chứa sản phẩm
> db.demo457.insertOne({
_id: 101,
details: [
{ ProductName: "Product-1", ProductPrice: 90 },
{ ProductName: "Product-2", ProductPrice: 190 }
]
});
> db.demo457.insertOne({
_id: 102,
details: [
{ ProductName: "Product-3", ProductPrice: 150 },
{ ProductName: "Product-4", ProductPrice: 360 }
]
});
2. Pipeline: Chỉ giữ sản phẩm giá >= 170
> db.demo457.aggregate([
{
$project: {
details: {
$filter: {
input: "$details",
as: "output",
cond: { $gte: ["$$output.ProductPrice", 170] }
}
}
}
}
]);
3. Kết quả
{ "_id": 101, "details": [{ "ProductName": "Product-2", "ProductPrice": 190 }] }
{ "_id": 102, "details": [{ "ProductName": "Product-4", "ProductPrice": 360 }] }
Cú pháp $filter
{
$filter: {
input: , // Mảng nguồn
as: , // Biến đại diện cho phần tử (vd: "output")
cond: // Điều kiện boolean, dùng $$output.trường
}
}
So sánh: $filter vs $unwind + $match
| Cách | Ưu điểm | Nhược điểm |
|---|---|---|
| $filter | Giữ cấu trúc doc, 1 stage, nhanh | Không tạo doc mới cho mỗi phần tử |
| $unwind + $match | Có thể dùng $group sau, linh hoạt | Tạo nhiều doc trung gian, chậm hơn |
Mở rộng: Lọc và biến đổi (map + filter)
{
$project: {
expensiveProducts: {
$map: {
input: {
$filter: {
input: "$details",
as: "p",
cond: { $gte: ["$$p.ProductPrice", 170] }
}
},
as: "item",
in: { name: "$$item.ProductName", price: "$$item.ProductPrice", discount: { $multiply: ["$$item.ProductPrice", 0.9] } }
}
}
}
}
Kết quả: Mảng chỉ chứa sản phẩm đắt, kèm giá giảm 10%.