Cá nhân tôi không phải là một fan hâm mộ lớn của việc chuyển đổi "dữ liệu" như tên của các phím trong kết quả. Các nguyên tắc khung tổng hợp có xu hướng thống nhất vì loại hoạt động này cũng không được hỗ trợ.
Vì vậy, sở thích cá nhân là duy trì "dữ liệu" là "dữ liệu" và chấp nhận rằng đầu ra được xử lý thực sự tốt hơn và hợp lý hơn đối với một thiết kế đối tượng nhất quán:
db.people.aggregate([
{ "$group": {
"_id": "$sex",
"hobbies": { "$push": "$hobbies" },
"total": { "$sum": 1 }
}},
{ "$unwind": "$hobbies" },
{ "$unwind": "$hobbies" },
{ "$group": {
"_id": {
"sex": "$_id",
"hobby": "$hobbies"
},
"total": { "$first": "$total" },
"hobbyCount": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.sex",
"total": { "$first": "$total" },
"hobbies": {
"$push": { "name": "$_id.hobby", "count": "$hobbyCount" }
}
}}
])
Cái nào tạo ra kết quả như thế này:
[
{
"_id" : "female",
"total" : 1,
"hobbies" : [
{
"name" : "tennis",
"count" : 1
},
{
"name" : "football",
"count" : 1
}
]
},
{
"_id" : "male",
"total" : 2,
"hobbies" : [
{
"name" : "swimming",
"count" : 1
},
{
"name" : "tennis",
"count" : 2
},
{
"name" : "football",
"count" : 2
}
]
}
]
Vì vậy, $group
ban đầu số lượng cho mỗi "giới tính" và xếp chồng các sở thích thành một mảng mảng. Sau đó, để khử chuẩn hóa bạn $unwind
hai lần để nhận các vật phẩm số ít, $group
để lấy tổng số theo sở thích theo từng giới tính và cuối cùng tập hợp lại một mảng cho từng giới tính.
Đó là cùng một dữ liệu, nó có cấu trúc nhất quán và hữu cơ, dễ xử lý và MongoDB và khung tổng hợp đã khá hài lòng khi tạo ra kết quả này.
Nếu bạn thực sự phải chuyển đổi dữ liệu của mình thành tên của các khóa (và tôi vẫn khuyên bạn không nên làm như vậy vì nó không phải là một mô hình tốt để làm theo trong thiết kế), thì việc chuyển đổi như vậy từ trạng thái cuối cùng là khá tầm thường đối với quá trình xử lý mã máy khách. Là một ví dụ JavaScript cơ bản phù hợp với shell:
var out = db.people.aggregate([
{ "$group": {
"_id": "$sex",
"hobbies": { "$push": "$hobbies" },
"total": { "$sum": 1 }
}},
{ "$unwind": "$hobbies" },
{ "$unwind": "$hobbies" },
{ "$group": {
"_id": {
"sex": "$_id",
"hobby": "$hobbies"
},
"total": { "$first": "$total" },
"hobbyCount": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.sex",
"total": { "$first": "$total" },
"hobbies": {
"$push": { "name": "$_id.hobby", "count": "$hobbyCount" }
}
}}
]).toArray();
out.forEach(function(doc) {
var obj = {};
doc.hobbies.sort(function(a,b) { return a.count < b.count });
doc.hobbies.forEach(function(hobby) {
obj[hobby.name] = hobby.count;
});
doc.hobbies = obj;
printjson(doc);
});
Và sau đó về cơ bản bạn đang xử lý từng kết quả con trỏ thành dạng đầu ra mong muốn, đây thực sự không phải là một hàm tổng hợp thực sự cần thiết trên máy chủ:
{
"_id" : "female",
"total" : 1,
"hobbies" : {
"tennis" : 1,
"football" : 1
}
}
{
"_id" : "male",
"total" : 2,
"hobbies" : {
"tennis" : 2,
"football" : 2,
"swimming" : 1
}
}
Trường hợp đó cũng nên tương đối ba giá trị để thực hiện loại thao tác đó vào xử lý luồng kết quả con trỏ để chuyển dạng theo yêu cầu, vì về cơ bản nó chỉ là cùng một logic.
Mặt khác, bạn luôn có thể thực hiện tất cả các thao tác trên máy chủ bằng cách sử dụng mapReduce để thay thế:
db.people.mapReduce(
function() {
emit(
this.sex,
{
"total": 1,
"hobbies": this.hobbies.map(function(key) {
return { "name": key, "count": 1 };
})
}
);
},
function(key,values) {
var obj = {},
reduced = {
"total": 0,
"hobbies": []
};
values.forEach(function(value) {
reduced.total += value.total;
value.hobbies.forEach(function(hobby) {
if ( !obj.hasOwnProperty(hobby.name) )
obj[hobby.name] = 0;
obj[hobby.name] += hobby.count;
});
});
reduced.hobbies = Object.keys(obj).map(function(key) {
return { "name": key, "count": obj[key] };
}).sort(function(a,b) {
return a.count < b.count;
});
return reduced;
},
{
"out": { "inline": 1 },
"finalize": function(key,value) {
var obj = {};
value.hobbies.forEach(function(hobby) {
obj[hobby.name] = hobby.count;
});
value.hobbies = obj;
return value;
}
}
)
Trong trường hợp mapReduce có kiểu đầu ra riêng biệt, nhưng các nguyên tắc tương tự được sử dụng trong tích lũy và thao tác, nếu không có khả năng hiệu quả như khung tổng hợp có thể làm:
"results" : [
{
"_id" : "female",
"value" : {
"total" : 1,
"hobbies" : {
"football" : 1,
"tennis" : 1
}
}
},
{
"_id" : "male",
"value" : {
"total" : 2,
"hobbies" : {
"football" : 2,
"tennis" : 2,
"swimming" : 1
}
}
}
]
Vào cuối ngày, tôi vẫn nói rằng hình thức xử lý đầu tiên là hiệu quả nhất và cung cấp cho tâm trí tôi cách hoạt động tự nhiên và nhất quán nhất của đầu ra dữ liệu, thậm chí không cố gắng chuyển đổi các điểm dữ liệu thành tên của các khóa. Có lẽ tốt nhất bạn nên xem xét việc làm theo mẫu đó, nhưng nếu bạn thực sự phải làm, thì có nhiều cách để điều chỉnh kết quả thành dạng mong muốn bằng nhiều cách tiếp cận để xử lý.