Както беше посочено, обработката на документи като тази не е възможна с рамката за агрегиране, освен ако всъщност не ще предоставите всички ключове, като например:
db.events.aggregate([
{ "$group": {
"_id": "$app_id",
"event_count": { "$sum": "$event_count" },
"0": { "$sum": "$event_count_per_type.0" },
"10": { "$sum": "$event_count_per_type.10" }
"20": { "$sum": "$event_count_per_type.20" }
"30": { "$sum": "$event_count_per_type.30" }
}}
])
Но вие, разбира се, трябва изрично да посочите всеки ключ, върху който искате да работите. Това важи както за рамката за агрегиране, така и за общите операции на заявки в MongoDB, тъй като за достъп до елементи, отбелязани в този формуляр за „поддокумент“, трябва да посочите „точния път“ до елемента, за да правите каквото и да било с него.
Рамката за агрегиране и общите заявки нямат концепция за "обхождане", което означава, че не могат да обработват "всеки ключ" на документ. Това изисква езикова конструкция, за да се направи, което не е предоставено в тези интерфейси.
Най-общо казано обаче, използването на „име на ключ“ като точка от данни, където името му всъщност представлява „стойност“, е малко „анти-модел“. По-добър начин да моделирате това би било да използвате масив и да представите вашия "тип" като стойност самостоятелно:
{
"app_id": "DHJFK67JDSJjdasj909",
"date: ISODate("2014-08-07T00:00:00.000Z"),
"event_count": 32423,
"events": [
{ "type": 0, "value": 322 },
{ "type": 10, "value": 4234 },
{ "type": 20, "value": 653 },
{ "type": 30, "value": 7562 }
]
}
Също така отбелязваме, че "дата" вече е правилен обект за дата, а не низ, което също е нещо, което е добра практика. Този вид данни обаче е лесен за обработка с рамката за агрегиране:
db.events.aggregate([
{ "$unwind": "$events" },
{ "$group": {
"_id": {
"app_id": "$app_id",
"type": "$events.type"
},
"event_count": { "$sum": "$event_count" },
"value": { "$sum": "$value" }
}},
{ "$group": {
"_id": "$_id.app_id",
"event_count": { "$sum": "$event_count" },
"events": { "$push": { "type": "$_id.type", "value": "$value" } }
}}
])
Това показва двуетапно групиране, което първо получава общите суми за "тип", без да посочва всеки "ключ", тъй като вече не е необходимо, след това връща като един документ за "app_id" с резултатите в масив, както първоначално са били съхранени. Този формуляр за данни обикновено е много по-гъвкав за разглеждане на определени „типове“ или дори „стойности“ в рамките на определен диапазон.
Когато не можете да промените структурата, единствената ви опция е mapReduce. Това ви позволява да "кодирате" преминаването на ключовете, но тъй като това изисква интерпретация и изпълнение на JavaScript, не е толкова бързо, колкото рамката за агрегиране:
db.events.mapReduce(
function() {
emit(
this.app_id,
{
"event_count": this.event_count,
"event_count_per_type": this.event_count_per_type
}
);
},
function(key,values) {
var reduced = { "event_count": 0, "event_count_per_type": {} };
values.forEach(function(value) {
for ( var k in value.event_count_per_type ) {
if ( !redcuced.event_count_per_type.hasOwnProperty(k) )
reduced.event_count_per_type[k] = 0;
reduced.event_count_per_type += value.event_count_per_type;
}
reduced.event_count += value.event_count;
})
},
{
"out": { "inline": 1 }
}
)
Това по същество ще премине и комбинира "ключовете" и ще сумира стойностите за всеки намерен.
Така че вашите опции са:
- Променете структурата и работете със стандартни заявки и агрегиране.
- Останете със структурата и изисквайте обработка на JavaScript и mapReduce.
Зависи от действителните ви нужди, но в повечето случаи преструктурирането носи ползи.