Université Mohamed Premier Master SDSI
Faculté Pluridisciplinaire de Nador Bases de Données NOSQL
Département Informatique 2021-2022
TP1 : Premiers pas sur MongoDB.
Initiation à MongoDB
1. Connexion à MongoDB
Pour lancer le shell de Mongo, il suffit d’exécuter la commande suivante dans le terminal de
commande.
> mongo
Vous devriez voir apparaître des avertissements divers. Il ne faut pas en tenir compte.
Une fois connecté au shell, il est possible de connaître l’ensemble des bases de données présentes
sur le serveur. Pour ceci, vous devez utiliser la commande suivante :
> show dbs
Connexion à une base : use nom_de_la_base. Cette commande possède une double fonction, elle
assure la connexion si la base existe, elle la crée puis s’y connecte si la base n’existe pas encore.
Une fois connecté, la commande db permet de connaître la base.
> use tp_mongo
switched to db tp_mongo
> db
tp_mongo
Une fois la base créée et l’utilisateur connecté, il est possible de supprimer la base. À noter : la
syntaxe utilisée est une syntaxe JavaScript : db désigne ici la base sur laquelle on est connecté
(ici tp_mongo), dropDatabase() est la fonction appelée qui ne prend aucun paramètre.
> [Link]()
2. Gérer des collections
Création d’une collection dans le shell
Nous allons créer ici une collection nommée personnes.
> [Link]('personnes')
Équivalent SQL : create table personnes ...
Obtention de la liste des collections d’une base
> show collections
1
Suppression d’une collection
> [Link]()
3. Gérer des documents
Insertion dans une collection
Après avoir recréé la collection personnes, il est possible d’y insérer des données.
> [Link]({'nom': 'ALAOUI', 'prenom': 'Ahmed', 'age': 34})
WriteResult({ "nInserted" : 1 })
> [Link]({'nom': 'AARAB', 'prenom': 'Hassan', 'age': 38})
WriteResult({ "nInserted" : 1 })
> [Link]({'nom': 'FAIK', 'prenom': 'Toufik', 'age': 28})
WriteResult({ "nInserted" : 1 })
Alternative :
> p1 = {nom: 'NOURI', prenom: 'Hicham', age: 35}
{ "nom" : "NOURI", "prenom" : "Hicham", "age" : 35 }
> [Link](p1)
WriteResult({ "nInserted" : 1 })
Notes :
• La syntaxe pour exprimer le format des données utilise le format BSON (Binary JavaScript
Object Notation). De manière plus lisible, les données pourraient être représentées dans la forme
suivante :
{
nom: 'BRISSON',
prenom: 'Laurent',
age: 83
}
• Ici, toutes les données ont le même format (nom + prénom + âge), ce ne sera pas forcément le
cas tout le temps, voir plus bas.
• Les chaînes de caractères sont encadrées par le caractère ' ou ", les valeurs numériques non.
Lister le contenu d’une collection
> [Link]()
Remarques :
• La fonction find() est utilisée ici sans paramètre, on précise donc que l’on veut l’intégralité de la
collection. Voir plus loin pour exprimer des requêtes plus fines.
2
• L’intégralité des attributs est affichée avec, en plus, l’attribut _id qui permet de retrouver de
façon non ambigüe le document dans la collection. Cet attribut est généré automatiquement par
MongoDB lors de l’insertion du document.
A faire
1. Insertion d’une personne dont on donne un attribut supplémentaire : DOUDOUH
Moncef, 50 ans, sexe M
2. Insertion d’une personne dont on ne connait que le nom et le prénom : DOUDOUH Ali
3. Insertion d’une personne avec un objet imbriqué (attribut contacts) contenant le
numéro de téléphone portable, le numéro de téléphone du domicile et l’adresse email
: DOUDOUH Omar, 55 ans, portable : 06 xx xx xx xx xx, domicile : 05 xx xx xx xx, email :
[Link]@[Link]
4. Pour visualiser l’imbrication, utilisez la commande [Link]().pretty()
5. Insertion d’un document dont la structure est entièrement différente. Par exemple, un
livre avec pour titre Astérix en Bretagne et pour auteur Gosciny-Uderzo.
Mémento syntaxique
Commande Description
mongo Lancement du shell
exit (ou Ctrl+C) Sortie du shell mongo
help Aide sur les principales commandes
use nom_de_la_base Connexion à la base spécifiée, création si elle
n’existe pas
[Link]() Suppression de la base (exige d’y être connecté)
show dbs Liste des bases de données
db Renvoie le nom de la base où l’utilisateur est
connecté
mongoimport Utilitaire MongoDB pour l’import de données
[Link]('nom_collection') Création d’une collection
db.nom_collection.insert({...}) Insertion dans une collection
db.nom_collection.drop() Suppression d’une collection
db.nom_collection.find() Afficher la liste complète des documents d’une
collection
show collections Liste les collections de la base où l’utilisateur est
connecté
3
Intégration de données de type JSON
1. Utilisation de Studio 3T
Création d’une base et d’une collection
Nous allons ici créer une première base de données, que l’on nommera test, contenant une seule
collection pour le moment, nommée essais.
1. Ouvrir Studio 3T
2. Cliquer directement sur Connect (votre serveur étant local, il n’y a pas besoin de le spécifier)
3. Vous devriez voir 3 bases de données déjà existantes : admin, config et local. Cliquer sur CREATE
DATABASE affiché en haut
4. Il faut maintenant nommer la nouvelle base de données, et la collection dans laquelle nous allons
mettre les données :
• Database Name : test
• Collection Name: restaurants
5. Cliquer maintenant sur CREATE DATABASE
6. Vous devriez voir apparaître la base test à gauche (et la collection restaurants lorsque vous
cliquez sur la petite flèche à droite de test)
Lorsque vous cliquez sur la collection, dans la base, vous voyez le détail de son contenu. Pour
le moment, notre collection restaurants est vide. Il n’y a donc rien. Il est possible d’importer des
documents directement.
Importation des données
Nous allons importer maintenant les données dans notre collection ainsi créée. Vous devez avoir
téléchargé le fichier [Link]
Une fois téléchargé, suivez la procédure suivante pour l’ajouter dans Mongo :
1. Cliquer sur restaurants pour voir le contenu de la collection (vide donc pour le moment)
2. Cliquer sur ADD DATA en haut et choisissez Import File (ou cliquer sur Import data directement)
3. Sélectionner le fichier téléchargé, et choisir aussi JSON
4. Cliquer sur IMPORT
5. Une fois l’opération terminé (25359 documents ajoutés), cliquer sur DONE
Votre base est maintenant créée avec une collection de 25359 restaurants donc.
Notes :
• Toute commande sur la collection restaurants utilise le préfixe : [Link]. Il suffira d’y
associer la fonction souhaitée pour avoir un résultat.
Avant de commencer, il faut voir à quoi ressemble un document de notre collection restaurants.
Utilisons la fonction findOne().
2. Filtrer et Projeter les données avec un motif JSon
Filtrage
Pour commencer on va tester un type simple de requête : le filtrage. On utilisant la fonction find()
4
à appliquer directement à la collection.
> [Link]( { "borough" : "Brooklyn" } )
Cette requête récupère tous les restaurants dans le quartier (borough) de Brooklyn.
Pour compter, il suffit d’ajouter la fonction “count"
> [Link]( { "borough" : "Brooklyn" } ).count()
Maintenant, nous cherchons parmi ces restaurants ceux qui font de la cuisine italienne. Pour
combiner deux "clés/valeurs", il suffit de faire le document motif donnant quels paires
clés/valeurs sont recherchés :
> [Link](
{ "borough" : "Brooklyn",
"cuisine" : "Italian" }
)
Ensuite, on va chercher les restaurants présents sur la 5° Avenue. La clé "street" est imbriquée
dans l’adresse ; comment filtrer une clé imbriquée (obtenue après fusion) ? Il faut utiliser les
deux clés, en utilisant un point "." comme si c’était un objet.
> [Link](
{ "borough" : "Brooklyn",
"cuisine" : "Italian",
"[Link]" : "5 Avenue" }
)
Pourquoi ne pas chercher le mot "pizza" dans le nom du restaurant ? Pour cela, il faut utiliser les
expressions régulières avec "/xxx/i" (le i pour "Insensible à la casse" - majuscule/minuscule).
> [Link](
{ "borough" : "Brooklyn",
"cuisine" : "Italian",
"[Link]" : "5 Avenue",
"name" : /pizza/i }
)
Il ne reste maintenant que 2 restaurants Italiens dans le quartier de Brooklyn dans la 5° Avenue,
dont le nom contient le mot "pizza".
Projection
Et si nous ne gardions dans le résultat que le nom ? C'est ce que l'on appelle une projection. Un
deuxième paramètre (optionnel) de la fonction find permet de choisir les clés à retourner dans le
résultat. Pour cela, il faut mettre la clé, et la valeur "1" pour projeter la valeur (on reste dans le
concept "clé/valeur").
> [Link]('restaurants').find(
{"borough":"Brooklyn",
"cuisine":"Italian",
"name":/pizza/i,
5
"[Link]" : "5 Avenue"},
{"name":1}
)
{"_id" : ObjectId("594b9173c96c61e672dd074b"), "name" : "Joe'S Pizza"}
{"_id" : ObjectId("594b9173c96c61e672dd1c16"), "name" : "Gina'S Pizzaeria/ Deli"}
L'identifiant du document "_id" est automatiquement projeté, si vous souhaitez le faire
disparaître il suffit de rajouter la valeur "0" : {"name":1, "_id":0}
Et si nous regardions les résultats des inspections des commissions d'hygiène ? Il faut pour cela
regarder la valeur de la clé [Link].
> [Link]('restaurants').find(
{"borough":"Brooklyn",
"cuisine":"Italian",
"name":/pizza/i,
"[Link]" : "5 Avenue"},
{"name" : 1,
"[Link]" : 1}
)
{
"name" : "Joe'S Pizza",
"grades" : [
{"score" : 12},
{"score" : 13},
{"score" : 42},
{"score" : 25},
{"score" : 19},
{"score" : 40},
{"score" : 22}
]
}
{
"name" : "Gina'S Pizzaeria/ Deli",
"grades" : [
{"score" : 12},
{"score" : 23},
{"score" : 13}
]
}
Ce qui est intéressant est de voir que le score est retrouvé dans les documents imbriqués de la
liste "grades", et que tous les scores sont projetés.
Filtrage avec des opérations
Le filtrage par valeur exacte n'est pas suffisant pour exprimer tout ce que l'on souhaiterait trouver
dans la collection. Je cherche maintenant les noms et scores des restaurants de Manhattan ayant
un score inférieur à 10. On peut utiliser des opérateurs arithmétiques sur les clés (valeur
numérique). Pour cela, il sera préfixé d'un dollar $. "Plus petit que" en anglais se dit "less than",
l'opérateur est donc $lt. Il faut y associer la valeur souhaitée : "[Link]" : {"$lt" : 10}.
L'opérateur est toujours imbriqué dans un document. Le concept "clé/valeur" doit être respecté.
Voici les opérateurs disponibles avec des exemples associés :
6
$gt, $gte >, ≥ Plus grand que (greater than) "a" : {"$gt" : 10}
$lt, $lte <, ≤ Plus petit que (less than) "a" : {"$lt" : 10}
$ne ≄ Différent de (not equal) "a" : {"$ne" : 10}
$in, $nin ∈, ∉ Fait parti de (ou ne doit pas) "a" : {"$in" : [10, 12, 15, 18] }
$or ៴ OU logique "a" : {“$or” : [{"$gt" : 10}, {“$lt” : 5} ] }
$and ៱ ET logique "a" : {“$and” : [{"$lt" : 10}, {“$gt” : 5} ] }
$not ¬ Négation “a" : {“$not” : {"$lt" : 10} }
$exists ∃ La clé existe dans le document “a” : {“$exists” : 1}
$size test sur la taille d'une liste “a” : {“$size” : 5}
(uniquement par égalité)
La requête donne ceci :
> [Link]('restaurants').find(
{"borough":"Manhattan",
"[Link]":{$lt : 10}
},
{"name":1,"[Link]":1, "_id":0})
{
"name" : "Dj Reynolds Pub And Restaurant"
"grades" : [
{"score" : 2},
{"score" : 11},
{"score" : 12},
{"score" : 12}
]
}
Remarque :
Ce qui est déroutant dans ce résultat, c'est le fait que l'on trouve des scores supérieurs à 10 ! Nous
ne sommes plus en relationnel, ainsi, l'opération "[Link]" : {"$lt" : 10}, veut dire : Est-ce que
la liste "grades" contient un score (au moins) avec une valeur inférieure à 10 ?
Et en effet, il y a un score à "2" respectant la question.
Si l'on souhaite ne récupérer que ceux qui n'ont pas de score supérieur à 10, il faut alors combiner
la première opération avec une négation de la condition " ≥10 ". La condition est alors vérifiée
sur chaque élément de la liste.
7
> [Link]('restaurants').find(
{"borough":"Manhattan",
"[Link]":{
$lt:10,
$not:{$gte:10}
}
},
{"name":1,"[Link]":1, "_id":0})
{
"name" : "1 East 66Th Street Kitchen",
"grades" : [
{"score" : 3},
{"score" : 4},
{"score" : 6},
{"score" : 0}
]
}
On pourrait même corser la chose en cherchant les restaurants qui ont un grade ‘C’ avec un score
inférieur à 40.
> [Link]({
"[Link]" : "C",
"[Link]" : {$lt : 40}
},
{"[Link]":1, "[Link]":1}
);
{
"_id" : ObjectId("594b9172c96c61e672dcd695"),
"grades" : [
{"grade" : "B","score" : 21},
{"grade" : "A","score" : 7},
{"grade" : "C","score" : 56},
{"grade" : "B","score" : 27},
{"grade" : "B","score" : 27}
]
}
{
"_id" : ObjectId("594b9172c96c61e672dcd6bc"),
"grades" : [
{"grade" : "A","score" : 9},
{"grade" : "A","score" : 10},
{"grade" : "A","score" : 9},
{"grade" : "C","score" : 32}
]
}
Le résultat est pour le moins étrange concernant le premier document car il y a bien un grade C,
mais avec un score de 56 ! Si l’on regarde la requête de plus près, ce n’est pas étonnant. Y a-t-il
un grade ‘C’ ? oui. Y a-t-il un score inférieur à 40 ? oui… Nous avons oublié de préciser qu’il
fallait que ce soit vérifié sur chaque élément ! La vérification se fait sur l’intérieur de la liste, pas
sur chaque élément de la liste. Pour cela, un opérateur permet de faire une vérification instance
par instance : $elemMatch.
8
> [Link]({
"grades" : {
$elemMatch : {
"grade" : "C",
"score" : {$lt :40}
}
}
},
{"[Link]" : 1,"[Link]" : 1}
);
{
"_id" : ObjectId("594b9172c96c61e672dcd6bc"),
"grades" : [
{"grade" : "A","score" : 9},
{"grade" : "A","score" : 10},
{"grade" : "A","score" : 9},
{"grade" : "C","score" : 32}
]
}
Une dernière pour finir, je voudrais maintenant les noms et quartiers des restaurants dont la
dernière inspection (la plus récente, donc la première de la liste) a donné un grade ‘C’. Il faut
donc chercher dans le premier élément de la liste. Pour cela il est possible de rajouter l’indice
recherché (indice 0) dans la clé.
> [Link]({
"[Link]":"C"
},
{"name":1, "borough":1, "_id":0}
);
{"borough" : "Queens","name" : "Mcdonald'S"}
{"borough" : "Queens","name" : "Nueva Villa China Restaurant"}
{"borough" : "Queens","name" : "Tequilla Sunrise"}
{"borough" : "Manhattan","name" : "Dinastia China"}
Distinct
Au fait, quels sont les différents quartiers de New York ? Pour cela, on peut utiliser la fonction
distinct() avec la clé recherchée pour avoir une liste de valeur :
> [Link]("borough")
[
"Bronx",
"Brooklyn",
"Manhattan",
"Queens",
"Staten Island",
"Missing"
]
Et si nous le faisions sur une liste de valeurs comme les grades donnés par les inspecteurs ?
9
> [Link]("[Link]");
[
"A",
"B",
"Z",
"C",
"P",
"Not Yet Graded"
]
La fonction distincte va donc chercher les instances de la liste pour en extraire chaque grade et
produire une liste distincte (plutôt que de faire une liste de liste distincte).
A faire
1. Donner les styles de cuisine présent dans la collection
2. Donner tous les grades possibles dans la base
3. Compter le nombre de restaurants proposant de la cuisine fraçaise ("French")
4. Compter le nombre de restaurants situé sur la rue "Central Avenue"
5. Compter le nombre de restaurants ayant eu une note supérieure à 50
6. Lister tous les restaurants, en n'affichant que le nom, l'immeuble et la rue
7. Lister tous les restaurants nommés "Burger King" (nom et quartier uniquement)
8. Lister les restaurants situés sur les rues "Union Street" ou "Union Square"
9. Lister les restaurants situés au-dessus de la lattitude 40.90
10. Lister les restaurants ayant eu un score de 0 et un grade "A"
Questions complémentaires
Nécessitent une recherche sur la toile pour compléter ce qu'on a déjà vu dans ce TP.
1. Lister les restaurants (nom et rue uniquement) situés sur une rue ayant le terme
"Union" dans le nom
2. Lister les restaurants ayant eu une visite le 1er février 2014
3. Lister les restaurants situés entre les longitudes -74.2 et -74.1 et les lattitudes 40.1 et
40.2
10