Cours 8 (2014) : Premiers pas avec mongodb

logo mongodb

Précision : ce billet ne parle pas de l’installation de mongo. Il suppose que vous utilisez l’installation sur les machines de l’université (voir TP correspondant), ou une installation que vous aurez faite par vos propres moyens.

Un système de gestion de base de données NoSQL

Qu’est-ce qu’un système de gestion de base de données (SGBD) ?

Une base de données sert à stocker des informations, les consulter et les mettre à jour. Dans un programme sur un serveur web, ces informations seront stockées sur le serveur.

On parle de SGBD (système de gestion de base de données). Un fichier texte, dans lequel des informations sont enregistrées ligne après lignes, constitue un SGBD, même rudimentaire. L’intérêt d’un SGBD élaboré (et donc qui nécessite l’utilisation de programmes/modules spéficiques) est de garantir des propriétés comme par exemple la performance des opérations, la cohérence, la consistance des données enregistrées, etc.

NoSQL

Le type de SGBD le plus courant utilise un langage de requêtes de la famille SQL (Structured Query Language). Mongo n’en fait pas partie.

Mongo s’inscrit dans un mouvement qui gagne en ampleur depuis plusieurs années, dit NoSQL (pour Not only SQL), d’utilisation de SGBD moins contraignants que ceux de la famille SQL.

Les avantages souvent mis en avant :

  • plus souples
  • plus faciles d’utilisation
  • conçus pour être utilisés sur des architectures massivement parallèles, sur des très grandes quantités de données

Inconvénient majeur :

  • moins de garanties de cohérence et de consistance des données. Ces contraintes sont laissées à la charge du programmeur.

Pour un site web, les opérations de base données suivent à peu près toujours le même schéma. Il n’est souvent pas utile de s’embarrasser de SQL, dont la syntaxe et les concepts sous-jacents sont très différents de ce qu’on manipule par ailleurs (dans la programmation en langage serveur et en javascript).

Principes de base de Mongo

Une installation de mongo peut contenir plusieurs bases de données (sur les machines de l’université, vous n’aurez qu’une seule base, qui porte le même nom que votre login).
Une entrée dans une base mongo est appelée un document. On stocke des documents dans des collections. Une base contient donc des collections de documents.

Les données comme clefs / valeurs

Un document peut être vu comme un ensemble de couples clef / valeur.

Exemples de documents très simples :

 { 'prenom': 'Christophe', 'nom': 'Prieur' }

 { 'prenom': 'Toto' }

 {
   'code': 'IO2',
   'intitulé': 'Internet et outils',
 }

On utilise ici la syntaxe JSON, qui est utilisée par l’interpréteur mongo (voir ci-dessous)

Rien n’oblige les documents d’une collection à posséder les mêmes champs, ni à ce que les champs de même nom aient des valeurs de même type (la cohérence de la base est laissée à la charge du programmeur). Une collection n’est qu’un ensemble de documents, sans contrainte pour ceux-ci.

Les valeurs peuvent être complexes (listes, dictionnaires), mais nous nous limiterons pour le moment à un exemple :

 {
   'code': 'IO2',
   'intitulé': 'Internet et outils',
   'semestre': 2,
   'outils': ['html', 'css', 'php', 'mongo', 'javascript']
 }

Identifiants

Chaque document est pourvu d’un identifiant supposé être unique (et si on utilise le système d’affection d’identifiant par défaut, il sera bien unique). C’est la clef _id (noter l’underscore). Elle n’est pas affichée dans les exemples ci-dessus.

Les opérations comme clefs / valeurs

Toutes les opérations sont effectuées au moyen de commandes prenant en paramètre des tableaux clef / valeur.

  • Les documents sont naturellement fournis comme des tableaux clef / valeur
  • Les requêtes sont fournies sous la forme de tableaux clef / valeur
  • Les options des commandes sont spécifiées sous la forme de tableaux clef / valeur

La syntaxe variera donc selon le langage utilisé :

  • dans l’interpréteur mongo (qui utilise javascript), on utilisera JSON (Javascript Object Notation)
  • en php, on utilisera la syntaxe php de description de tableaux associatifs (clef / valeur)

Mais les opérations seront les mêmes : l’interpréteur mongo et l’objet MongoClient de php sont deux implémentations différentes d’une même interface de programmation. Détails ci-dessous, d’abord pour l’interpréteur (donc en javascript), ensuite pour php.

Cinq commandes de base (insert, find, findOne, update, remove)

Référence : opérations sur les collections.

Avant tout, l’interpréteur

Dans cette section, les commandes sont décrites avec la syntaxe de l’interpréteur mongo (donc javascript).
La version php sera fournie dans la section suivante.

Pour lancer l’interpréteur sur les machines de l’université, utilisez la commande ci-dessous :

mongo --host pams -p -u login base
  • pams est le nom du serveur
  • -p indique que la connexion à la base requiert un mot de passe (qui vous sera demandé interactivement)
  • -u permet de fournir votre login
  • base est le nom de votre base, c’est en fait également login

Sur une installation en local, sur votre ordinateur par exemple, où aucune restriction n’est imposée (c’est le cas dans l’installation par défaut), la commande mongo sans arguments suffit.

mongo

Contrairement à l’interpréteur du shell (bash), l’interpréteur mongo n’exécute pas des commandes à proprement parler mais évalue des expressions et affiche leur valeur (et lorsque ces expressions comportent des appels de méthodes, cela revient à exécuter des commandes, même si on n’appelle pas ça comme ça). Ce qui est très utile pour obtenir le résultat de certaines opérations (et quand on a un doute sur la syntaxe).

      > 1+2
      3
      > 'bon'+'jour'
      bonjour
      > var n=10
      > n+3
      13

L’interpréteur mongo fournit une auto-complétion des noms de méthodes au moyen de la touche Tab (une fois s’il n’y a pas ambiguïté, deux fois pour afficher la liste des suites possibles).

Il est possible d’exécuter des commandes écrites dans un fichier au moyen de la fonction load :

load('test.js')

insert

La référence : insert

Pour insérer un document dans une collection (que la collection existe ou non) :

db.collection.insert( document )
db.collection.insert( documents )
  • document est un tableau clefs / valeurs
  • documents est une liste de tableaux clefs / valeurs
  • collection est le nom de la collection à laquelle on souhaite ajouter le(s) document(s). Si la collection n’existait pas au préalable, elle est créée (c’est de cette manière qu’on crée les collections)

db.etudiants.insert({
    'prenom': 'Camille',
    'nom': 'Simon'
})

db.etudiants.insert( { 'prenom': 'Thomas' } )

db.etudiants.insert( [ { 'prenom': 'Jordan' }, { 'prenom': 'Mélanie'} ] )

db.etudiants.insert(
  [
    {
      'prenom': 'Camille',
      'nom': 'Alberti'
    },
    {
      'prenom': 'Laura',
      'nom': 'Seban'
    }
  ]
)

db.cours.insert([
    { 'code': 'IO2', 'intitulé': 'Internet et outils' },
    { 'code': 'TO2', 'intitulé': 'Types de données et objets' }
])

Deux remarques de syntaxe :

  • les espaces et sauts de ligne n’importent pas
  • attention à fermer tout ce qui est ouvert, dans le bon ordre (parenthèses, crochets, parenthèses)

find

La référence : find

Pour consulter des documents dans une collection :

db.collection.find()
db.collection.find(requete)
db.collection.find(requete, projection)
  • sans paramètre, tous les documents sont renvoyés
  • requete est un tableau clefs / valeurs spécifiant des opérateurs sur les champs des documents recherchés
  • projection est un tableau permettant de limiter les champs que l’on souhaite consulter dans les documents recherchés (cette option sera traitée ultérieurement)

db.etudiants.find()
db.etudiants.find( { ‘prenom’: ‘Camille’ } )

Exemple de résultat obtenu :

> db.etudiants.find( { 'prenom': 'Camille' } )
{ "_id" : ObjectId("532d40c72d150b4635b8cfc9"), "prenom" : "Camille", "nom" : "Simon" }
{ "_id" : ObjectId("532d40c72d150b4635b8cfcd"), "prenom" : "Camille", "nom" : "Alberti" }

Le champ _id a été ajouté par le système au moment de l’opération insert. On peut ensuite l’utiliser pour désigner un document précis :

> db.etudiants.find({ _id: ObjectId("532d40c72d150b4635b8cfc9") })
{ "_id" : ObjectId("532d40c72d150b4635b8cfc9"), "prenom" : "Camille", "nom" : "Simon" }

Le résultat renvoyé par find est un curseur, un objet qui permet d’itérer sur les documents. Voir plus bas l’utilisation de ce curseur avec php. La version Javascript de cette itération n’est pas présentée ici, considérons juste un exemple d’utilisation simple, comme un tableau :

> var c = db.etudiants.find({'prenom': 'Camille'})
> c[0]
{
    "_id" : ObjectId("532d40c72d150b4635b8cfc9"),
    "prenom" : "Camille",
    "nom" : "Simon"
}
> c[0].nom
Simon

findOne

La référence : findOne.

La fonction findOne fait la même chose que find mais sans s’embarrasser d’un curseur, lorsqu’on souhaite récupérer un document unique (par son identifiant, par exemple).

> var camille = db.etudiants.findOne({'_id': ObjectId('532d40c72d150b4635b8cfc9')})
> camille
{
    "_id" : ObjectId("532d40c72d150b4635b8cfc9"),
    "prenom" : "Camille",
    "nom" : "Simon"
}
> camille.nom
Simon

Si la requête désigne plusieurs documents, le premier trouvé est renvoyé.

update

La référence : update.

Pour modifier un ou des documents existant :

db.collection.update(requete, modifications)
  • requete est de la même forme que pour find
  • modification est un tableau clefs / valeurs définissant des opérations sur les champs. On n’utilisera pour le moment que l’opérateur $set pour ajouter un champ, comme dans l’exemple ci-dessous.

db.users.update({'prenom': 'Thomas'}, { '$set': {nom: 'Hummel'} })

remove

La référence : remove.

Pour supprimer un ou plusieurs documents :

db.collection.remove()
db.collection.remove(requete)
  • sans paramètre, tous les documents sont supprimés (attention, donc)
  • requete est de la même forme que pour find, elle désigne les documents qui seront supprimés.

> db.etudiants.find( { 'prenom': 'Camille' } )
{ "_id" : ObjectId("532d40c72d150b4635b8cfc9"), "prenom" : "Camille", "nom" : "Simon" }
{ "_id" : ObjectId("532d40c72d150b4635b8cfcd"), "prenom" : "Camille", "nom" : "Alberti" }
> db.etudiants.remove({ _id: ObjectId("532d40c72d150b4635b8cfc9") })
> db.etudiants.find({prenom: 'Camille'})
{ "_id" : ObjectId("532d40c72d150b4635b8cfcd"), "prenom" : "Camille", "nom" : "Alberti" }

Fonctionnement avec php

L’objet MongoClient

La référence : MongoClient

Connexion à la base

On utilise la syntaxe suivante :

new MongoClient("mongodb://username:password@server/database");

On peut par exemple définir les paramètres de la connexion dans un fichier séparé, ce qui permet d’écrire des scripts indépendants de ces paramètres. C’est aussi particulièrement important pour le stockage du mot de passe.

require_once('parametres-base.php');

$url = 'mongodb://'.DBUSER.':'.DBPASSWD.'@'.DBSERVER.'/'.DBNAME;
$m = new MongoClient($url);

$db = $m->{DBNAME};

Note sur la syntaxe : dans l’expression $m->{DBNAME}, les accolades permettent de remplacer DBNAME par sa valeur, et ainsi récupérer l’attribut qui porte ce nom dans l’objet stocké dans $m.

Un exemple de fichier parametres-base.php :

<?php
define('DBSERVER', 'pams');
define('DBUSER', 'cprieur');
define('DBNAME', 'cprieur');
require_once('18aa28061571fa8cced19dc62e7a2fbc/p.php')
?>

On a utilisé la fonction php define, qui permet en php de définir des constantes.

Dans cet exemple, le mot de passe est stocké dans un fichier p.php, rangé dans un répertoire dont le nom ne peut être trouvé par hasard, par une personne mal intentionnée.

Le fichier p.php pourra contenir ce qui suit :

<?php
define('DBPASSWD', 'mon mot de passe');
?>

Attention, votre mot de passe doit être stocké dans un fichier et un répertoire qui sont accessibles au serveur web mais pas aux autres étudiants. Les étudiants sont dans votre groupe, le serveur web non, ce qui donne les droits suivants :

# pour le répertoire
chmod 701 rep

# pour le fichier
chmod 604 p.php

# ou bien
chmod g-rwx,o+x rep
chmod g-rwx,o+r p.php

Exemple d’ajout d’un document

Le fonctionnement est le même qu’avec l’interpréteur, mais en utilisant la syntaxe php :

$etudiant = array('prenom' => 'Camille', 'nom' => 'Simon' );
$db->etudiants->insert($etudiant);

Références :

Récupérer l’identifiant lors de l’insertion

Après un appel à insert, un champ _id est ajouté au tableau qui a été passé en paramètre. Dans l’exemple ci-dessus, on peut récupérer l’identifiant par :

$id = $etudiant['_id'];

Parcourir les résultats de find

$cursor = $db->etudiants->find();

L’objet renvoyé permet d’itérer sur les documents trouvés, indexés par leur identifiant :

foreach($cursor as $id => $document) {
    traite($id, $document);
}

Dans le corps de la boucle, $id contient l’identifiant du document (le champ _id), $document est un tableau associatif clefs / valeurs comprenant tous les champs du document renvoyés par find (y compris l’identifiant lui-même).

traite($id, $document)
{
  echo '<dt>nom</dt><dd>'.$document['nom'].'</dd>';
  echo '<dt>prenom</dt><dd>'.$document['prenom'].'</dd>';
}

Comme avec l’interpréteur, on peut définir une requête :

$requete = array( 'prenom' => 'Camille' );
$cursor = $db->etudiants->find($requete);

Références :

Recherche d’un document par son identifiant

$requete = array('_id' => new MongoId('532d40c72d150b4635b8cfc9'));
$etudiant = $db->etudiants->findOne($requete);

Références

Généralités

Mongo

php

Cette entrée a été publiée dans Cours, avec comme mot(s)-clef(s) , , , , . Vous pouvez la mettre en favoris avec ce permalien.

3 réponses à Cours 8 (2014) : Premiers pas avec mongodb

  1. Gus Humon dit :

    Trois admins de base de données entrent dans un bar NoSQL…
    Ils en ressortent un peu plus tard, sans avoir pu trouver de table.

  2. Jack dit :

    Excellent instruction. Moi, j’ai fait mes premiers pas avec MongoDB en suivant un tutoriel vidéo sur http://www.alphorm.com/tutoriel/formation-en-ligne-mongodb-administration. En tout cas, votre article est très réussi.

Laisser un commentaire

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

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Merci de répondre à cette question pour prouver que vous n'êtes pas un robot :

Combien font 3 multiplié par 4 ?