MongoDB est une base de données NoSQL distribué de type Document Store (site web)
Objectifs :
Principe de base : les données sont des documents
collections
JSON
JavaScript Object Notation
{}
{ "nom": "jollois", "prenom": "fx" }
[]
[ 1, 5, 10]
string
et number
) et trois constantes (true
, false
, null
)Validation possible du JSON sur jsonlint.com/
{
"tubd": {
"formation": "DU Analyste Big Data",
"responsable": { "nom": "Poggi", "prenom": "JM" },
"etudiants" : [
{ "id": 1, "nom": "jollois", "prenom": "fx" },
{ "id": 2, "nom": "aristote", "details": "délégué" },
{ "id": 5, "nom": "platon" }
],
"ouverte": true
},
"tudv": {
"formation": "DU Data Visualisation",
"ouverte": false,
"todo": [
"Creation de la maquette",
"Validation par le conseil"
],
"responsable": { "nom": "Métivier" }
}
}
BSON
: extension de JSON
Schéma dynamique
ALTER TABLE
ou de redesign de la basePas de jointures entre les collections
Commandes suivantes réalisables à partir du shell
R
On peut interroger une base de données de ce type via le package mongolite
dans R
. Dans la suite, nous allons nous connecter sur un serveur distant, et travailler pour l’exemple sur une base des pays du monde (datant de fin 99).
library(mongolite)
m = mongo(url = "mongodb://193.51.82.104:2231",
db = "world",
collection = "country")
Le premier document est présenté ci-dessous. La base contient les informations de 239 pays.
{
"_id" : ObjectId("586a0f9e9a4df5e90a24d1ed"),
"Code" : "ABW",
"Name" : "Aruba",
"Continent" : "North America",
"Region" : "Caribbean",
"SurfaceArea" : 193,
"IndepYear" : "NA",
"Population" : 103000,
"LifeExpectancy" : 78.4,
"GNP" : 828,
"GNPOld" : 793,
"LocalName" : "Aruba",
"GovernmentForm" : "Nonmetropolitan Territory of The Netherlands",
"HeadOfState" : "Beatrix",
"Capital" : {
"ID" : 129,
"Name" : "Oranjestad",
"District" : "–",
"Population" : 29034
},
"Code2" : "AW",
"OffLang" : [
{
"Language" : "Dutch",
"Percentage" : 5.3
}
],
"NotOffLang" : [
{
"Language" : "English",
"Percentage" : 9.5
},
{
"Language" : "Papiamento",
"Percentage" : 76.7
},
{
"Language" : "Spanish",
"Percentage" : 7.4
}
]
}
R
R
ne gérant pas nativement les données JSON
, les documents sont traduits, pour la librairie mongolite
, en data.frame
. Pour récupérer le premier document, nous utilisons la fonction find()
de l’objet créé m
.
d = m$find(limit = 1)
d
class(d)
Les objets Capital
, OffLang
et NotOffLang
sont particuliers, comme on peut le voir dans le JSON
. Capital
est une liste, et les deux autres sont des tableaux de listes. Voila leur classe en R
.
class(d$Capital)
d$Capital
class(d$OffLang)
d$OffLang
class(d$NotOffLang)
d$NotOffLang
La fonction find()
de l’objet m
permet de retourner tous les documents. On peut se limiter à un certain nombre de documents avec l’option limit
, comme précédemment.
Pour faire une restriction sur la valeur d’un attribut, il faut utiliser l’option query
, avec un formalisme particulier. Il faut écrire au format JSON
dans une chaîne, avec pour les champs à comparer leur nom suivi de la valeur (pour l’égalité) ou d’un objet complexe pour les autres tests (infériorité, supériorité, présence dans une liste).
Pour une projection, c’est l’option fields
à renseigner. On écrit au format JSON
, avec la valeur 1
pour les champs qu’on souhaite avoir en retour. Par défaut, l’identifiant (_id
) est toujours présent, mais on peut le supprimer en indiquant 0
.
Dans cet exemple, on recherche le document dont l’attribut "Name"
est égal à "France"
, et on affiche uniquement les attributs "Name"
et "Population"
. Dans la deuxième expression, on supprime l’affichage de l’identifiant interne à MongoDB.
m$find(query = '{"Name": "France"}',
fields = '{"Name": 1, "Population": 1}')
m$find(query = '{"Name": "France"}',
fields = '{"_id": 0, "Name": 1, "Population": 1}')
Ici, on recherche les pays du continent "Europe"
dont la population est supérieur à 50M d’habitants.
m$find(query = '{"Continent": "Europe", "Population": {"$gt": 50000000}}',
fields = '{"_id": 0, "Name": 1, "Population": 1}')
m$find(query = '{"Continent": {"$in": ["Antarctica", "South America"]}}',
fields = '{"_id": 0, "Name": 1, "Continent": 1}')
Dans ces exemples, nous cherchons en premier les pays avec comme capital "Paris"
, et ensuite les pays dont une des langues officielles est le français.
m$find(query = '{"Capital.Name": "Paris"}',
fields = '{"_id": 0, "Name": 1, "Capital": 1}')
m$find(query = '{"OffLang.Language": "French"}',
fields = '{"_id": 0, "Name": 1, "OffLang.Percentage": 1}')
Il est aussi posible de trier les documents retournés, via l’option sort
. Toujours en JSON
, on indique 1
pour un tri croissant et -1
pour un tri décroissant.
m$find(fields = '{"_id": 0, "Name": 1}',
sort = '{"Population": -1}',
limit = 10)
On peut déjà faire un dénombrement avec la fonction count()
de l’objet m
. Sans option, on obtient le nombre de documents de la collection. On peut aussi ajouter une restriction pour avoir le nombre de documents respectant ces conditions. Les requêtes s’écrivent de la même manière que pour la fonction find()
.
m$count()
m$count(query = '{"Name": "France"}')
m$count(query = '{"Continent": "Europe"}')
Il existe la fonction aggregate()
pour tous les calculs d’agrégat (et même plus). Il faut passer dans le paramètre pipeline
un tableau d’actions, pouvant contenir les éléments suivants :
$project
: redéfinition des documents (si nécessaire)$match
: restriction sur les documents à utiliser$group
: regroupements et calculs à effectuer$sort
: tri sur les agrégats$unwind
: découpage de tableauxgroup by NULL
m$aggregate(pipeline = '[
{"$group": {"_id": "$Continent", "NbPays": {"$sum": 1}}}
]')
m$aggregate(pipeline = '[
{"$group": {"_id": "$Continent", "NbPays": {"$sum": 1}, "Population": {"$sum": "$Population"}}},
{"$sort": { "NbPays": -1}}
]')
Si on veut avoir le pourcentage de population parlant une langue spécifique officiellement, il faut utiliser cette fonction, avec l’action $unwind
. Celle-ci permet de dupliquer les lignes de chaque document, avec chaque valeur du tableau indiqué dans $unwind
. Ensuite, on se retreint aux pays dont une langue officielle est le français avec $match
. Enfin, on n’affiche que le nom du pays et le pourcentage de la population parlant le français.
m$aggregate('[
{ "$unwind": "$OffLang" },
{ "$match": { "OffLang.Language": "French"}},
{ "$project": { "_id": 0, "Name": 1, "OffLang.Percentage": 1 }}
]')
m$aggregate('[
{ "$unwind": "$OffLang" },
{ "$project": { "Language": "$OffLang.Language", "Percentage": "$OffLang.Percentage"}},
{ "$group": { "_id": "$Language", "NbPays": {"$sum": 1}, "PctMoyen": { "$avg": "$Percentage" } }},
{ "$sort": { "NbPays": -1}}
]')
Le paradigme Map-Reduce permet de décomposer une tâche en deux étapes :
Exemple classique : décompte des mots présents dans un ensemble de texte
<mot, 1>
(un document = beaucoup de résultats générés)On utilise la fonction mapreduce()
de m
pour appliquer l’algorithme Map-Reduce sur les documents de la collection, avec les paramètres suivants :
map
: fonction JavaScript
emit(key, value)
pour créer un couple résultatreduce
: fonction JavaScript
key
et values
(tableau des valeurs créés à l’étape précédente)return result
pour renvoyer le résultatout
: collection éventuelle dans laquelle stocker les résultats dans MongoDBDans la fonction concernant l’étape Map, on utilise l’objet this
pour accéder aux attributs du document. Le langage utilisé est le JavaScript
.
Dans l’exemple ci-dessous, nous calculons pour chaque continent la population de celui-ci.
m$mapreduce(
map = 'function() { emit(this.Continent, this.Population)}',
reduce = 'function(cont, nb) { return Array.sum(nb) }'
)
On peut utiliser ce paradigme pour calculer le pourcentage médian de la population parlant cette langue.
m$mapreduce(
map = 'function() {
if (this.OffLang) {
for (i = 0; i < this.OffLang.length; i++) {
emit(this.OffLang[i].Language, this.OffLang[i].Percentage);
}
}
}',
reduce = 'function(id, values) {
values.sort( function(a,b) {return a - b;} );
var half = Math.floor(values.length/2);
if(values.length % 2)
return values[half];
else
return (values[half-1] + values[half]) / 2.0;
}'
)
Les données de la base médicaments ont été importées dans MongoDB via ce script (merci de ne pas l’exécuter).
Vous pouvez vous connecter à cette base à l’aide du code suivant.
library(mongolite)
bd = mongo(url = "mongodb://193.51.82.104:2231",
db = "medicaments",
collection = "import20161109")
Trouver les informations suivantes :
CodeCIS
est 60051234
"MEDICE"
"IBUPROFENE 400 mg - BRUFEN 400 mg, comprimé pelliculé."
(Afficher seulement la dénomination et le titulaire)"Autorisation active"
comme StatutAMM
Surveillance
)CIP
) des médicaments qui sont à "prescription hospitalière"
(cf CPD
)Essayer de faire les exercices du TP1 :
NB :
"cutanée;orale;sublinguale"
). Il faut donc ici aussi faire un pré-traitement.