Để gộp các mảng từ nhiều tài liệu/trường khác nhau và lấy **các giá trị duy nhất (unique)**, MongoDB Aggregation cung cấp các toán tử Set: $concatArrays (ghép mảng), $setDifference (loại bỏ phần tử trùng với mảng rỗng -> distinct), $filter (loại giá trị rỗng), $unwind + $group + $addToSet (tập hợp duy nhất).
1. Tạo dữ liệu: 2 tài liệu, mảng details và Info (cấu trúc khác nhau)
> db.demo420.insert({ "details": [{ "Value1": 10, "Value2": 20, "Value3": 30 }] });
> db.demo420.insert({ "Info": [{ "Value1": 10, "Value2": 20, "Value3": 300 }] });
2. Aggregation phức tạp (cách gốc)
> db.demo420.aggregate([
{
$project: {
_id: 0,
unique: {
$filter: {
input: {
$setDifference: [
{ $concatArrays: ["$Info.Value1", "$Info.Value2", "$Info.Value3"] },
[]
]
},
cond: { $ne: ["$$this", ""] }
}
}
}
},
{ $unwind: "$unique" },
{ $group: { _id: null, uniqueArray: { $addToSet: "$unique" } } }
]);
Vấn đề: Query gốc tham chiếu $Info.Value1 nhưng tài liệu 1 chỉ có details, không có Info -> kết quả có thể sai/null.
3. Cách chuẩn hơn: Chuẩn hóa tên trường -> gộp -> unique
> db.demo420.aggregate([
// Chuẩn hóa: Đưa tất cả giá trị Value1, Value2, Value3 vào mảng "vals"
{ $project: { vals: { $concatArrays: ["$details.Value1", "$details.Value2", "$details.Value3", "$Info.Value1", "$Info.Value2", "$Info.Value3"] } } },
{ $unwind: "$vals" },
{ $group: { _id: null, uniqueArray: { $addToSet: "$vals" } } }
]);
4. Kết quả (giả sử dữ liệu có cả 2 trường)
{ "_id": null, "uniqueArray": [300, 20, 10] }
Giải thích toán tử:
$concatArrays: Ghép nhiều mảng thành 1.$setDifference: [arr, []]: Trả về mảng distinct (loại trùng) - trick cũ.$addToSet: Trong $group, tự động distinct (chuẩn hơn $setDifference trick).$filter: Loại bỏ giá trị rỗng/null.
Best Practice: Thiết kế schema tránh tên trường khác nhau (details vs Info) cho cùng loại dữ liệu. Dùng schema nhất quán.