Лично я не большой поклонник преобразования «данных», как имена ключей в результате. Основополагающие принципы агрегирования имеют тенденцию к увеличению, так как эта операция не поддерживается.
Так что личные предпочтения, чтобы сохранить «данные» как «данные» и признать, что обрабатываемый выход на самом деле лучше и логичнее последовательного проектирования объекта:
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" }
}
}}
])
который производит результат, как это:
[
{
"_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
}
]
}
]
Таким образом, начальная $group
делает подсчет за «пол» и складывает на хобби в массив массивов. Затем, чтобы де-нормировать вас $unwind
дважды, чтобы получить особые предметы, $group
, чтобы получить итоговые значения за каждое хобби под каждым сексом и, наконец, перегруппируйте массив для каждого пола в одиночку.
Это те же данные, что и согласованная и органическая структура, которую легко обрабатывать, а MongoDB и структура агрегации были весьма счастливы в производстве этого выхода.
Если вы действительно должны преобразовать свои данные в имена ключей (и я по-прежнему рекомендую вам не делать это, так как это не является хорошим шаблоном для проектирования), то выполнение такой трансформации из конечного состояния довольно тривиально для клиента обработки кода. В качестве основного примера JavaScript подходящий для оболочки:
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);
});
И тогда вы в основном обработки каждого результата курсор в желаемую форму выходного, который на самом деле не является функцией агрегации, что действительно необходимо на сервере в любом случае:
В этом случае должно быть довольно сложно реализовать такое манипулирование в потоковой обработке результата курсора, чтобы преобразовать его по мере необходимости, поскольку это в основном одна и та же логика.
С другой стороны, вы всегда можете реализовать все манипуляции на сервере с помощью MapReduce вместо:
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;
}
}
)
Где MapReduce имеет свой особый стиль вывода, но одни и те же принципы используются в накоплении и манипуляции , если скорее всего, не так эффективно, как в рамках агрегации можно сделать:
"results" : [
{
"_id" : "female",
"value" : {
"total" : 1,
"hobbies" : {
"football" : 1,
"tennis" : 1
}
}
},
{
"_id" : "male",
"value" : {
"total" : 2,
"hobbies" : {
"football" : 2,
"tennis" : 2,
"swimming" : 1
}
}
}
]
в конце концов, я до сих пор говорят, что первый вид обработки является наиболее эффективным и обеспечивает на мой взгляд, наиболее естественным и последовательным работы e, даже не пытаясь преобразовать точки данных в имена ключей. Вероятно, лучше всего рассмотреть следующий шаблон, но если вам действительно нужно, тогда есть способы манипулировать результатами в желаемой форме в различных подходах к обработке.
Просто хочу отказаться от «спасибо» за ваш ответ ... хотя я не задавал вопрос :) Я очень помог мне! – chrisdennig