Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. MongoDB
  3. Effectuer des requêtes dans MongoDB
Extrait - MongoDB Comprendre et optimiser l'exploitation de vos données (avec exercices et corrigés)
Extraits du livre
MongoDB Comprendre et optimiser l'exploitation de vos données (avec exercices et corrigés) Revenir à la page d'achat du livre

Effectuer des requêtes dans MongoDB

Chercher de l’information avec find et findOne

Les méthodes find et findOne ont exactement la même signature. Prenons celle de find :

db.collection.find(< requete >, < projection >) 

Les paramètres requête et projection sont tous deux des documents : le premier détaillera votre requête tandis que le second décrira les champs qui vont être affichés (ou projetés, ces termes sont interchangeables).

Pour lister l’ensemble des documents figurant dans notre collection personnes contenue dans la base de données test, nous avons déjà eu l’occasion d’utiliser la méthode find, sans toutefois lui passer d’argument. Celle-ci retourne en pratique ce que l’on appelle un curseur. Par défaut, un curseur est constitué de groupes de 20 documents. Lorsque vous faites une recherche renvoyant plus de 20 documents via le shell MongoDB, il vous faudra taper it (pour iterate) afin d’avoir accès aux 20 prochains résultats, jusqu’à ce que le curseur ait été totalement parcouru. Fort heureusement, il est possible de changer cette limitation pour la durée de la session ou bien à demeure. Si vous souhaitez par exemple que vos curseurs affichent des groupes de 40 documents, il vous suffira de taper à l’invite :

DBQuery.shellBatchSize = 40 

Le changement s’opérera...

Les opérateurs de comparaison

Nous allons maintenant effectuer des requêtes sur le seul champ de type entier présent dans nos documents : le champ age.

Si nous souhaitons afficher les personnes dont l’âge est d’exactement 76 ans, nous pouvons exécuter la requête suivante :

db.personnes.find({"age": 76}) 

Mais nous pouvons également utiliser l’opérateur d’égalité $eq pour demander une égalité stricte :

db.personnes.find({"age": {$eq: 76}}) 

Si la première forme est bien plus lisible, vous constatez toutefois que le résultat est rigoureusement le même ! La seule différence réside dans la façon d’écrire l’égalité. Nous avons dans le deuxième cas un document qui contient lui-même un document dont la clé est l’opérateur d’égalité $eq et dont la valeur est le nombre 76.

Si nous souhaitons obtenir le prénom des personnes âgées de 76 ans, nous allons faire usage de la partie projection de notre méthode de recherche find en lui listant les champs que nous souhaitons voir apparaître à l’écran. Notre document aura une clé nommée prenom et une valeur booléenne qui sera true.

db.personnes.find({"age": {$eq:...

Les opérateurs logiques

1. L’opérateur $and

C’est grâce à l’opérateur $and que ces deux documents vont pouvoir être joints. Sa syntaxe est :

{$and: [{critère1}, {critère2}, ...]} 

Si nous réécrivons notre condition de filtre, nous obtenons :

{$and: [{"age": {$exists: 1}}, {"age": {$nin: [70, 80]}}]} 

Nos critères se trouvent à présent énumérés dans un tableau auquel sera appliquée une succession de « ET » logiques. Nous pouvons désormais écrire la version finale de notre requête :

db.personnes.find({ 
 $and: [{ 
   "age": { 
     $exists: 1 
   } 
 }, { 
   "age": { 
     $nin: [70, 80] 
   } 
 }] 
}, { 
 "_id": 0, 
 "prenom": 1, 
 "nom": 1 
}) 

Dans le cas d’un simple ET comme celui que nous venons de réaliser, le mieux reste cependant de faire usage du ET implicite, en listant nos critères l’un derrière l’autre :

db.personnes.find({ 
 "age": { ...

Autres opérateurs

1. L’opérateur $expr

Apparu avec la version 3.6, $expr permet d’utiliser des expressions dans nos requêtes. Ces expressions peuvent notamment contenir des opérateurs, des objets ou des chemins pointant des champs.

Nous allons à présent afficher le prénom des personnes dont la longueur du nom (en nombre de caractères) multipliée par 12 est supérieure à l’âge. Notre find filtre d’abord sur la présence obligatoire des champs nom et age car, notre expression s’apprêtant à opérer dessus, nous voulons nous assurer qu’ils sont disponibles. Nous utilisons ensuite l’opérateur $strLenCP qui va travailler sur la valeur contenue dans le champ nom de chaque document où ce dernier est présent. Il va ensuite vérifier pour chaque document si le produit de la longueur du nom par 12 donne un nombre supérieur à la valeur contenue dans le champ age. Cette notation avec le symbole dollar ($) entouré de guillemets est ce que l’on appelle le field path, il pointe les valeurs contenues dans les champs du document en cours de traitement. C’est un peu l’équivalent du pointeur this suivi du nom d’une de ses données membres dans de nombreux langages de programmation orientée objet.

db.personnes.find({ 
 "nom": { $exists: 1 }, 
 "age": { $exists: 1 }, 
 $expr: { $gt: [ {$multiply: [{ $strLenCP: "$nom" }, 12]}, "$age"]} 
 }, 
 {"_id":0, "prenom":1} 
) 

Créons une nouvelle collection nommée banque afin d’utiliser $expr dans un nouveau contexte. Cette collection contient quatre documents ayant un champ numérique credit obligatoire et un champ debit optionnel. Voici la commande qui permet de la créer :

db.banque.insertMany([ 
   {"_id": 1, "credit": 2000, "debit":[1000, 22, 50]}, 
   {"_id": 2, "credit": 200, "debit":[50, 180]}, 
   {"_id": 3, "credit": 770, "debit":[1000]}, 
   {"_id": 4, "credit": 0} ...

Opérateurs de tableaux

Ajouter des valeurs dans un tableau à l’aide de $push

Cet opérateur nous permet d’ajouter (littéralement, de « pousser ») une ou plusieurs valeurs à l’intérieur d’un tableau. Sa syntaxe est très intuitive :

{ $push: { < champ >: < valeur >, ...} } 

L’opérateur $push possède un inverse, très logiquement nommé $pull qui possède exactement la même syntaxe.

Créons une collection hobbies pour nous exercer et insérons-y quelques documents :

db.hobbies.insertMany([ 
 {"_id": 1, "nom": "Yves"}, 
 {"_id": 2, "nom": "Sandra", "passions": []}, 
 {"_id": 3, "nom": "Line", "passions": ["Théâtre"]} 
]) 

Ajoutons une passion à Yves, qui est un randonneur invétéré :

db.hobbies.updateOne({"_id": 1}, {$push: {"passions": "Randonnée"}}) 

Le champ n’existait pas dans le document d’identifiant 1 ; il a donc été créé et son unique élément « Randonnée » y a été ajouté.

Line s’est découvert une passion pour la natation ; cependant, elle a déjà une autre passion, la mise à jour va donc consister à ajouter un nouvel élément à son tableau :

db.hobbies.updateOne({"_id": 3}, {$push: {"passions": "Natation"}}) 

Après deux essais en piscine, Line s’est aperçue qu’elle était allergique au chlore, la natation ne comptera finalement pas parmi ses passions, nous devons l’enlever :

db.hobbies.updateOne({"_id": 3}, {$pull: {"passions": "Natation"}}) 

Sandra, qui n’était pas quelqu’un de très passionné jusqu’alors, se prend subitement d’amour pour le dessin et la peinture ; nous allons donc ajouter deux éléments d’un coup et il nous faudra utiliser le modificateur $each pour y parvenir :...

Le tri

Le tri s’effectue avec la méthode sort qui s’applique sur un curseur, comme suit :

curseur.sort(< tri >) 

Le paramètre tri est un document qui décrit les tris à effectuer. Ils sont de deux types : croissants (notés 1) et décroissants (-1). Pour trier notre collection eleves par ordre croissant de nom, nous allons exécuter la requête suivante :

db.eleves.find({}, {"_id": 0, "nom": 1}).sort({"nom": 1}) 

Pour trier d’abord par ordre croissant de nom, puis par ordre décroissant de prénom, nous devrons légèrement modifier notre document de tri :

db.eleves.find({},{"_id":0,"nom": 1,"prenom": 1}).sort({"nom": 1,"prenom": -1}) 

Il est tout à fait possible de trier sur les valeurs contenues dans des documents. Pour notre collection eleves, nous pourrions ainsi demander un tri décroissant sur la première note obtenue dans le tableau notes :

db.eleves.find({},{"_id": 0, "nom": 1, "prenom": 1}).sort({"notes.0.note": -1}) 

Nous pouvons également utiliser la méthode limit conjointement à sort afin de limiter le nombre de résultats affichés. Son utilisation sera la même que lorsque nous l’avions...