Agrégats#
En plus des recherches classiques d’informations, le calcul d’agrégat est très utilisé, pour l’analyse, la modélisation ou la visualisation de données. Ce calcul s’effectue avec la fonction aggregate(). Celle-ci prend en paramètre un tableau d’opérations (appelé aussi pipeline), pouvant contenir les éléments suivants :
- $project : redéfinition des documents (si nécessaire)
- $match : restriction sur les documents à utiliser
- $group : regroupements et calculs à effectuer
- $sort : tri sur les agrégats
- $unwind : découpage de tableaux
- ...
Syntaxe
> db.coll.aggregate([
{$group: {_id: <expression>, // Group By Expression
<field1>: { <accumulator1> : <expression1> },
...}
}
])
Agrégat simple#
Voici un premier exemple permettant un calcul pour toute la base. Ici, nous réalisons un dénombrement (il fait la somme de la valeur 1 pour chaque document).
db.restaurants.aggregate([
{ $group: { _id: null, nb: { $sum: 1 }}}
])
Les calculs peuvent être plus complexes, comme nous le verrons plus tard. Bien évidemment, ces calculs d’agrégats peuvent se faire aussi en ajoutant des critères de regroupement. Attention au $ avant le nom de l’item.
db.restaurants.aggregate([
{ $group: { _id: "$borough", nb: { $sum: 1 }}}
])
Tri#
Ce résultat peut être trié en ajoutant l’action $sort dans le tableau, avec le même mécanismes que précédemment (1 : ascendant, -1 : descendant).
db.restaurants.aggregate([
{ $group: { _id: "$borough", nb: { $sum: 1 }}},
{ $sort: { "nb": -1 }}
])
Redéfinition des documents (et limite)#
Il est parfois intéressant (voire nécessaire) de redéfinir les documents, pour ne garder que les items qui nous intéressent. Dans cette projection, il existe différentes fonctions de traitement comme la concaténation (cf ci-dessous).
L’action $limit permet de limiter le nombre de sorties renvoyées par le moteur.
db.restaurants.aggregate([
{ $project: { _id: 0, name: 1, info: { $concat: [ "$cuisine", " - ", "$borough" ]} }},
{ $limit: 5 }
]).pretty()
Restriction#
On peut aussi faire une restriction avant le calcul, avec l’opération $match.
db.restaurants.aggregate([
{ $match: { cuisine: "French" }},
{ $group: { _id: "$borough", nb: { $sum: 1 }}}
])
Découpage des tableaux#
Imaginons maintenant que nous souhaitons calculer le nombre de restaurant par grade. La première idée serait de réaliser l’opération suivante.
db.restaurants.aggregate([
{ $group: { _id: "$grades.grade", nb: { $sum: 1 }}}
])
Malheureusement, nous voyons que le regroupement se fait par ensemble de grades existant dans la base. Il faut donc faire un découpage des tableaux grades dans chaque document. Pour cela, il existe l’opération $unwind.
Pour montrer comment fonctionne cette opération, voici les 5 premières lignes renvoyées lorsqu’on l’applique directement sur les données. On s’apercoit que chaque document ne contient plus qu’une seule évaluation.
db.restaurants.aggregate([
{ $unwind: "$grades" },
{ $limit: 5 }
]).pretty()
Par ce biais, nous pouvons donc maintenant faire l’opération de regroupement par grade.
db.restaurants.aggregate([
{ $unwind: "$grades" },
{ $group: { _id: "$grades.grade", nb: { $sum: 1 }}}
])
Calcul statistique#
Comme indiqué précédemment, on peut faire tous les calculs d’agrégats classiques, comme ici avec la somme ($sum), la moyenne ($avg), le minimum ($min) et le maximum ($max).
db.restaurants.aggregate([
{ $unwind: "$grades" },
{ $group: {
_id: null,
nb: { $sum: 1 },
scoreTot: { $sum: "$grades.score" },
scoreMoy: { $avg: "$grades.score" },
scoreMin: { $min: "$grades.score" },
scoreMax: { $max: "$grades.score" }
}}
]).pretty()
Regroupement de valeurs#
Une fois que les documents sont scindés en plusieurs suite à l’action $unwind, il peut être intéressant de regrouper les valeurs dans un tableau. Ici, nous cherchons les différents grades obtenus par chaque restaurant.
db.restaurants.aggregate([
{ $limit: 5 },
{ $unwind: "$grades" },
{ $group: { _id: "$name", nb: { $sum: 1 }, eval: { $addToSet: "$grades.grade" }}}
])