Đó là một khó khăn!
Đầu tiên, giải pháp đơn giản:
db.test.aggregate([
{ "$match": { "user": "Hans" } },
// duplicate each document: one for "age", the other for "childs"
{ $project: { age: "$age", childs: "$childs",
data: {$literal: ["age", "childs"]}}},
{ $unwind: "$data" },
// pivot data to something like { data: "age", value: "40" }
{ $project: { data: "$data",
value: {$cond: [{$eq: ["$data", "age"]},
"$age",
"$childs"]} }},
// Group by data type, and count
{ $group: { _id: {data: "$data", value: "$value" },
count: { $sum: 1 },
value: {$first: "$value"} }},
// aggregate values in an array for each independant (type,value) pair
{ $group: { _id: "$_id.data", values: { $push: { count: "$count", value: "$value" }} }} ,
// project value to the correctly name field
{ $project: { result: {$cond: [{$eq: ["$_id", "age"]},
{age: "$values" },
{childs: "$values"}]} }},
// group all data in the result array, and remove unneeded `_id` field
{ $group: { _id: null, result: { $push: "$result" }}},
{ $project: { _id: 0, result: 1}}
])
Sản xuất:
{
"result" : [
{
"age" : [
{
"count" : 3,
"value" : "40"
},
{
"count" : 1,
"value" : "50"
}
]
},
{
"childs" : [
{
"count" : 1,
"value" : "1"
},
{
"count" : 3,
"value" : "2"
}
]
}
]
}
Và bây giờ, để có một số giải thích:
Một trong những vấn đề chính ở đây là mỗi tài liệu đến phải là một phần của hai các khoản tiền khác nhau. Tôi đã giải quyết điều đó bằng cách thêm một mảng chữ ["age", "childs"]
vào tài liệu của bạn, rồi giải nén chúng theo mảng đó. Bằng cách đó, mỗi tài liệu sẽ được trình bày hai lần trong giai đoạn sau.
Sau khi hoàn tất, để dễ dàng xử lý, tôi thay đổi biểu diễn dữ liệu thành một thứ dễ quản lý hơn như { data: "age", value: "40" }
Các bước sau sẽ thực hiện tổng hợp dữ liệu theo từng lần. Lên đến $project
thứ ba bước sẽ ánh xạ các trường giá trị với age
tương ứng hoặc childs
trường.
Hai bước cuối cùng sẽ chỉ gói gọn hai tài liệu thành một, xóa _id
không cần thiết trường.
Pfff!