Comme indiqué, le traitement de documents comme celui-ci n'est pas possible avec le cadre d'agrégation à moins que vous ne fournissiez réellement toutes les clés, telles que :
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" }
}}
])
Mais vous devez bien sûr spécifier explicitement chaque touche sur laquelle vous souhaitez travailler. Cela est vrai à la fois pour le cadre d'agrégation et les opérations de requête générales dans MongoDB, car pour accéder aux éléments notés dans ce formulaire de "sous-document", vous devez spécifier le "chemin exact" de l'élément afin de pouvoir en faire quoi que ce soit.
Le cadre d'agrégation et les requêtes générales n'ont pas de concept de "traversée", ce qui signifie qu'ils ne peuvent pas traiter "chaque clé" d'un document. Cela nécessite une construction de langage pour faire ce qui n'est pas fourni dans ces interfaces.
De manière générale cependant, utiliser un "nom de clé" comme point de données où son nom représente en fait une "valeur" est un peu un "anti-modèle". Une meilleure façon de modéliser cela serait d'utiliser un tableau et de représenter votre "type" comme une valeur en soi :
{
"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 }
]
}
Notez également que la "date" est maintenant un objet de date approprié plutôt qu'une chaîne, ce qui est également une bonne pratique à faire. Ce type de données est cependant facile à traiter avec le cadre d'agrégation :
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" } }
}}
])
Cela montre un groupement en deux étapes qui obtient d'abord les totaux par "type" sans spécifier chaque "clé" puisque vous n'avez plus à le faire, puis revient en un seul document par "app_id" avec les résultats dans un tableau tels qu'ils étaient initialement stockés. Ce formulaire de données est généralement beaucoup plus flexible pour examiner certains "types" ou même les "valeurs" dans une certaine plage.
Lorsque vous ne pouvez pas modifier la structure, votre seule option est mapReduce. Cela vous permet de "coder" la traversée des clés, mais comme cela nécessite une interprétation et une exécution JavaScript, ce n'est pas aussi rapide que le framework d'agrégation :
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 }
}
)
Cela va essentiellement parcourir et combiner les "clés" et résumer les valeurs pour chacune trouvée.
Donc, vos options sont :
- Modifiez la structure et travaillez avec des requêtes et une agrégation standards.
- Rester avec la structure et exiger le traitement JavaScript et mapReduce.
Cela dépend de vos besoins réels, mais dans la plupart des cas, la restructuration apporte des avantages.