Documents de cours 2021-2022
Il est bien évidemment possible de réaliser des calculs d’agrégats (de type somme, moyenne, minimum et maximum) dans MongoDB,
avec la fonction aggregate(). Celle-ci permet beaucoup d’autres opérations.
Cette fonction va prendre en paramètre un tableau nommé pipeline : tableau composé d’une suite d’opérations.
Chaque opération sera faite après la précédente. L’ordre des opérations a donc une importance cruciale. Et le même opérateur peut apparaître plusieurs fois.
Voici quelques unes des opérations possibles :
| Fonction | Opération |
|---|---|
$limit |
restriction à un petit nombre de documents (très utiles pour tester son calcul) |
$sort |
tri sur les documents |
$match |
restriction sur les documents à utiliser |
$unwind |
séparation d’un document en plusieurs sur la base d’un tableau |
$addFields |
ajout d’un champs dans les documents |
$project |
redéfinition des documents |
$group |
regroupements et calculs d’aggégrats |
$sortByCount |
agrégat + tri |
$lookup |
jointure avec une autre collection |
| … |
pipeline$limitOn indique juste avec un entier le nombre de document que l’on veut afficher.
db.restaurants.aggregate([
{ $limit: 10 }
])
Comme déjà vu dans le précédent TP, on peut ajouter la fonction pretty() au résultat pour avoir un affichage plus clair.
db.restaurants.aggregate([
{ $limit: 10 }
]).pretty()
$sortOn indique de façon identique à celle du paramètre sort de la fonction find()
db.restaurants.aggregate([
{ $limit: 10 },
{ $sort: { name: 1 }}
]).pretty()
$matchIci, c’est identique à celle du paramètre query des autres fonctions
db.restaurants.aggregate([
{ $limit: 10 },
{ $sort: { name: 1 }},
{ $match: { borough: "Brooklyn" }}
]).pretty()
db.restaurants.aggregate([
{ $match: { borough: "Brooklyn" }},
{ $limit: 10 },
{ $sort: { name: 1 }}
]).pretty()
$unwindLe but de cette opération est d’exploser un tableau dans un document.
Un document avec un tableau à n éléments deviendra n documents avec chacun un des éléments du tableau en lieu et place de celui-ci
Nous devons mettre le nom du tableau servant de base pour le découpage (précédé d’un $)
grades)
db.restaurants.aggregate([
{ $limit: 10 },
{ $unwind: "$grades" }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $unwind: "$grades" },
{ $match: { "grades.grade": "B" }}
]).pretty()
$unwind et $match, le résultat est clairement différentdb.restaurants.aggregate([
{ $limit: 10 },
{ $match: { "grades.grade": "B" }},
{ $unwind: "$grades" }
]).pretty()
$addFields et $projectOn souhaite ici rédéfinir les documents en ajoutant des éléments ($addFields) ou en se restreignant à certains éléments
($project)
{ "champs" : 1 } : conservation du champs (0 si suppression - idem que dans find(), pas de mélange sauf pour _id)
$project{ "champs": { "$opérateur" : expression }} : permet de définir un nouveau champs{ "nouveau_champs": "$ancien_champs" } : renommage d’un champsQuelques opérateurs utiles pour la projection (plus d’info ici)
$arrayElemAt : élément d’un tableau$first et $last : premier ou dernier élément du tableau$size : taille d’un tableau$substr : sous-chaîne de caractères$cond : permet de faire une condition (genre de if then else)…
db.restaurants.aggregate([
{ $limit: 10 },
{ $addFields: { nb_grades: { $size: "$grades" } } }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $project: { name: 1, borough: 1 } }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $project: { address: 0, grades: 0 } }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $project: { name: 1, borough: 1 , street: "$address.street"} }
]).pretty()
grades)db.restaurants.aggregate([
{ $limit: 10 },
{ $project: { name: 1, borough: 1, nb_grades: { $size: "$grades" } } }
]).pretty()
grades est préent mais égal à NULL)gradesdb.restaurants.aggregate([
{ $project: { name: 1, borough: 1, nb_grades: { $size: "$grades" } } },
{ $sort: { nb_grades: 1 }},
{ $limit: 10 }
]).pretty()
grades (indicé 0)db.restaurants.aggregate([
{ $limit: 10 },
{ $project: { name: 1, borough: 1, grade: { $arrayElemAt: [ "$grades", 0 ]} } }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $project: { nom: { $toUpper: "$name" }, borough: 1 } }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $project: {
nom: { $toUpper: "$name" },
quartier: { $substr: [ "$borough", 0, 3 ] }
} }
]).pretty()
db.restaurants.aggregate([
{ $limit: 10 },
{ $addFields: { quartier: { $toUpper: { $substr: [ "$borough", 0, 3 ] } } }},
{ $project: {
nom: { $toUpper: "$name" },
quartier: { $cond: { if: { $eq: ["$borough", "Bronx"] }, then: "BRX", else: "$quartier" } },
borough: 1
} }
]).pretty()
$groupCet opérateur permet le calcul d’agrégats tel qu’on le connaît.
_id : déclaration du critère de regroupement
$champs : regroupement selon ce champs{ a1: "$champs1", ... } : regroupement multiple (avec modification des valeurs possible)$sum : somme (soit de valeur fixe - 1 pour faire un décompte donc, soit d’un champs spécifique)$avg, $min, $max$addToSet : regroupement des valeurs distinctes d’un champs dans un tableau$push : aggrégation de champs dans un tableaudb.restaurants.aggregate([
{ $group: { _id: "Total", NbRestos: { $sum: 1 }}}
])
db.restaurants.aggregate([
{ $group: { _id: "$borough", NbRestos: { $sum: 1 }}}
])
db.restaurants.aggregate([
{ $match: { borough: "Queens" }},
{ $unwind: "$grades" },
{ $group: { _id: "null", score: { $avg: "$grades.score" }}}
])
db.restaurants.aggregate([
{ $unwind: "$grades" },
{ $group: { _id: "$borough", score: { $avg: "$grades.score" }}},
{ $sort: { score: -1 }}
])
$match permet de supprimer les restaurants sans évaluations (ce qui engendrerait des moyennes = NA)db.restaurants.aggregate([
{ $project: {
borough: 1, street: "$address.street",
eval: { $arrayElemAt: [ "$grades", 0 ]}
} },
{ $match: { eval: { $exists: true } } },
{ $match: { "eval.score": { $gte: 0 } } },
{ $group: {
_id: { quartier: "$borough", rue: "$street" },
score: { $avg: "$eval.score" }
}},
{ $sort: { score: 1 }},
{ $limit: 10 }
])
$addToSet et $push, on les applique sur les grades obtenus pour les 10 premiers restaurants
$addToSet : valeurs distinctes$push : toutes les valeurs présentesdb.restaurants.aggregate([
{ $limit: 10 },
{ $unwind: "$grades" },
{ $group: {
_id: "$name",
avec_addToSet: { $addToSet: "$grades.grade" },
avec_push: { $push: "$grades.grade" }
}}
])
$sortBycountCet opérateur réalise un regroupement sur le champs spécifié (précédé d’un $), compte le nombre de document pour chaque
modalité de ce champs, puis fait un tri décroissant sur le nombre calculé. Il est clairement fait pour réaliser des TOPs donc.
db.restaurants.aggregate([
{ $sortByCount: "$borough" }
])
db.restaurants.aggregate([
{ $group: { _id: "$borough", count: { $sum: 1 } } },
{ $sort: { count: -1 }}
])
addToSet)gradesaddToSet, et restriction à ceux pour lequel le tableau créé = [“A”] - par exemple)pushslice pour prendre une partie d’un tableau