Vous êtes sur la page 1sur 16

Relationnel et contrôle de l'intégrité des données

À la naissance de l'informatique, plusieurs modèles de stockage de l'information sont


explorés, comme les modèles hiérarchiques ou réseau.
Mais le modèle relationnel permet de mieux assurer le contrôle de l'intégrité des données,
grâce à un modèle théorique puissant et simple.
On notera en particulier :
 Le schéma : on peut exprimer des règles de cohérence a priori et déléguer leur contrôle au système
 La normalisation : on peut supprimer la redondance par un mécanisme de décomposition et
retrouver l'information consolidée par les jointures
 La transaction : le système assure le maintien d'états cohérents au sein d'environnements
concurrents et susceptibles de pannes

Relationnel et performance en contexte transactionnel


La représentation relationnelle se fonde sur la décomposition de l'information ce qui minimise les
entrées/sorties (accès disques, transfert réseau) et permet d'être très performant pour répondre à des
questions et des mises à jour ciblées. C'est donc une bonne solution dans un contexte transactionnel qui
comprend de nombreux accès ciblés à la base.

En revanche ce n'est plus une bonne solution pour des accès globaux à la base (puisqu'il faut alors effectuer
beaucoup de jointures pour reconsolider l'ensemble de l'information).

Identification
Object Identifiers (OID)
Universally Unique IDentifier (UUID)
Uniform Resource Name (URN)

Imbrication
Structure des valeurs stockées connue par le serveur (RO, JSON, XML, structure interne de type colonne...).

Hachage et distribution
 Logique de dépôt uniquement (stocker et retrouver) ;
 c'est la couche applicative qui fait tout le travail (traitement, cohérence...).

Schema-less
Les bases NoSQL se fondent sur une approche dite schema-less, c'est à dire sans schéma logique défini à
priori.

L'équivalent du CREATE TABLE en SQL n'est pas nécessaire, même pas possible ; on peut directement
faire l'équivalent de INSERT INTO.

Cela apporte de la souplesse et de la rapidité, mais se paye avec moins de contrôle et donc de cohérence
des données.

Le mouvement NoSQL tend à réintégrer des fonctions de schématisation a priori, à l'instar de ce qui se fait
en XML : le schéma est optionnel, mais conseillé en contexte de contrôle de cohérence.

1
Mongo DB
Nous allons consacrer ce chapitre à une base dite “documentaire” qui représente les données
au format JSON. Il s’agit de MongoDB, un des systèmes NoSQL les plus populaires du
moment.

MongoDB n’impose pas de schéma, ce qui peut être vu comme un avantage, mais peut
devenir rapidement pénalisant puisque la charge du contrôle des données est reportée du côté
de l’application.
MongoDB propose un langage d’interrogation qui lui est propre (donc, non standardisé),
pratique mais limité.

Les données utilisées en exemple ici sont celles de notre base de films, que vous pouvez récupérer sur le site du
Webscope, http://webscope.bdpedia.fr/index.php?ctrl=xml. Choisissez bien entendu les exports en JSON. Si
vous disposez de documents JSON plus proches de vos intérêts, vous êtes bien entendu invités à les prendre
comme base d’essai.

Installation de MongoDB

Les Schéma
La première étape de la création d'une base de données relationnelle est de définir son schéma
c'est-à-dire l'ensemble des tables la composant et l'ensemble des champs de ces tables.

Cette étape crée une certaine rigidité dans l'implémentation, implique d'avoir une vision assez
claire des évolutions de l'application et peut poser problème si la structure des données
recueillies change dans le temps.

Les systèmes NoSQL sans-schéma peuvent ignorer cette étape et stocker des données
hétérogènes au fur et à mesure de leur alimentation. Cette utilisation permet une grande
flexibilité et des capacités d'adaptation au niveau de la base de données.

La contrepartie est que les applications qui liront la base de données devront être capables
d'intégrer des données plus hétérogènes et de structure plus complexe.

Marché
Les SGBD relationnels sont largement répandus dans les entreprises. Dimensionnés pour une
quantité d'informations et un nombre d'utilisateurs typiques d'une entreprise, ils ont pour fonction
principale le traitement de transactions.
Ils montrent cependant leurs limites lorsqu'ils sont utilisés dans un périmètre plus large, tel qu'un
site web populaire, fréquenté par des millions de visiteurs dans le monde entier.
Les SGBD relationnels exigeraient alors des logiciels et des ordinateurs coûteux ainsi que des
compétences en optimisation peu répandues.
Ce segment de marché est de ce fait occupé par les logiciels NoSQL, conçus spécifiquement
pour un usage de type Internet (beaucoup de lecture). Ces produits abandonnent la
représentation matricielle de l'information et le langage de commande SQL en échange d'une
simplicité, d'une performance et surtout d'une scalabilité accrues.
Simple à mettre en œuvre, le stockage d'information à l'aide de tableaux associatifs (clé/valeur)
existe depuis le début de l'histoire des bases de données, en 1970.

2
Des langages comme Perl et PHP les ont rendus familiers aux programmeurs. Les nouvelles
demandes en rapport avec les sites web de grande audience apparus dans les années 2000 et la
facilité de mise en œuvre des tableaux associatifs ont fait émerger ces solutions.
Elles ont comme point commun l'abandon du langage SQL et sont donc nommées NoSQL.

3
Exemple : mongo DB
MongoDB est un engin de base de données dans la famille des engins NoSQL. Dans ce
contexte, il n’y a pas de table ou de schéma de structure de données. Les données sont
enregistrées dans des structures nommées Documents.

MongoBD est une base de données open source NoSQL orientée document. Elle stocke des
données au format JSON (en fait BSON, qui est une version binaire de JSON).
Le serveur MongoDB est organisé en plusieurs databases :
 Chaque database contient des collections.
 Chaque collection contient des documents.
 Chaque document est au format JSON et contient des propriétés.

SQL MongoDB

base de données ou schéma base de données

table collection

enregistrement document

attribut (atomique) propriété

La notion de document est la base d’un système NoSQL. Toutefois, sachez que
MongoDB est plus que cela. Par exemple, MongoDB offre:

 Les Requêtes CRUD sur les documents (création, lecture, mise à jour et


suppression)
 Environnement de développement complet en ligne de commande
 Sécurisation des accès aux données
 Fonctions d’agrégation comme MapReduce
 Indexation
 Réplication des données et fractionnement sur plusieurs instances de
MongoDB

Schema-less
Mongodb est une base schema-less, une collection peut contenir des
documents de structures différentes et il n'est pas possible de définir la
structure a priori d'une collection.
La structure d'une collection n'est donc définie que par les documents qui
la composent, et elle peut évoluer dynamiquement au fur et à mesure des
insertions et suppressions.

Identification clé / valeur


Chaque document est identifié par un identifiant nommé _id unique pour
une collection, fonctionnant comme une clé primaire artificielle.

4
Architecture
MongoDB fonctionne sous la forme d'un serveur auquel il est possible de
se connecter avec un client textuel (mongo shell).
MongoDb peut être distribuée sur plusieurs serveurs (partitionnement
horizontal ou sharding) et accédée à travers de multiples couches
applicatives (langages, API...)

Installation de MongoDB
MongoDB est disponible sur Windows, OSX et Linux :
https://docs.mongodb.com/manual/installation

1.  Téléchargez la version de MongoDB à http://www.mongodb.org/downloads


2. Installer dans un répertoire à la racine de votre disque C:\ et utilisez un répertoire sans
espaces comme c:\mongodb-win32-x86.

Configuration de l'environnement MongoDB

MongoDB nécessite un répertoire pour stocker toutes les données. Par défaut, le chemin de ce
répertoire est \data\db. Créer ce répertoire. On obtient le répertoire c:\data\db.

Ou bien

Dans le répertoire C:\mongodb…\bin, créez un document nommé mongod.cfg et insérez-y les


lignes suivantes:

 logpath=C:\data\logs\mongo.log
 dbpath=C:\data\db

Vous pouvez spécifier un autre chemin pour les fichiers de données mais en utilisant l'option

 --dbpath à mongod.exe lors du lancement,

Exemple :

C:\mongodb\bin\mongod.exe --dbpath "C:\test\mongodb\data"

3. Création d’un répertoire pour vos bases de données C:\data\logs


4. Pour vérifier que l'installation a bien fonctionné, ajouter le chemin de mongodb au path et
exécutez la commande  mongod.exe –config C:\mongodb-2.6\bin\mongod.cfg.
5. Connecter vous à votre instance MongoDB avec la commande  mongo.exe

5
Pour se connecter à un serveur MongoDB :
 présent sur la même machine : exécuter simplement mongo dans un terminal ;

 distant sur le port standard de MongoDB : exécuter mongo --host nom-du-serveur.

 mongod est le processus démon primaire pour le système MongoDB. Il traite les requêtes
sur les données, gère l'accès aux données, et effectue des opérations de traitement en
arrière-plan.
 Lancez mongo via une invite de commandes en tapant la commande suivante : mongo
 La connexion ne peut réussir que si le service MongoDB est lancé.
 Par défaut, mongo recherche un serveur de base de données sur le port 27017 sur
l'interface locale. Pour se connecter à un serveur sur un port ou une interface différente,
utilisez les paramètres --port et --host.

Création d’un service Windows pour exécuter MongoDB en tant que


service système

Exécutez la commande suivante: sc.exe create MongoDB binPath= « c:\mongodb-


…\bin\mongod.exe –service –config C:\mongodb-…\bin\mongod.cfg »
DisplayName= « MongoDB Standard » start= « auto »

Pour démarrer le service, vous pouvez exécuter sc.exe start MongoDB.

Pour arrêter MongoDB, appuyez sur Control + C dans le terminal où l'instance de mongoDB est


lancée.

Créer une nouvelle base de données


Pour créer une base de données il faut exécuter l'instruction use sur une
nouvelle base de données, puis donner un ordre d'insertion d'un premier
document JSON avec insert.

Exemple
use db1
db.col1.insert( { "nom":"salima" } )

 On peut voir la liste des bases de données avec : show dbs


Pour accéder à la base de données db1 il faut utiliser la commande :
use db1

 On peut voir la liste des collections de la base de données en cours


avec : show collections
 On peut voir le contenu d'une collection avec : db.col1.find()

Cette commande donne comme résultat :


Le résultat attendu est le suivant, la clé générée étant bien entendu différente :

6
{ "_id":ObjectId("5dbe32b3286a9a8fad205"), "nom":"salima" }

Sélection d'une base de données

1. Pour voir le nom de la base de données courante, tapez db.


2. Pour voir la liste des bases de données, tapez show dbs.
3. Pour changer de base de données : use <database>.
4. # afficher les collections : Show collections
5. # renommer une collection : db.oldname.renameCollection("newname")
6. # effacer une collection : db.contacts.drop()
7. # effacer la base dans laquelle on est : db.dropDatabase()
8. Pour afficher l'aide : Help.

Insertion des documents


L'insertion de données dans une base MongoDB se fait avec l'instruction
db.collection.insert(Document JSON).

Si la collection n'existe pas, elle est créée dynamiquement.


L'insertion associe un identifiant (_id) à chaque document de la collection.
Exemple
db.Cinema.insert(
{
"nom":"Honkytonk Man",
"realisateur":{
"nom":"Eastwood",
"prenom":"Clint"
},
"annee":1982,
"acteurs":[
{
"nom":"Eastwood",
"prenom":"Kyle"
},
{
"nom":"Eastwood",
"prenom":"Clint"
}
]
}
)

Trouver des documents


La recherche de documents dans une base MongoDB se fait par l'instruction
db.collection.find(Document JSON, document JSON), où :

 le premier document JSON définit une restriction;

7
 le second document JSON (optionnel) définit une projection.

Exemple
Restriction
db.Cinema.find({"nom":"Honkytonk Man"})
retourne les documents JSON tels qu'ils ont à la racine un attribut "nom" avec la valeur
"Honkytonk Man".

Restriction et projection
db.Cinema.find({"nom":"Honkytonk Man"}, {"nom":1, "realisateur":1} )
retourne les documents JSON tels qu'ils ont à la racine un attribut "nom" avec la valeur
"Honkytonk Man", et seul les attributs situés à la racine "nom" et "realisateur" sont
projetés.

Insertion de plusieurs documents en utilisant une boucle « for »

Il est possible de générer des données de test en utilisant une boucle for. À partir du Shell mongo,
tapez la commande suivante :

for (var i = 1; i <= 10; i++) {


   db.testData.insert( { x : i } )
}

Insertion de plusieurs documents en utilisant une fonction JavaScript

Les données précédentes peuvent être générées en utilisant une fonction JavaScript :

function insertData(dbName, colName, num) {


var col = db.getSiblingDB(dbName).getCollection(colName);
for (i = 0; i < num; i++) {
col.insert({x:i});
}
print(col.count());
}

Cette fonction prend trois paramètres :

 la base de données ;
 le nom de la collection (existante ou non) ;
 le nombre de documents à créer.

Par exemple : insertData("bdtest", "testData", 15).

Cette fonction doit être introduite dans le fichier « .mongorc.js ».

8
Interroger Mongo en JavaScript
La console mongo permet d’exécuter des programme JavaScript avec
instruction load.

//test.js
2 print("Hello world");
> load("test.js")

Parcours d'un résultat de requête Mongo


//query.js
conn = new Mongo();
db = conn.getDB("db1");

recordset = db.User.find({"liked":{$elemMatch:{"star":3}}}, {"_id":0,


"liked.film":1})

while ( recordset.hasNext() ) {
printjson( recordset.next() );
}

 Utilisation de l'objet « Cursor »

Lorsqu'on interroge une collection, MongoDB retourne un objet «cursor» qui


contient les résultats de la requête.

Le shell mongo parcourt donc le cursor pour afficher les résultats, et n'affiche


dans un premier temps que les 20 premiers résultats.

Sur le shell mongo, il faut taper la commande it pour afficher la suite des


résultats.

Utilisation d'une boucle pour parcourir un objet « cursor »

Veuillez insérer plusieurs documents dans la collection en utilisant l'une des


méthodes de génération de données de test vues précédemment (shell ou
javascript).

1. Dans le shell MongoDB, faites une requête sur la collection testData en


affectant l'objet cursor à une variable c : var c = db.testData.find();
2. Affichez l'ensemble des résultats en utilisant une boucle while pour
parcourir le contenu de la variable c contenant l'objet cursor : 

while ( c.hasNext() ) printjson( c.next() ).

9
La méthode « hasNext() » retourne «true» si l'objet «cursor» contient
encore des documents.

La méthode « next() » affiche le document suivant.

La méthode printjson() affiche les documents sous un format JSON-like.

Utilisation d'un objet « cursor » comme un tableau

Il est possible de manipuler un objet «cursor» comme s'il était un


tableau :

1-      Faites une requête sur la collection testData en affectant le résultat dans


une variable :

var c = db.testData.find()

2-      Affichons par exemple le sixième élément : printjson( c [ 6 ] )

lorsqu'on accède aux documents d'un objet «cursor» en utilisant la notation


index des tableaux, mongo appelle dans un premier temps la méthode
« cursor.toArray() » et charge les documents retournés dans la RAM.
L'indexation est ainsi effectuée sur le tableau obtenu.

Pour de très grands volumes de données, mongo peut ainsi manquer de mémoire.

Requête pour l'obtention d'un document spécifique (équivaut à where)

Il est possible d'effectuer des requêtes avec paramètres pour obtenir un


document spécifique.

L'obtention de documents spécifiques se fait en interrogeant la collection en


utilisant un « document de requête » comme paramètre à la méthode
« find() ».

Dans notre cas : retrouvons les documents ayant 7 pour valeur de x, par
exemple. La requête est la suivante :

db.testData.find( { x : 7 } )

Le critère « x a pour valeur 7 » se traduit par le paramètre suivant : { x : 7 }.

10
Obtention d'un document unique d'une collection

La méthode « findOne() » permet d'obtenir un résultat unique à partir d'une


collection MongoDB.

La méthode « findOne() » prend les mêmes paramètres que la méthode


« find() », mais retourne un document au lieu d'un objet «cursor».

La commande suivante permet d'obtenir un document de la collection testData:

db.testData.findOne()

 Limiter le nombre de documents du résultat d'une requête

Pour des raisons de performance, il est possible de réduire la taille du résultat en


limitant le nombre de données à retourner via une requête.

La méthode « limit() » appliquée à un « cursor » permet de spécifier le nombre


maximum de documents à retourner.

Exemple :

db.testData.find().limit(3)

Insertion

Documents contenant un tags tableau

db.tags.insert({"tags":["red", "tall", "cheap"]});


db.tags.insert({"tags":["blue", "tall", "expensive"]});
db.tags.insert({"tags":["blue", "little", "cheap"]});

trouver tout ce qui inclut le tag "bleu"


db.tags.find({tags: { $elemMatch: { $eq: "blue" } }})

trouver tous les tags "bleu" et seulement bleu


db.tags.find({tags: "blue"})

trouver tous les tags "bleu" et "pas cher"


db.tags.find({ tags: { $all: ["cheap", "blue"] } } )

trouver tout pas "bleu"


db.tags.find({tags: { $ne: "blue" } })

# requêter selon des propriétés d'un sous-document


# admettons l'objet suivant :

11
# { nom: "Quentin", agenda: { lundi: "programmation", mardi: "dev", mercredi: "code" } }

# on pourra matcher depuis son sous document ainsi (notez bien les guillemets autour
de agenda.mardi) :

db.users.find({ 'agenda.mardi': 'dev' })

 db.db1.find().sort({ addedOn: 1 })

 db.db1.find().count()

12
Gestion de restaurants

Avant de commencer, il faut voir à quoi ressemble un document de notre


collection restaurants. Utilisons la fonction "findOne()".

db.restaurants.findOne()

{
"_id" : ObjectId("594b9172c96c61e672dcd689"),
"restaurant_id" : "30075445",
"name" : "Morris Park Bake Shop",
"borough" : "Bronx",
"cuisine" : "Bakery",
"address" : {
"building" : "1007",
"coord" :{"type":"Point","coordinates":[-73.856077,40.848447]},
"street" : "Morris Park Ave",
"zipcode" : "10462"
},
"grades" : [
{"date" : ISODate("2014-03-03T00:00:00.000Z"),"grade" : "A","score" : 2},
{"date" : ISODate("2013-09-11T00:00:00.000Z"),"grade" : "A","score" : 6},
{"date" : ISODate("2013-01-24T00:00:00.000Z"),"grade" : "A","score" : 10},
{"date" : ISODate("2011-11-23T00:00:00.000Z"),"grade" : "A","score" : 9},
{"date" : ISODate("2011-03-10T00:00:00.000Z"),"grade" : "B","score" : 14}
]
}

Filtrer et Projeter les données


Filtrage

db.restaurants.find( { "borough" : "Brooklyn" } )

6 085 restaurants sont retournés.

Parmi ces restaurants ceux afficher qui font de la cuisine italienne. Pour combiner
deux "clés/valeurs", il suffit de faire le document motif donnant quels paires
clés/valeurs sont recherchés :

db.restaurants.find(
{ "borough" : "Brooklyn",
"cuisine" : "Italian" }
)

Chercher le mot "pizza" dans le nom du restaurant

13
Pour cela, il faut utiliser les expressions régulières avec "/xxx/i" (le i pour "Insensible
à la casse").

db.restaurants.find(

{ "borough" : "Brooklyn",

"cuisine" : "Italian",

"address.street" : "5 Avenue",

"name" : /pizza/i }

Projection : ne garder dans le résultat que le nom

Un deuxième paramètre (optionnel) de la fonction find permet de choisir les clés à


retourner dans le résultat. Pour cela, il faut mettre la clé, et la valeur "1" pour projeter
la valeur.

db.getCollection('restaurants').find(

{"borough":"Brooklyn",

"cuisine":"Italian",

"name":/pizza/i,

"address.street" : "5 Avenue"},

{"name":1}

{"_id" : ObjectId("594b9173c96c61e672dd074b"), "name" : "Joe'S Pizza"}

L'identifiant du document "_id" est automatiquement projeté, pour le faire disparaître


il suffit de rajouter la valeur "0" : {"name":1, "_id":0}

Filtrage avec des opérations

Le filtrage par valeur exacte n'est pas suffisant pour exprimer tout ce que l'on
souhaiterait trouver dans la collection.

Je cherche maintenant les noms et scores des restaurants de Manhattan ayant un


score inférieur à 10.

14
On peut utiliser des opérateurs arithmétiques sur les clés (valeur numérique). Pour
cela, il sera préfixé d'un dollar $.

"Plus petit que" en anglais se dit "less than", l'opérateur est donc $lt. Il faut y
associer la valeur souhaitée : 

"grades.score" : {"$lt" : 10}.

L'opérateur est toujours imbriqué dans un document. Le concept "clé/valeur" doit


être respecté. Voici les opérateurs disponibles avec des exemples associés :

$gt, $gte >, ≥ Plus grand que (greater "a" : {"$gt" : 10}
than)
$lt, $lte <, ≤ Plus petit que (less than) "a" : {"$lt" : 10}
$ne ≄ Différent de (not equal) "a" : {"$ne" : 10}
$in, ∈, Fait parti de (ou ne doit pas) "a" : {"$in" : [10, 12, 15, 18] }
$nin ∉
$or ៴ OU logique "a" : {“$or” : [{"$gt" : 10}, {“$lt” :
5} ] }
$and ៱ ET logique "a" : {“$and” : [{"$lt" : 10}, {“$gt” : 5} ]
}
$not ¬ Négation “a" : {“$not” : {"$lt" : 10} }
$exists ∃ La clé existe dans le “a” : {“$exists” : 1}
document
$size   test sur la taille d'une liste “a” : {“$size” : 5}

(uniquement par égalité)

db.getCollection('restaurants').find(

{"borough":"Manhattan",

"grades.score":{$lt : 10}

},

{"name":1,"grades.score":1, "_id":0})

Ce qui est dérangeant dans ce résultat, c'est le fait que l'on trouve des scores
supérieurs à 10 !

Nous ne sommes plus en relationnel, ainsi, l'opération "grades.score" : {"$lt" : 10},


veut dire :

Est-ce que la liste "grades" contient des scores avec une valeur inférieure à 10 ?

Pour ne récupérer que ceux qui n'ont pas de score supérieur à 10, il faut alors
combiner la première opération avec une négation de la condition " ≥10".

15
La condition est alors vérifiée sur chaque élément de la liste.

Il faut ajouter la condition suivante : $not:{$gte:10}

db.getCollection('restaurants').find(
{"borough":"Manhattan",
"grades.score":{
$lt:10,
$not:{$gte:10}
}
},
{"name":1,"grades.score":1, "_id":0})

Chercher les restaurants qui ont un grade ‘C’ avec un score inférieur à 40.

db.restaurants.find({
"grades.grade" : "C",
"grades.score" : {$lt : 30}
},
{"Grades.grade":1, "grades.score":1}
);

$elemMatch

db.restaurants.find({
"grades" : {
$elemMatch : {
"grade" : "C",
"score" : {$lt :40}
}
}
},
{"grades.grade" : 1,"grades.score" : 1}
);

Distinct

db.restaurants.distinct("borough")

16

Vous aimerez peut-être aussi