Sujet de TP 2 en MongoDB : requêtes avancées & indexation
Ce sujet de TP va aborder la notion d’indexation dans les BD Mongo afin d’améliorer/optimiser les
performances de vos requêtes et accélérer la recherche de données. Vous allez ensuite charger un jeu
de données et écrire les requêtes permettant chacune de répondre à un besoin en information précis.
I. Partie 1
1. Rappel : modèle de données en MongoDB
MongoDB est une base de données NoSQL orientée documents. Une base de données MongoDB
consiste en un ensemble de collections, elles-mêmes formées de documents, construits selon un
schéma dynamique et non prédéfini.
Un document est un ensemble ordonné de paires clé-valeurs, où les clés sont des chaînes de
caractères et les valeurs peuvent une instance de n’importe quel type de donnée parmi ceux prédéfinis
(null, boolean, number, string, date, regular expression, array, object id, binary data et code), ou bien
un autre document.
2. Gestion des Index en MongoDB : fonctionnement
Lorsqu’une collection de la BD comprend plusieurs milliers de documents, une requête “find” sur un
objet particulier va être gourmande en ressources matérielles et va ralentir votre application
(augmentation du temps d’exécution) puisque Mongodb va devoir parcourir l’ensemble de la
collection jusqu’à le trouver (Collection Scan). Pour remédier à cette situation, il est pertinent
d’ajouter des MongoDB index sur les collections. Les index créent une copie d’un segment de votre
donnée, parfois dans un ordre spécifique, dans votre base de données. Cela permet à MongoDB de
pouvoir les parcourir de manière efficace pour vous retourner le ou les documents recherchés
rapidement.
Note : Lors de la création d’une collection, MongoDB crée un index ID par défaut.
Les différents types de Mongodb Index : Indexation Unique et Composée
1. Single field Mongodb index :
● Opération utilisant un seul champ indexé : Comme son nom l’indique, le single
field mongodb index sert à faire une requête sur un seul champ d’un document. Pour
créer cet index, il faut utiliser la commande mongo :
db.collection.createIndex( <key>, <options> )
Ex1 : db.users.createIndex( { firstName: 1 } )
Explication : Cette commande permet de créer un index sur la collection users et sur
le champ firstName correspondant à son prénom. L’option 1 indique que ce mongodb
index classera les prénoms par ordre croissant, ce qui est utile lorsque vous performez
des agrégations $sort pour classer vos users, dans une page liste de membres par
exemple.
Si votre collection users contient 50 000 documents, le fait de rechercher un
document par le champ firstName permettra à votre base de données de faire une
recherche sur les mongodb index firstName. Cette recherche effectuera un indexScan
et ne parcourra qu’un seul document pour vous retourner le résultat. En revanche, si
vous faites une requête sur un autre champ, mongodb sera obligé de faire un
collection Scan.
Ex 2: db.users.createIndex({age : -1})
Le nombre entier négatif (-1) dans le code indique à MongoDB de classer l’âge dans
un ordre inverse lorsque vous interrogez des données à l’aide de l’index d’âge. Ainsi,
vous n’aurez peut-être pas besoin de spécifier un ordre de tri pendant les requêtes
puisque l’index s’en charge déjà.
Le shell nous répond de la façon suivante :
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
db.users.getIndexes() : celle-ci nous affiche bien un tableau contenant deux
documents avec les détails de nos index
● Opération utilisant plusieurs champs : Si vous avez indexé le champ firstName de votre
collection Users, vous pouvez tout de même tirer les avantages de cet index lorsque vous
faites des requêtes comportant plusieurs champs, tant que le champ indexé fait partie des
critères. Par exemple, la recherche suivante:
db.users.find({ firstName: 'Sébastien', failedLoginAttempts: {$gte : 3})
Dans ce cas, la requête va utiliser le mongodb index firstName pour récupérer les users ayant
le prénom « Sébastien » et ensuite parcourir chacun de ces documents pour filtrer uniquement
ceux qui ont plus de 3 tentatives de login échouées (l'agrégation greater than)
Utiliser un seul mongodb index pour faire des requêtes sur plusieurs champs permet de
bénéficier d’une amélioration significative des performances. Pour améliorer encore plus les
requêtes comportant plusieurs champs, il existe les Compound Index
2. Compound Mongodb index
Dans une utilisation normale de votre application, il est probable que vous ayez besoin de manipuler
un document en vous basant sur plusieurs critères. Par exemple récupérer les utilisateurs provenant
d’une certaine ville et s’étant inscrit il y a moins de 14 jours. Dans ces cas-là, le Compound index est
l’outil qu’il faut utiliser. Le Compound Index contient plus d’un index. Dans ce type d’index,
l’ordre dans lequel les champs sont énumérés a son importance !
Représentation d’un compound mongodb index sur les champs nom et prénom.
Prenons l’exemple de la collection users où on aurait indexé le champ last_name puis first_name via
la commande: db.users.createIndex( { "last_name": 1, "first_name": 1 } )
Dans ce cas, faire une recherche telle que:
db.users.findOne({ last_name: "Bailey", first_name:"Christ"}) va utiliser le Compound mongodb
index et parcourir un seul document pour retrouver notre donnée.
Ex2 : Pour créer un index composé d’adresse et de type de produit sur une collection de clients, par
exemple : db.customer.createIndex({adresse : 1, produits : 1})
L’index sera ordonné d’abord par ordre alphabétique d’adresse et par ordre alphabétique de type de
produit ensuite, au sein de chacune des différentes valeurs d’adresse.
Pour spécifier un nom lors de la création d’un index composé : db.customers.createIndex({location
: 1, produits : 1, poids : -1}, {name : « myCompundIndex »})
Pour afficher tous les index d’une collection : db.collectionName.getIndexes()
Pour supprimer un index : db.collectionName.dropIndex({KEY:1 ou -1})
Pour supprimer plusieurs indexes : db.collectionName.dropIndexes({KEY1:1, KEY2, 1})
Pour plus de détails : https://www.mongodb.com/docs/manual/indexes/
II. Partie 2 : exercice pratique
1. Lancer le serveur MongoDB avec la commande mongod
2. Télécharger les fichiers suivants
http://camillepradel.fr/teaching/nosql/movielens_export/movielens_movies.json
http://camillepradel.fr/teaching/nosql/movielens_export/movielens_users.json
3. Télécharger et installer MongoDB Tools msi (contient des outils d’import, export etc.)
https://www.mongodb.com/docs/database-tools/installation/installation/
4. Ajouter à la variable d’environnement système path le chemin suivant :
C:\Program Files\MongoDB\Tools\100\bin
5. Importer ces deux fichiers dans MongoDB à partir de ligne commande système et non pas
mongo shell comme le montre la figure ci-dessous
Ces commandes permettent de créer les collections movies et users dans la base de données
MovieLens et de les peupler avec les données issues des fichiers json stockés sous la racine C.
En général :
mongoimport --db DB_Name --collection Collection_Name --type=json [optional] --file
Name-of-file-to-import
6. Connectez-vous à la base de données MovieLens
mongosh
use MovieLens : permet de connecter la session à la base de données MovieLens.
show collections: affiche les collections de la base de données courante (movies & users)
help: donne un aperçu des commandes les plus importantes et de leur usage.
6. Comprendre le schéma de la BD MovieLens
Les documents d’une collection ne sont pas soumis à un schéma fixe. Cependant, les documents de
chaque collection ont une structure similaire. Nous donnons dans cette partie un exemple de chaque
collection.
La collection movies contient des informations sur les films, c’est-à-dire leur id, titre et genre.
La collection users contient des informations portant sur les utilisateurs et les notes qu’ils ont donné
aux films. Parmi les informations sur les utilisateurs, on trouve leur id, nom, âge, occupation et sexe.
Les notes données par chaque utilisateur sont représentées dans un tableau de document, chaque
document contenant l’id d’un film (faisant référence aux id de la collection movies), la note attribuée
et la date à laquelle l’utilisateur a laissé la note.
Questions :
1. Requêtes simples
Question 1. Combien y a-t-il d’utilisateurs dans la base de données ?
https://docs.mongodb.com/manual/reference/command/count/
Question 2. Combien y a-t-il de films dans la base de données ?
Question 3. Quelle est l’occupation de Clifford Johnathan ? Écrivez une requête dont la réponse
affiche uniquement son nom et son occupation.
http://docs.mongodb.org/manual/reference/method/db.collection.find/
Question 4. Combien d’utilisateurs ont entre 18 et 30 ans (inclus) ?
Question 5. Combien d’utilisateurs sont artistes (artist) ou scientifiques (scientist) ? Question 6. Quelles
sont les dix femmes auteurs (writer) les plus âgées ?
Question 7. Quelles sont toutes les occupations présentes dans la base de données ?
2. Insertions, mises-à-jour et suppressions
Question 8. Insérer un nouvel utilisateur dans la base de données (vous, par exemple). Ne pas inclure
pour l’instant le champ movies.
https://www.mongodb.com/docs/manual/tutorial/insert-documents/
Question 9. Choisir un film de la collection movies et mettre à jour l’entrée insérée précédemment en
ajoutant le champ movies respectant le schéma adopté par les autres entrées. Pour le champ
timestamp, utiliser l’heure courante : Math.round(new Date().getTime() / 1000) OU Date.now()
https://www.mongodb.com/docs/manual/tutorial/update-documents/
Question 10. Supprimer l’entrée de la base de données.
https://www.mongodb.com/docs/manual/tutorial/remove-documents/
Question 11. Pour tous les utilisateurs qui ont pour occupation "programmer", changer cette
occupation en "developer".
https://www.mongodb.com/docs/manual/tutorial/update-documents/
3. Expressions régulières
http://docs.mongodb.org/manual/reference/operator/query/regex/
Question 12. Combien de films sont sortis dans les années quatre-vingt ? (L’année de sortie est
indiquée entre parenthèses à la fin du titre de chaque film)
Question 13. Combien de films sont sortis entre 1984 et 1992 ? Question 14.
Combien y a-t-il de films d’horreur ?
Question 15. Combien de films ont pour type à la fois "Musical" et "Romance"?
4. ForEach
Question 16. Comme vous avez pu le constater, stocker l’année de sortie du film dans son titre n’est
pas très pratique. Modifier la collection movies en ajoutant à chaque film un champ year contenant
l’année et en supprimant cette information du titre. De nombreuses méthodes peuvent répondre à ce
besoin ; privilégier au maximum les approches exploitant les fonctionnalités de MongoDB (il est par
exemple déconseillé, pour des raisons évidentes de performances, de demander l’intégralité des films
à la base de données, de les stocker dans une liste javascript, puis d’itérer sur cette liste pour calculer
les nouvelles valeurs de champs et mettre à jour les éléments, toujours en javascript).
http://docs.mongodb.org/manual/reference/method/cursor.forEach/
https://www.mongodb.com/docs/manual/reference/method/
Question 17. Modifier la collection movies en remplaçant pour chaque film la valeur du champ genres
par un tableau de chaînes de caractères.
Question 18. Modifier la collection users en remplaçant pour chaque utilisateur le champ timestamp
par un nouveau champ date, de type Date. Le champ timestamp est exprimé en secondes depuis
l’epoch Unix, c’est-à-dire le 1er janvier 1970. En javascript, les instances de Date sont créées en
utilisant le nombre de millisecondes depuis l’epoch Unix.
5. Requêtes sur des tableaux
Lecture
Question 19. Combien d’utilisateurs ont noté le film qui a pour id 1196 (Star Wars: Episode V - The
Empire Strikes Back (1980)) ?
Question 20. Combien d’utilisateurs ont noté tous les films de la première trilogie Star Wars (id 260,
1196, 1210) ?
http://docs.mongodb.org/manual/reference/operator/query/all/
Question 21. Combien d’utilisateurs ont notés exactement 48 films ?
http://docs.mongodb.org/manual/reference/operator/query/size/
Notez que $size ne peut être apparié qu’à des nombres exacts. La sélection des utilisateurs qui ont vu
plus d’un certain nombre de films doit être effectuée en deux étapes ; c’est le sujet des questions
suivantes.
Question 22. Pour chaque utilisateur, créer un champ num_ratings qui indique le nombre de films
qu’il a notés.
Question 23. Combien d’utilisateurs ont noté plus de 90 films ?
Question 24. Combien de notes ont été soumises après le 1er janvier 2001 ?
Question 26. Obtenez les informations portant uniquement sur Tracy Edward et sa note du film Star
Wars: Episode VI - Return of the Jedi, qui a pour id 1210.
Question 27. Combien d’utilisateurs ont donné au film "Untouchables, The" la note de 5.
Ecriture
Question 28. L’utilisateur Barry Erin vient juste de voir le film Nixon, qui a pour id 14 ; il lui attribue
la note de 4. Mettre à jour la base de données pour prendre en compte cette note. N’oubliez pas que le
champ num_rattings doit représenter le nombre de films notés par un utilisateur.
Question 29. L’utilisatrice Marquis Billie n’a en fait pas vu le film "Santa with Muscles", qui a pour id
1311. Supprimer la note entrée par mégarde dans la base de données.
Question 30. Les genres du film "Cinderella" devraient être Animation, Children's et Musical.
Modifier en une seule requête le document correspondant pour qu’il contienne ces trois genres sans
doublon.
6. Références
MongoDB n’intègre pas de support des jointures. Le plus souvent, les références sont dénormalisées
(dupliquées) et stockées sous forme de documents internes. Mais il est parfois plus judicieux de
stocker des informations liées dans des documents différents appartenant à des collections différentes
et de les relier par des références. Il y a deux façons de faire ça :
● Références manuelles : le champ _id d’un document est stocké dans un autre document.
C’est le cas dans notre jeu de données, où les champs _id des films sont stockés dans les
tableaux de votes dans users.
● DBRef : DBRef est une convention qui permet de référencer un document. Elle est formée par
ses informations de collection, son id et éventuellement la base de données où il est
enregistré. Il n’est pas conseillé d’utiliser cette méthode car elle n’est pas supportée par toutes
les opérations.
https://www.mongodb.com/docs/manual/reference/database-references/
Question 31. Modifier la collection users en y ajoutant un champ movies.movieref qui contient une
DBRef vers le film concerné.
Question 32. En exploitant le champ nouvellement créé, déterminer combien d’utilisateurs
ont noté le film Taxi Driver.
Question 33. En exploitant le champ nouvellement créé, déterminer combien d’utilisateurs
ont attribué au film Taxi Driver une note de 5.
Vous pouvez désormais supprimer le champ movieref qui ne sera plus utilisé dans la suite :
Notez qu’il n’est pas possible de mettre à jour avec un seul update() tous les éléments d’un tableau.
7. Index
Question 34. Chercher le nom des dix femmes qui ont noté un film le plus récemment. Notez que si
l’on ajoute la fonction explain() à la fin de la requête, on obtient des informations sur son exécution.
Question 35. Créer un index sur les champs gender et movies.date.
https://www.mongodb.com/docs/manual/reference/method/db.collection.createIndex/#mongodb-meth
od-db.collection.createIndex
Question 36. Exécuter à nouveau la requête 34. Commenter les différences.
Un Compte-Rendu CR (document pdf) à rendre à la fin de la séance.
Mettez les extraits de vos requêtes et codes MongoDB (captures d’écrans) avec une explication sur
vos codes, Vous ne rendrez que votre CR (pas de fichiers de code en plus)
Envoyez votre CR par email à
[email protected] en précisant l’objet de mail sous cette
forme :
Renommer le fichier à votre nom et prénom
Mettre comme objet TP3_TP4 MongoDB