• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Découvrez le fonctionnement de MongoDB

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Mise en place d’une base de données MongoDB

MongoDB est une base de données NoSQL orientée documents. Comme nous le verrons, l’ensemble du système tourne autour de cette gestion de documents, y compris le langage d’interrogation, ce qui en fait son point fort. Nous allons nous attaquer dès maintenant à la mise en place d’un serveur Mongo et comment intégrer vos données dans cet environnement.

 

Installation

Pour bien fonctionner, une base MongoDB a besoin de trois choses :

  • L’installation du serveur, que vous pouvez télécharger ici (version 3.4.7)

  • La création d’un répertoire pour stocker les données. Par exemple : C:\data\db (par défaut)

  • Le lancement du serveur, avec l’exécutable mongod (disponible sur $MONGO/bin)

Si tout se passe bien, le serveur devrait tourner comme dans le screenshot ci-dessous (ne pas fermer la fenêtre sous peine d’éteindre la base de données). Le serveur attend une connexion sur le port 27017.

Log de connexion du serveur mongod
Log de connexion du serveur mongod

Interface utilisateur

Maintenant que le serveur tourne, on peut s’attaquer à son administration avec une interface graphique. Nous prendrons Robo3T version 1.1 (anciennement robomongo) qui est largement utilisé.

Après lancement de l’application, cliquez sur “create” comme dans l'image ci-dessous pour créer une nouvelle connexion à MongoDB (avec “localhost” et le port “27017” par défaut).

Création d'une connexion sous Robo3T
Création d'une connexion sous Robo3T

Création d'une collection

Ca y est, nous pouvons manipuler la base de données et y ajouter une collection de documents. Une collection c’est l’équivalent d’une table pour une base de données relationnelle. Mais dans notre cas, une collection n’a pas de schéma donnant la structure d’une collection. Comme nous le verrons par la suite un document n’a pas de structure fixe, de fait, une base de données gère une ‘collection’ de documents, et non une table.

Créons tout d’abord une base de données ‘ma_bd’ (bouton droit sur la connexion “Create database”), puis sur les collections de cette base, créer une collection “test” (bouton droit sur “Collections(0)”)

Création d'une collection sous Robo3T
Création d'une collection sous Robo3T

Modélisation de documents

Documents JSON

Maintenant que l’environnement est prêt, une question se pose : à quoi ressemblent mes données ? À des documents JSON. Le modèle est très simple :

  1. Tout est clé/valeur : “clé” : “valeur”

  2. Un document est encapsulé dans des accolades {...}, pouvant contenir des listes de clés/valeurs

  3. Une valeur peut être un type scalaire (entier, nombre, texte, booléen, null), des listes de valeurs [...], ou des documents imbriqués

On peut donc insérer un document dans notre collection “test” (double click sur la collection, champ de texte en noir pour exécuter une requête) :

db.test.save (
{
  "cours" : "NoSQL",
  "chapitres" : ["familles", "CAP", "sharding", "choix"],
  "auteur" : {
     "nom" : "Travers",
     "prenom" : "Nicolas"
  }
} )

Relationnel vers JSON - comment faire ?

Cela semble simple, c’est à s’y méprendre ! En effet, les données utilisées de manière classiques sont en relationnel (Exportation en CSV pour faire simple). Mais voilà, nous devons faire face à un gros problème dans les bases NoSQL : Il faut proscrire les jointures !

De fait, si vous avez un SGBDR avec deux tables, vous ne pourrez pas faire de requêtes de jointure (ou alors, ça vous coûtera horriblement cher). Il faut dans ce cas trouver une solution pour fusionner les deux tables pour n’en produire qu’une seule en sortie. C’est ce qu’on appelle la dénormalisation.

Reprenons notre exemple précédent, mais avec un schéma un peu plus complexe (schéma de gauche). Une personne peut avoir plusieurs domaine d’expertise, des emplois successifs, et une habitation :

Schéma Entité/Association original
Schéma Entité/Association original

Si je dois intégrer une base de données avec une collection par entité (rectangle) et association (ovales), le nombre de jointures pour les requêtes sur la base NoSQL risque de faire exploser le système. Du coup, des fusions sont nécessaires pour réduire le coût des requêtes. Mais quelles tables doit-on imbriquer ? Dans quel sens le faire ?

Dénormalisation du schéma

Voici quelques étapes de modélisation qui vont vous permettre de produire des documents JSON qui répondront à votre demande, tout en minimisant les problèmes de jointures et d’incohérences :

  • Des données fréquemment interrogées conjointement.
    Par exemple, les requêtes demandent fréquemment le lieu d’habitation d’une personne. De fait, la jointure devient coûteuse. Accessoirement, cette information étant peu mise à jour, cela pose peu de problèmes. Résultat, l’entité ‘Habitation’ et l’association ‘habite’ sont intégrés à l’entité Personne. Habitation devient un document imbriqué à l’intérieur de Personne, représenté par : “{habite}”

  • Toutes les données d’une entité sont indépendantes.
    Prenons l’exemple des domaines d’expertise d’une personne, ils sont indépendants des domaines d’une autre personne. De fait, rapatrier les données de cette entité n’impacte aucune autre instance de Personne. Ainsi, la liste des domaines est importé dans Personne et représenté par : “[domaines]”

  • Une association avec des relations 1-n des deux côtés.
    Cette fois-ci, c’est plus délicat pour l’entité Etablissement. Une personne peut avoir plusieurs emplois et un employeur, plusieurs employés. De fait, une imbrication de l’employeur dans Personne peut avoir de gros impacts sur les mises à jour (tous les employés à mettre à jour !). Il est donc peu recommandé d’effectuer une fusion complète. Pour cela, seule l’association est imbriquée sous forme d’une liste de documents, intégrant les attributs (qualité et date), ainsi qu’une référence vers l’employeur. Ainsi : “[{emploie+ref}]”

  • Même taux de mises à jour.
    Dans le cas des emplois d’une personne, là également nous pourrions effectuer une fusion de l’association “emploie”. En effet, le taux de mises à jour des emplois est équivalent à celui de la Personne, de fait, sans incidence sur les problèmes de cohérence de données.

Au vu de ces critères, voici le schéma qui pourrait être obtenu par fusion successives :

Schéma dénormalisé
Schéma dénormalisé

Les listes sont représentées par des crochets et les imbrications par des accolades. Nous pourrons remarquer que les établissements sont stockés dans une association à part, mais que la référence est gardée dans une liste intégrée à l’entité "Personne". Voici un exemple d’instance de cette représentation. Vous pouvez remarquer que les établissements sont référencés et des informations peuvent ne pas être renseignées.

{
  "_id" : 1,    "nom" : "Travers",   "prenom" : "Nicolas",
  "domaines" : ["SGBD", "NoSQL", "RI", "XML"],
  "emplois" : [
    {"id_etablissement" : "100", "qualité" : "Maître de Conférences",
        "date" : "01/09/2007"},
    {"id_etablissement" : "101", "qualité" : "Vacataire",
        "date" : "01/09/2012"}
  ],
  "Habite" : {"adresse" : "292 rue Saint Martin", "ville" : "Paris"}
}

Maintenant, nous sommes prêts pour concevoir des bases NoSQL orientées documents avec des fusions. Ne reste plus qu’à faire la conversion de données relationnelles vers ces documents. Pour cela, il est possible de créer un ETL (Extract-Transform-Load) avec votre langage de programmation préféré qui va, à la volée, chercher les informations nécessaires pour produire des données correspondant au schéma. Une seconde possibilité est d’utiliser l’outil OpenRefine (anciennement GoogleRefine) pour nettoyer et définir un format de sortie JSON. La dernière possibilité est d’intégrer chaque fichier dans MongoDB (chacun une collection distincte), puis d’utiliser les opérateurs $lookup et $redact pour produire la sortie souhaitée (les opérateurs MongoDB seront étudiés dans le chapitre suivant).

Importer les données

Nous allons maintenant importer un jeu de données déjà formaté à notre base de données. Un jeu de données OpenData est disponible sur des restaurants de New York produits par la mairie sur les résultats des inspections. Pour l’importation :

  1. Téléchargez l’archive suivante : restaurants.zip

  2. Décompresser l’archive (j’appellerai le répertoire utilisé $RESTAURANT)

  3. Nous allons créer une base de données “new_york” (paramètre --db) et une collection “restaurants” (paramètre --collection). Attention, il ne faut pas de majuscules !

  4. Dans une console (Windows : invite de commande, Linux : Shell/Konsole), aller dans le répertoire $MONGO/bin

  5. Exécutez la commande suivante :

$MONGO/bin/mongoimport --db new_york --collection restaurants $RESTAURANT/restaurants.json

Ca y est, vous avez une base de données de 25 357 restaurants que vous pouvez consulter directement avec Robo3T.

Screenshot de la collection
Screenshot de la collection "restaurants" sous Robo3T

Nous allons maintenant pouvoir interroger cette collection avec le langage proposé par MongoDB.

Exemple de certificat de réussite
Exemple de certificat de réussite