Jak wspomniano, przetwarzanie takich dokumentów nie jest możliwe w ramach agregacji, chyba że faktycznie dostarczysz wszystkie klucze, takie jak:
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" }
}}
])
Ale oczywiście musisz wyraźnie określić każdy klucz, nad którym chcesz pracować. Dotyczy to zarówno struktury agregacji, jak i ogólnych operacji zapytań w MongoDB, ponieważ aby uzyskać dostęp do elementów zapisanych w tym formularzu „poddokumentu”, musisz określić „dokładną ścieżkę” do elementu, aby cokolwiek z nim zrobić.
Struktura agregacji i zapytania ogólne nie mają pojęcia „przechodzenia”, co oznacza, że nie mogą przetwarzać „każdego klucza” dokumentu. Wymaga to konstrukcji językowej, której nie ma w tych interfejsach.
Ogólnie rzecz biorąc, używanie „nazwy klucza” jako punktu danych, gdzie jej nazwa faktycznie reprezentuje „wartość”, jest trochę „antywzorcem”. Lepszym sposobem modelowania tego byłoby użycie tablicy i reprezentowanie „typu” jako samej wartości:
{
"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 }
]
}
Należy również zauważyć, że „data” jest teraz właściwym obiektem daty, a nie ciągiem, co również jest dobrą praktyką. Tego rodzaju dane można jednak łatwo przetwarzać w ramach agregacji:
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" } }
}}
])
To pokazuje dwuetapowe grupowanie, które najpierw pobiera sumy według „typu” bez określania każdego „klucza”, ponieważ nie musisz już tego robić, a następnie zwraca jako pojedynczy dokument na „app_id” z wynikami w tablicy, tak jak były pierwotnie przechowywane. Ten formularz danych jest na ogół znacznie bardziej elastyczny, jeśli chodzi o wyszukiwanie określonych „typów”, a nawet „wartości” w określonym zakresie.
Tam, gdzie nie możesz zmienić struktury, jedyną opcją jest mapReduce. Pozwala to "zakodować" przechodzenie kluczy, ale ponieważ wymaga to interpretacji i wykonania JavaScript, nie jest tak szybkie jak framework agregacji:
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 }
}
)
To zasadniczo prześledzi i połączy „klucze” i zsumuje wartości dla każdego znalezionego.
Masz więc następujące opcje:
- Zmień strukturę i pracuj ze standardowymi zapytaniami i agregacją.
- Pozostań przy strukturze i wymagaj przetwarzania JavaScript i mapReduce.
Zależy to od Twoich rzeczywistych potrzeb, ale w większości przypadków restrukturyzacja przynosi korzyści.