Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
Module : Big Data et NoSQL
TP10 : Le NoSQL avec MongoDB — Framework
d’agrégation et jointures
Partie 1. Présentation et contexte
Voir l'énoncé du TP pour la présentation du contexte et les rappels sur le framework
d'agrégation.
Partie 2. Requêtes — Corrigé
2.1 — $match et $group
Q1. Combien y a-t-il de patients par sexe ? Le résultat doit afficher pour chaque sexe (M
ou F) le nombre de patients.
[Link]([
{ "$group": { "_id": "$sexe", "nombre": { "$sum": 1 } } }
])
→ $sum: 1 est l'idiome standard pour compter : pour chaque document du groupe, on ajoute
1.
Q2. Combien y a-t-il de patients par ville de résidence ? Triez le résultat par nombre
décroissant.
[Link]([
{ "$group": { "_id": "$[Link]", "nombre": { "$sum": 1 } } },
{ "$sort": { "nombre": -1 } }
])
→ La notation pointée "$[Link]" fonctionne dans les expressions d'agrégation comme
partout ailleurs.
Q3. Pour chaque mutuelle, combien y a-t-il de patients ? Excluez les patients qui n'ont
pas de mutuelle.
[Link]([
{ "$match": { "mutuelle": { "$exists": true, "$ne": null } } },
{ "$group": { "_id": "$mutuelle", "nombre": { "$sum": 1 } } },
{ "$sort": { "nombre": -1 } }
])
→ Le $match est placé en début de pipeline : c'est l'optimisation la plus importante. Plus tôt
on filtre, moins on traite de données dans les étapes suivantes.
1/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
→ On utilise $match pour éviter d'afficher le groupe qui représente les patients n'ayant pas
de champ mutuelle ou dont la valeur de mutuelle est null.
Exemple : { _id: null, nombrePatients: ... }
Q4. Quel est l'âge moyen, minimum et maximum des patients ?
[Link]([
{
"$group": {
"_id": null,
"ageMoyen": { "$avg": "$age" },
"ageMin": { "$min": "$age" },
"ageMax": { "$max": "$age" }
}
}
])
→ On utilise _id: null car on veut calculer les statistiques sur l'ensemble des patients, sans
regroupement par catégorie.
Q5. Pour chaque groupe sanguin, calculez : le nombre de patients, l'âge moyen, et la liste
des prénoms (sans doublon).
[Link]([
{
"$group": {
"_id": "$groupe_sanguin",
"nombre": { "$sum": 1 },
"ageMoyen": { "$avg": "$age" },
"prenoms": { "$addToSet": "$prenom" }
}
}
])
→ $addToSet collecte sans doublon. Si on voulait les doublons, on utiliserait $push.
2/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
2.2 — $project et transformations
Q6. Affichez pour chaque patient une nouvelle structure contenant uniquement :
nom_complet (concaténation de prenom + espace + nom), age, et ville.
[Link]([
{
"$project": {
"_id": 0,
"nom_complet": { "$concat": ["$prenom", " ", "$nom"] },
"age": 1,
"ville": "$[Link]"
}
}
])
→ Pour renommer un champ (ici, sortir [Link] sous le nom ville), on utilise la syntaxe
ville: "$[Link]".
Q7. Pour chaque médecin, affichez son nom, son tarif en MAD, et un nouveau champ
tarif_euro (1 EUR = 11 MAD).
[Link]([
{
"$project": {
"_id": 0,
"nom_complet": 1,
"tarif_consultation_mad": 1,
"tarif_euro": { "$divide": ["$tarif_consultation_mad", 11] }
}
}
])
Q8. Pour chaque patient, affichez son nom et un booléen est_majeur_adulte (vrai si age
>= 18).
[Link]([
{
"$project": {
"_id": 0,
"nom": 1,
"est_majeur_adulte": { "$gte": ["$age", 18] }
}
}
])
→ Les opérateurs de comparaison comme $gte, $lt, $eq peuvent s'utiliser dans une
expression $project pour produire un booléen.
3/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
2.3 — Tri et pagination
Q9. Affichez les 5 médecins ayant le tarif de consultation le plus élevé, avec uniquement
leur nom et leur tarif.
[Link]([
{ "$sort": { "tarif_consultation_mad": -1 } },
{ "$limit": 5 },
{ "$project": { "_id": 0, "nom_complet": 1, "tarif_consultation_mad": 1 } }
])
Q10. Pour chaque ville, affichez le nombre de patients, triés par nombre décroissant.
Limitez aux 3 villes les plus représentées.
[Link]([
{ "$group": { "_id": "$[Link]", "nombre_patients": { "$sum": 1 } } },
{ "$sort": { "nombre_patients": -1 } },
{ "$limit": 3 }
])
2.4 — $unwind
Q11. Combien y a-t-il de consultations au total dans la base (toutes consultations
confondues, tous patients confondus) ?
[Link]([
{ "$unwind": "$consultations" },
{ "$count": "total_consultations" }
])
→ Après $unwind, chaque consultation produit un document. $count compte ces documents.
Solution 2 :
[Link]([
{ "$unwind": "$consultations" },
{
"$group": {
"_id": null,
"total_consultations": { "$sum": 1 }
}
}
])
4/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
Q12. Affichez le nombre de consultations par diagnostic, triées par fréquence
décroissante.
[Link]([
{ "$unwind": "$consultations" },
{
"$group": {
"_id": "$[Link]",
"nombre": { "$sum": 1 }
}
},
{
"$sort": {
"nombre": -1
}
}
])
Q13. Quel est le diagnostic le plus fréquent dans la base ?
[Link]([
{ "$unwind": "$consultations" },
{ "$group": { "_id": "$[Link]", "nombre": { "$sum": 1 } } },
{ "$sort": { "nombre": -1 } },
{ "$limit": 1 }
])
Q14. Pour chaque médecin (par son medecin_id), combien de consultations a-t-il
effectuées ? Triez par nombre décroissant.
[Link]([
{ "$unwind": "$consultations" },
{
"$group": {
"_id": "$consultations.medecin_id",
"nombre": { "$sum": 1 }
}
},
{
"$sort": {
"nombre": -1
}
}
])
5/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
2.5 — Combiner $unwind et $match
Q15. Top 5 des diagnostics chez les patients de Casablanca uniquement.
[Link]([
{ "$match": { "[Link]": "Casablanca" } },
{ "$unwind": "$consultations" },
{
"$group": {
"_id": "$[Link]",
"nombre": { "$sum": 1 }
}
},
{
"$sort": {
"nombre": -1
}
},
{ "$limit": 5 }
])
→ Le $match initial filtre les patients AVANT de décomposer leurs consultations : plus
efficace que de filtrer après $unwind.
Q16. Pour les patients de plus de 60 ans, quel est le nombre moyen de consultations par
patient ?
[Link]([
{
"$match": {
"age": { "$gt": 60 }
}
},
{
"$project": {
"nombreConsultations": { "$size": "$consultations" }
}
},
{
"$group": {
"_id": null,
"moyenneConsultations": { "$avg": "$nombreConsultations" }
}
}
])
→ $size sur le tableau consultations donne directement le nombre, sans avoir besoin de
$unwind.
6/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
Q17. Pour les patients diabétiques, calculez la moyenne, le minimum et le maximum de
la tension systolique sur l’ensemble de leurs consultations contenant une mesure de
tension.
Un patient est considéré comme diabétique s’il possède au moins une consultation dont le
diagnostic contient le mot « diabète ».
[Link]([
{
"$match": {
"[Link]": {
"$regex": "diabète",
"$options": "i"
}
}
},
{
"$unwind": "$consultations"
},
{
"$match": {
"[Link]": {
"$exists": true
}
}
},
{
"$group": {
"_id": null,
"moyenne_systolique": {
"$avg": "$[Link]"
},
"min_systolique": {
"$min": "$[Link]"
},
"max_systolique": {
"$max": "$[Link]"
}
}
}
])
→ Deux $match : le premier sélectionne les patients diabétiques, le second (après $unwind)
ne garde que les consultations qui ont effectivement le champ tension.
7/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
2.6 — $lookup : jointures multi-collections
Q18. Pour chaque consultation (après $unwind), joignez les informations du médecin.
Affichez : la date de consultation, le motif, et le nom complet du médecin (depuis la
collection medecins).
[Link]([
{ "$unwind": "$consultations" },
{
"$lookup": {
"from": "medecins",
"localField": "consultations.medecin_id",
"foreignField": "_id",
"as": "info_medecin"
}
},
{ "$unwind": "$info_medecin" },
{
"$project": {
"_id": 0,
"date": "$[Link]",
"motif": "$[Link]",
"medecin": "$info_medecin.nom_complet"
}
}
])
→ Après $lookup, le résultat est toujours un tableau (même quand il y a une seule
correspondance). On le « déplie » avec $unwind pour avoir un objet directement.
8/9
Faculté Polydisciplinaire - Safi Filières : IIA & SD / Semestre : 6
Q19. Pour chaque spécialité de médecin, combien de consultations ont été effectuées ?
Triez par nombre décroissant.
[Link]([
{ "$unwind": "$consultations" },
{
"$lookup": {
"from": "medecins",
"localField": "consultations.medecin_id",
"foreignField": "_id",
"as": "info_medecin"
}
},
{ "$unwind": "$info_medecin" },
{
"$group": {
"_id": "$info_medecin.specialite",
"nombre_consultations": { "$sum": 1 }
}
},
{
"$project": {
"_id": 0,
"specialite": "$_id",
"nombre_consultations": 1
}
},
{
"$sort": {
"nombre_consultations": -1
}
}
])
9/9