MongoDB tout en douceur

MongoDB est une base de données appartenant à la catégorie des bases NoSQL.

Il existe suffisamment d’articles sur cette mouvance pour que je n’y revienne pas.

MongoDB est orientée documents. Cela signifie qu’une clé donnée permet d’avoir accès à un document structuré, dont les données peuvent être stockées de manière hiérarchique (dans le cas de MongoDB, avec JSON).

Il est possible de la requêter soit via un shell (avec des commandes en JavaScript) soit via un langage de programmation à travers un des multiples drivers mis à disposition par 10Gen, la société éditrice de la base (Java, Scala, Python, .Net, PHP, etc.).

Lors de la rédaction de cet article, j’ai utilisé la version 2.4.5 de MongoDB.

Installation

L’installation se fait très simplement :

  • On commence par télécharger l’archive qui doit être décompressée sur le disque, dans le répertoire de votre choix. Les différentes versions de la base se trouvent à cette URL : http://www.mongodb.org/downloads
  • On peut ensuite ajouter le répertoire « bin » de la base dans le path, de manière à pouvoir utiliser les commandes depuis n’importe quel emplacement. Les captures d’écran suivantes concernent Windows mais il existe des équivalences sous Mac OS ou sous Linux.

Création de la variable système

Mise à jour du path

  • On choisit ensuite le répertoire de stockage des données.

Répertoire de stockage des données

 

  • Enfin, on lance la base à l’aide de la commande « mongod ». L’option dbpath permet de spécifier le répertoire de stockage des données. On peut aussi bien lui donner une valeur relative qu’absolue.

La console au démarrage

C’est fait, vous avez une base Mongo qui tourne sur le port 27017 !

De nombreuses options sont possibles lors du lancement de la base, comme le choix du port ou du répertoire de stockage des données.

Organisation des données

Il est possible de déclarer plusieurs bases, un peu comme avec un SGBD classique. La principale différence réside dans le processus de création de ces bases : il suffit, dans le shell, de dire que l’on souhaite utiliser la base « exercice » pour qu’aussitôt, elle soit créée.

Viennent ensuite les collections. Elles pourraient s’apparenter aux tables dans une base SQL, mais avec une différence de poids : la structure des données stockées dans une collection n’est pas imposée. Il est tout à fait possible de mélanger des fiches d’identité avec des bons de commande ou des poires avec des concombres.

En tout cas, c’est en théorie comme ça que cela fonctionne.

Dans la pratique, il est fortement conseillé d’avoir dans une collection donnée des documents structurellement proches car sinon, l’utilisation des indexes ou des clés de sharding pourrait devenir problématique.

Les indexes sont portés par les collections et ont la même fonction que dans une base traditionnelle : indexation et tri des données.

Les documents sont stockés dans la base au format BSON (Binary JSON). En version 2.4, MongoDB n’accepte pas les documents de plus de 16 Mo, ce qui vues les capacités de stockage actuelles peut sembler bien peu. Cette limitation est en fait là pour se protéger d’éventuels défauts de conception.

Les requêtes sont formulées au format JSON, de même que leurs résultats.

Le shell

Le shell est accessible en tapant la commande « mongo » dans une invite de commande.

Il existe une aide contextuelle dans Mongo, utilisable en tapant le mot clé « help ».

Par exemple, taper « help() » dans le shell permet d’avoir la liste des commandes générales.

Taper « db.help() » permet d’avoir la liste des commandes spécifique à la base en cours.

Taper « db.maCollection.help() » donne la liste des commandes que l’on peut passer au niveau d’une collection.

La première étape est de choisir avec quelle base de données on souhaite travailler. Ce choix se fait en utilisant la commande « use nom_base« .

La liste des bases est accessible en tapant « show dbs« .

Dans une base donnée, la liste des collection est accessible en tapant « show collections« .

Une fois la base sélectionnée, il est possible de faire appel à des méthodes qui permettent de :

  • Faire une requête de type CRUD.
  • Analyser le plan d’exécution d’une requête.
  • Créer un index.
  • Faire appel au framework d’agrégation (que nous aborderons un peu plus tard).
  • Répliquer une base.

Les paramètres passés à ces méthodes sont des documents JSON.

Le lancement d’une méthode se fait toujours de la même manière :

db.collection.methode()

db est une constante qui vous fait pointer sur la base courante.

collection est le nom de la collection dans laquelle on souhaite agir.

methode() est le nom de la méthode à exécuter avec ou sans paramètres.

Insertion d’un document en base

Pour insérer un document en base, il faut spécifier dans quelle collection doit se faire l’insertion et utiliser la méthode « insert » en lui passant en paramètre le document à insérer, toujours au format JSON.

db.personne.insert({prenom : « Mourad » , nom : « Boudjellal »})

La commande « insert » s’occupe de créer un document.

La collection n’a pas forcément besoin d’exister préalablement à l’insertion du document. Si elle n’existe pas, elle est créée juste avant l’insertion du document.

Une fois l’insertion effectuée, le document est consultable :

{
« _id » : ObjectId(« 51e27a5582ebb7c7ef073b9d »),
« nom » : « Boudjellal »,
« prenom » : « Mourad »
}

On peut voir qu’il y a un champ de plus que ceux passés à la commande d’insertion. Il s’agit de l’identifiant unique, toujours appelé « _id ». Si la requête de création en contient un, il est utilisé (avec un contrôle de doublons), sinon, il est généré.

Recherche d’un document dans une collection

Pour faire une requête de consultation, il y a deux possibilités find() et findOne().

findOne() permet de ne renvoyer qu’un seul document, en fonction des critères passés à la méthode.

Find(), de son côté, renvoie une collection de documents, toujours en fonction des critères passés à la méthode.

Par exemple, pour retrouver le document inséré plus tôt, la commande est :

db.personne.findOne()

Comme la collection ne contient pour le moment qu’un seul document, le retour est :

{
« _id » : ObjectId(« 51e27a5582ebb7c7ef073b9d »),
« nom » : « Boudjellal »,
« prenom » : « Mourad »
}

La méthode findOne peut également accepter des paramètres, exprimés sous la forme de documents.

Le premier contient les critères de recherches dans le cas d’une collection contenant plusieurs documents.

Le second indique les champs que l’on souhaite afficher dans le résultat de la requête.

Par exemple :

db.personne.findOne({prenom: »Mourad »}, {« nom »:true})

Le retour est :

{ « _id » : ObjectId(« 51e27a5582ebb7c7ef073b9d »), « nom » : « Boudjellal » }

On peut voir que malgré la sélection des champs qui a été faite, l’identifiant s’affiche. En fait, si on ne dit pas clairement que l’on ne souhaite pas l’afficher, il est toujours présent. Pour n’avoir que le nom, il faut exécuter la commande suivante :

db.personne.findOne({prenom: »Mourad »}, {« nom »:true, « _id »:false})

La méthode find, qui accepte les mêmes paramètres que findOne, permet d’avoir accès à un ensemble de documents répondant aux critères choisis.

Sans paramètres, find renvoie l’intégralité de la collection.

Par exemple, après avoir un peu étoffé la collection « personne », lancer un db.personne.find() renvoie :

{ « _id » : ObjectId(« 51e27a5582ebb7c7ef073b9d »), « nom » : « Boudjellal », « prenom » : « Mourad » }

{ « _id » : ObjectId(« 51e27e5082ebb7c7ef073b9e »), « nom » : « Arleston », « prenom » : « Christophe » }

{ « _id » : ObjectId(« 51e27e9682ebb7c7ef073b9f »), « nom » : « Dorison », « prenom » : « Xavier » }

On peut voir que la présentation en ligne n’est pas forcément des plus lisibles. Il est possible de rendre le retour d’un find plus lisible grâce à la méthode pretty().

Ainsi db.personne.find().pretty() renvoie :

{
« _id » : ObjectId(« 51e27a5582ebb7c7ef073b9d »),
« nom » : « Boudjellal »,
« prenom » : « Mourad »
}
{
« _id » : ObjectId(« 51e27e5082ebb7c7ef073b9e »),
« nom » : « Arleston »,
« prenom » : « Christophe »
}
{
« _id » : ObjectId(« 51e27e9682ebb7c7ef073b9f »),
« nom » : « Dorison »,
« prenom » : « Xavier »
}

Si la collection sur laquelle le find est lancé contient plusieurs dizaines de documents, la console ne va pas tous les afficher en une fois.

Un curseur est ouvert sur le serveur (pour une durée de 10 minutes par défaut). Une portion des documents est affichée et une invite de commande propose de saisir « it » si on veut voir la suite de la collection. Cela permet d’éviter ce qui arrive fréquemment en SQL : un select qui renvoie 5000 lignes et un résultat qui défile pendant plusieurs dizaines de secondes.

Bien entendu, les critères de recherche ne se limitent pas à l’égalité. On peut aussi utiliser des opérateurs tels que plus grand, plus petit ou différent.

Dans le document de critère, cela se traduit par un sous-document.

Par exemple, pour afficher toutes les personnes dont le nom de famille vient après B, on peut passer la requête suivante :

db.personne.find({nom:{$gt: »B »}})

Le résultat de cette requête est :

{ « _id » : ObjectId(« 51e27a5582ebb7c7ef073b9d »), « nom » : « Boudjellal », « prenom » : « Mourad » }

{ « _id » : ObjectId(« 51e27e9682ebb7c7ef073b9f »), « nom » : « Dorison », « prenom » : « Xavier » }

Les opérateurs sont :

– $gt : plus grand

– $gte : plus grand ou égal

– $lt : plus petit

– $lte : plus petit ou égal

– $ne : différent

En plus de ces opérateurs d’égalité et d’inégalité, il existe d’autres mots clés.

Petit aparté, on peut voir que les mots-clés sont tout préfixés par le caractère $.

Comme MongoDB est une base sans schéma de données, il est possible d’avoir des documents de structures différentes dans une même collection. Par exemple, on peut ajouter :

db.personne.insert({nom: »Goscinny », prenom: »Rene », profession: »Scenariste »})

Et ensuite, passer des requêtes telles que :

db.personne.find({« profession »:{$exists:true}})

De cette manière il est possible de n’avoir que les documents avec ou sans un attribut donné.

Il est également possible, pour une requête donnée, de savoir combien de documents sont renvoyés en utilisant la méthode count.

db.personne.find({« profession »:{$exists:true}}).count()

On peut aussi compter le nombre de documents dans une collection :

db.personne. count()

Il est enfin possible d’obtenir des statistiques sur une collection :

db.personne.stats()

qui va nous renvoyer le résultat suivant :

{
« ns » : « local.startup_log »,
« count » : 3,
« size » : 2172,
« avgObjSize » : 724,
« storageSize » : 10485760,
« numExtents » : 1,
« nindexes » : 0,
« lastExtentSize » : 10485760,
« paddingFactor » : 1,
« systemFlags » : 0,
« userFlags » : 0,
« totalIndexSize » : 0,
« indexSizes » : {},
« capped » : true,
« max » : NumberLong(« 9223372036854775807 »),
« ok » : 1
}

Dans le prochain article, on abordera la mise à jour et la suppression des données, les créations d’index.

A propos Benjamin

Développeur Java, évangéliste du logiciel libre auprès de mes proches, j'essaie de concilier tout ça avec la curiosité qui m'a toujours fait avancer.
Ce contenu a été publié dans Développement, MongoDB. Vous pouvez le mettre en favoris avec ce permalien.

2 réponses à MongoDB tout en douceur

  1. Pierre dit :

    Bravo très bonne approche pour faire comprendre le fonctionnement pour un débutant intéressé par le Mongo DB comme moi 🙂

  2. hassan dit :

    Merci pour ce tuto nous attendons plus.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.