Premières requêtes en MongoDB
Contents
1. Premières requêtes en MongoDB¶
Autrices et auteurs : Julie FRANCOISE, Manon MAHEO et Valentin PENISSON
1.1. Introduction à MongoDB¶
Dans un système de base de données relationnelles (comme les bases de données que l’on interroge avec la syntaxe SQL), les données sont stockées par ligne dans des tables (également appelées relations). Le modèle de données relationnel est un modèle structuré, comportant des attributs typés (les colonnes/attributs des tables ont un type précis qu’il soit numérique, alphanumérique ou temporel) et des contraintes d’intégrité (comme par exemple celle de l’unicité des valeurs de la clé primaire). Dans ce type de structure, il est nécessaire d’établir des jointures sur plusieurs tables afin de tirer des informations pertinentes sur la base de données.
Dans MongoDB, les données sont modélisées sous forme de paires clés-valeurs (comme dans un dictionnaire en Python). On ne parle plus de tables et d’enregistrements mais de collections et de documents. Une collection est un ensemble de documents, c’est l’équivalent d’une table dans un modèle relationnel. Un document est un enregistrement, une ligne dans le modèle de données relationnel. Ce système de gestion de données nous évite de faire des jointures de tables car toutes les informations nécessaires sont stockées dans un même document.
De plus, l’utilisation de bases de données NoSQL avec MongoDB nous permet plus de flexibilité en termes de mise à jour de la structure des données : aucun modèle n’est supposé sur les données, aucun attribut n’est obligatoire et il n’y a pas de type fixé pour un attribut.
Tout document appartient donc à une collection et a un champ appelé _id
qui identifie le document dans la base de données. Prenons pour exemple la base de données etudiants
. Voici un exemple de document :
{
"_id" : ObjectId("56011920de43611b917d773d"),
"nom" : "Paul",
"notes" : [
10.0,
12.0
],
"sexe" : "M"
}
Dans ce document, on a accès au nom de l’étudiant par la clé nom
, à ses notes par la clé notes
(attention, ici on a une liste de valeurs entre crochets, ce type d’attribut n’existe pas dans le modèle relationnel) et à son sexe par la clé sexe
. L’étudiant représenté par ce document est identifié à l’aide d’une clé _id
.
Les clés se doivent d’être des chaînes de caractères et les valeurs peuvent être des valeurs booléennes, des nombres, des chaînes de caractères, des dates ou des listes de valeurs comme nous venons de le voir. Les clés et les valeurs sont sensibles à la casse et au type. Chaque clé doît être unique, il n’est pas possible d’avoir deux fois la même clé dans un document.
Dans ce chapitre, nous étudierons dans un premier temps comment interroger les données d’une base de données MongoDB avec la fonction find
.
Dans un deuxième temps, nous regarderons comment effectuer des requêtes plus complexes, impliquant des opérateurs de comparaison.
Quelques méthodes utiles pour des requêtes en MongoDB, une fiche “résumé” des points saillants à retenir et un petit quiz sont fournis à la fin de ce chapitre.
1.2. Requêtes d’interrogation et de filtrage des données¶
Pour récupérer des documents stockés dans une collection, il est nécessaire d’utiliser la fonction find
.
Remarque
Toute commande sur une collection intitulée collectionName
utilise le préfixe db : db.collectionName
. Il suffit d’y associer la fonction souhaitée pour avoir un résultat. En l’occurence, ici la syntaxe de données d’interrogation MongoDB est db.collectionName.find()
(où vous devrez changer collectionName
par le nom de la collection sur laquelle vous souhaitez effectuer une requête).
1.2.1. Syntaxe d’interrogation de données sans et avec condition¶
En MongoDB, lorsque l’on interroge une base de données, il existe deux types de requêtes simples, retournant respectivement toutes les occurences d’une collection ou seulement la première. Que l’on souhaite récupérer la première occurrence de la liste des résultats ou bien toute la liste des résultats, voici la syntaxe :
Retourner toutes les occurences d’une collection avec find()
db.collectionName.find()
db.collectionName.find({})
Retourner uniquement la première occurence d’une collection avec findOne()
db.collectionName.findOne()
db.collectionName.findOne({})
À noter : Dans les deuxièmes propositions de chaque cas présenté ci-dessus, on a des accolades entre les parenthèses de la fonction. Ces accolades correspondent au document masque. Elles sont vides ce qui indique que nous ne posons pas de condition sur les documents à retourner. Si au contraire on souhaite fixer des contraintes sur les documents à retourner, il suffit de passer en argument d’une de ces fonctions un document masque contenant les valeurs souhaitées.
Pour voir cela sur un exemple, sélctionnons la base food
comme suit :
use food
switched to db food
Cette base de données contient une collection NYfood
qui recense un ensemble de restaurants new-yorkais et nous donne, pour chaque restaurant, des informations sur son quartier, son adresse, son type de cuisine, son nom, les notes qu’il a obtenues et son identifiant. Voici un extrait d’un document présent dans la collection NYfood
:
{
"_id" : ObjectId("6006c1882822efb1c9290f68"),
"address" : {
"building" : "265-15",
"loc" : {
"type" : "Point",
"coordinates" : [
-73.7032601,
40.7386417
]
},
"street" : "Hillside Avenue",
"zipcode" : "11004"
},
"borough" : "Queens",
"cuisine" : "Ice Cream, Gelato, Yogurt, Ices",
"grades" : [
{
"date" : ISODate("2014-10-28T00:00:00.000Z"),
"grade" : "A",
"score" : 9
},
{
"date" : ISODate("2013-09-18T00:00:00.000Z"),
"grade" : "A",
"score" : 10
},
{
"date" : ISODate("2012-09-20T00:00:00.000Z"),
"grade" : "A",
"score" : 13
}
],
"name" : "Carvel Ice Cream",
"restaurant_id" : "40361322"
}
En utilisant la syntaxe précédente, recherchons par exemple les documents de la collection NYfood
correspondant à des boulangeries (pour lesquels le champ “cuisine” vaut “Bakery”) du Bronx (pour lesquels le champ “borough” vaut “Bronx”).
db.NYfood.find(
{"cuisine": "Bakery", "borough": "Bronx"}
)
SELECT *
FROM NYfood
WHERE cuisine = 'Bakery' AND borough = 'Bronx'
Dans cet exemple, et dans vos requêtes MongoDB en général, la virgule représente un ET logique entre les contraintes.
1.2.2. Poser une condition sur une clé de sous-document¶
Il se peut que pour une clé d’un document, comme par exemple l’adresse d’un restaurant dans la collection NYfood
, nous disposions d’un sous-document contenant à la fois les coordonnées GPS et l’adresse postale. Plutôt qu’une liste de valeur comme présentée précédemment, nous avons comme valeur de la clé un nouveau document.
Si l’on souhaite poser une condition sur une clé ou plusieurs clés de sous-document, on utilise alors la syntaxe suivante :
db.NYfood.find({"address.zipcode": "10462"})
où address
est le sous-document et zipcode
la clé de ce dernier. Dans cet exemple, nous nous intéressons aux restaurants pour lesquels le zipcode est "10462"
.
1.2.3. Projection des données¶
Les résultats obtenus jusqu’à présent sont parfois assez indigestes, notamment parce que toutes les clés sont retournées pour tous les documents. Il est possible de limiter cela en spécifiant les clés à retourner comme second argument de la fonction find()
. On appelle cela une projection.
La projection permet de sélectionner les informations à renvoyer. Si, par exemple, je m’intéresse uniquement aux noms des boulangeries du Bronx, je vais limiter les informations retournées en précisant, comme deuxième argument de ma requête find
, la clé name
avec la valeur true
.
db.NYfood.find({"cuisine": "Bakery", "borough": "Bronx"}, {"name": true})
SELECT name
FROM NYfood
WHERE cuisine = 'Bakery' AND 'borough' = 'Bronx'
C’est donc l’équivalent du SELECT name
en SQL. Jusqu’ici, on utilisait le SELECT *
(pour all) c’est-à-dire qu’on récupérait toutes les valeurs de chaque clé ou de chaque attribut.
Embellissez vos résultats !
Lorsque vous effectuez vos requêtes depuis un client MongoDB en ligne de commande, les résultats de la fonction find()
peuvent apparaître désorganisés. MongoDB fournit pretty()
qui affiche les résultats sous une forme plus lisible. La syntaxe est la suivante : db.collectionName.find().pretty()
😉
Pour plus de renseignements sur la fonction find()
, consultez la documentation MongoDB disponible ici.
1.3. Requêtes plus complexes en utilisant des opérateurs¶
Nous verrons dans cette section deux types d’opérateurs : les opérateurs de comparaison et les opérateurs logiques.
Remarque
La syntaxe des requêtes avec des opérateurs de comparaison est la suivante : db.collectionName.find({"x": {operateur: valeur}})
.
1.3.1. Opérateurs de comparaison¶
L’opérateur de comparaison permet de comparer deux élements entre eux. Le tableau suivant regroupe l’ensemble des opérateurs de comparaison :
Opérateur logique |
Mot clé en MongoDB |
---|---|
= |
|
≠ |
|
< |
|
> |
|
≤ |
|
≥ |
|
∈ |
|
∉ |
|
clé existante |
|
|.| |
|
Les opérateurs $eq
, $ne
, $lt
, $gt
, $lte
, $gte
s’utilisent de la même façon en MongoDB. Ces opérateurs comparent la valeur d’une variable à une valeur fixe (nombre, booléen, chaine de caractères…), comme dans l’exemple suivant :
MongoDB
db.notes.find({"notes": {$gte: 13}})
SQL
SELECT *
FROM notes
WHERE notes >= 13
Cet exemple retourne la liste des étudiants ayant au moins une note supérieure ou égale à 13.
Les opérateurs $in
et $nin
s’utilisent de la même façon en MongoDB ($nin
étant la négation de $in
).
Ces opérateurs testent l’existence de la valeur d’une variable dans une liste.
La façon de l’utiliser en MongoDB est la suivante :
MongoDB
db.collectionName.find(
{"a": { $in: ["chaine1", "chaine2"] }
}
)
SQL
SELECT *
FROM t
WHERE a IN ("chaine1", "chaine2")
L’opérateur $exists
vérifie l’existence d’une clé dans un document. Sa syntaxe en MongoDB est :
db.collectionName.find(
{"a": { $exists: true}
}
)
Cette requête renvera donc les documents ayant une clé a
.
Enfin, l’opérateur $size
permet de récuperer les documents pour lesquels l’attribut considéré est une liste d’une certaine taille.
Sa syntaxe en MongoDB est la suivante :
db.collectionName.find(
{"a": { $size: 5}
}
)
Le résultat obtenu est l’ensemble des documents pour lesquels la liste associée à la clé a
est de taille 5.
1.3.2. Opérateurs logiques¶
Les différents opérateurs logiques en MongoDB sont : $or
, $not
et $nor
.
Ces opérateurs permettent de tester plusieurs conditions simultanément.
1.3.2.1. ET logique¶
Comme indiqué plus haut, pour faire une requête avec un ET logique en MongoDB, il suffit de séparer par une virgule les conditions. L’exemple ci-dessous nous montre l’équivalence entre MongoDB et le langage SQL :
MongoDB
db.collectionName.find(
{"a": 1, "b": 5}
)
SQL
SELECT *
FROM t
WHERE a = 1 and b = 5
Le résultat de la requête sera les documents validant les deux conditions suivantes : a=1
et b=5
.
1.3.2.2. OU logique¶
L’opérateur $or
permet de renvoyer les documents qui remplissent au moins un des conditions de la requête.
Le OU logique se construit de la manière suivane : $or : [{condition 1}, ... , {condition i}]
. Voici un exemple faisant le parallèle entre le langage MongoDB et le langage SQL :
MongoDB
db.collectionName.find(
{$or :
[
{"a": 1},
{"b": 5}
]
}
)
SQL
SELECT *
FROM t
WHERE a = 1 or b = 5
Le résultat de la requête sera les documents validant au moins un des deux conditions suivantes : a=1
ou b=5
.
1.3.2.3. NON OU logique¶
L’opérateur $nor
permet de renvoyer les documents ne validant aucune des conditions d’une liste de conditions. Voici sa syntaxe qui est très semblable à celle de $or
:
db.collectionName.find(
{$nor :
[
{"a": 1},
{"b": "blue"}
]
}
)
Le résultat de cette requête sera l’ensemble des documents ne contenant pas la valeur 1 pour l’attribut a
ni la valeur "blue"
pour l’attribut b
.
1.3.2.4. NON logique¶
Le $not
renvoie les documents qui ne remplissent pas la condition qu’il contient.
Reprenons l’exemple précédent :
db.collectionName.find(
{$not :
{"a": 1}
}
)
Cette requête retournera donc l’ensemble des documents pour lesquels la clé "a"
n’est pas associée à la valeur 1
.
Pour plus de renseignements sur les opérateurs, consultez la documentation MongoDB disponible à cette adresse.
1.4. Méthodes utiles pour des requêtes en MongoDB¶
1.4.1. Connaître la liste des collections dans une base de données¶
Pour connaître la liste des collections contenues dans une base de données, on utilise la commande suivante sur la base de données considérée :
db.getCollectionInfos()
[
{
"name" : "NYfood",
"type" : "collection",
"options" : {
},
"info" : {
"readOnly" : false,
"uuid" : UUID("812b36e5-7a94-487e-985d-5a3c7ec1ba41")
},
"idIndex" : {
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
}
}
]
Ci-dessus, par exemple, la base de données courante (food
) ne contient qu’une collection dont le nom (name
) est "NYfood"
.
1.4.2. Valeurs distinctes d’un champ : la méthode distinct
¶
La méthode distinct
permet de renvoyer toutes les valeurs distinctes du champ spécifié. C’est l’équivalent du SELECT DISTINCT
en SQL.
Par exemple, la requête suivante en MongoDB permet d’afficher la liste des notes attribuées à des restaurants du quartier "Manhattan"
pour la collection NYfood
.
Syntaxe et exemple en MongoDB
db.collectionName.distinct(
champ,
{...} // Ici, une condition
)
Équivalent de la syntaxe en SQL
SELECT DISTINCT(champ)
FROM collectionName
WHERE ... -- Ici, une condition
1.4.3. Compter le nombre d’éléments : la méthode count
¶
La méthode count
permet de compter le nombre de documents dans une collection. On peut l’utiliser directement sur la collection de base pour connaître le nombre de documents dans la collection ou bien l’utiliser après avoir exécuté une requête :
db.NYfood.count()
db.NYfood.find({"cuisine": "Bakery"}).count()
db.NYfood.count({"cuisine": "Bakery"})
Bien entendu, les résultats seront différents car nous n’avons pas le même nombre de documents avant et après un filtrage de données. Dans le premier exemple, on souhaite afficher le nombre de documents dans la collection NYfood
tandis que dans les deux autres, on récupère le nombre de documents correspondant à des boulangeries (pour lesquels l’attribut "cuisine"
vaut "Bakery"
).
1.4.4. Trier les documents résultat : la méthode sort
¶
La méthode sort
sert à trier les documents de sortie à partir d’une ou plusieurs clés. Pour choisir l’ordre de tri, il suffit de spécifier la valeur 1
pour trier dans l’ordre croissant et -1
pour trier dans l’ordre décroissant.
Par exemple, pour trier les documents de sortie en fonction de la clé key1
de façon croissante on utilisera la syntaxe suivante :
db.collectionName.find({}).sort(
{"key1" : 1}
)
Il est également possible de faire un tri sur plusieurs clés :
db.collectionName.find({}).sort(
{"key1" : 1, "key2" : -1}
)
Ici, on fera un tri croissant sur la clé key1
et, en cas d’égalité, les résultats seront triés par valeur de key2
décroissante.
1.4.5. Limiter le nombre de documents retournés : la méthode limit
¶
La méthode limit
permet de limiter le nombre de documents renvoyés. Elle accepte les arguments numériques. Voici la syntaxe :
db.collectionName.find({...}).limit(2)
1.5. Pour conclure ce chapitre¶
1.5.1. Fiche résumé pour bien démarrer en MongoDB¶
Objectif |
Syntaxe |
---|---|
Récupérer toutes les occurrences de la collection |
|
Récupérer la première occurrence de la collection |
|
Filtrer les données |
|
Limiter l’affichage des clés |
|
Formater les documents de sortie |
|
Requêtes avec des opérateurs de comparaison |
|
Trier les documents de sortie |
|
Compter les documents de sortie |
|
Limiter les documents de sortie |
|
Valeurs distinctes d’un champ |
|
1.5.2. Quiz “Premières requêtes en MongoDB” : À vous de jouer !¶
Pour vous tester et être certain que vous avez bien compris, répondez aux questions du quiz ci-dessous.
Qu’est-ce qui caractérise MongoDB ?
C’est un modèle orienté graphique
C’est un modèle orienté document
C’est un modèle structuré
Cliquez pour montrer la réponse
Réponse 2 : C’est un modèle orienté document.
Que nous renvoie cette requête sur la collection notes
de la base etudiants
?
db.notes.find({}, {"nom": true, "_id": false})
Affiche tout le contenu de la collection
notes
Les noms des étudiants de la base et la clé
_id
qui identifie chaque étudiantTous les noms des étudiants de la base, mais pas les autres clés
Cliquez pour montrer la réponse
Réponse 3 : L’argument "_id": false
permet de retirer l’affichage de la clé id
. On ne récupère que le noms des étudiants de la base.
Comment récupérer la liste des étudiants ayant obtenu exactement deux notes ?
db.notes.find({"notes": {$size: 2}})
db.notes.find({"notes": {$exists: true}})
db.notes.find({$or: [{"notes": {$size: 1}},{"notes": {$size: 2}}]})
Cliquez pour montrer la réponse
Réponse 1 : L’opérateur $size
permet de prendre en compte la taille de la liste de valeurs, telle qu’une liste de notes. Ici on souhaite que la liste de notes soit précisement de taille 2.
Quel opérateur permet de ne renvoyer que les documents qui ne vérifient aucune condition de la liste ?
L’opérateur
$eq
L’opérateur
$nor
L’opérateur
$gt
Cliquez pour montrer la réponse
Réponse 2 : L’opérateur $nor
permet de ne renvoyer que les documents qui ne vérifient aucune condition de la liste.
Quelle méthode ci-dessous ne fait pas partie du langage MongoDB ?
db.collectionName.find({}).orderby({"key" : 1})
db.collectionName.find({}).sort({"key" : 1})
db.collectionName.find({}).limit(3)
Cliquez pour montrer la réponse
Réponse 1 : Bien que ORDER BY
soit une instruction en SQL, ce n’est pas disponible pour les bases de données en MongoDB.
Afin que le langage MongoDB n’ait plus aucun secret pour vous, nous vous invitons à lire les chapitres suivants et à consulter la documentation MongoDB !