Documents de cours 2020-2021 - FX Jollois
Le but de ce TP est de voir l’utilisation des commandes MongoDB dans R.
Nous allons utiliser la librairie mongolite
. Pour l’utiliser (après l’avoir installée), on l’importe classiquement comme ci-dessous.
library(mongolite)
La première opération est de créer une connexion entre R et MongoDB en utilisant la fonction mongo()
. Celle-ci prend en paramètre la base et la collection, plus si besoin l’adresse du serveur. S’elle n’y est pas, elle se connecte en local (ce qui est notre cas normalement).
m = mongo(
collection = "restaurants",
db = "test")
Par le biais de l’objet ainsi créé (m
), on a accès aux différentes fonctions que l’on a vu dans Mongo (précisemment count()
, distinct()
, find()
et aggregate()
).
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 address
et grades
sont particuliers, comme on peut le voir dans le JSON
. Le premier est une liste, et le deuxième est un tableau. Voila leur classe en R.
class(d$address)
d$address
class(d$grades)
d$grades
Il existe la fonction count()
qui compte directement le nombre de document. Dans le cas où l’on veut compter les documents qui respectent une certaine condition, nous utilisons le paramètre query
. Comme vous pouvez le voir dans les exemples ci-dessous, il est nécessaire de passer la requête en JSON
, dans une chaîne de caractères.
m$count()
m$count(query = '{ "borough": "Brooklyn" }')
m$count(query = '{ "borough": "Brooklyn", "cuisine": "French" }')
m$count(query = '{ "borough": "Brooklyn", "cuisine": { "$in": ["French", "Italian"]} }')
m$count(
query = '{
"borough": "Brooklyn",
"cuisine": { "$in": ["French", "Italian"]}
}'
)
street
du champs address
m$count(
query = '{
"address.street": "Franklin Street"
}'
)
m$count(
query = '{
"grades.score": 0
}'
)
m$count(
query = '{
"grades.score": { "$lte": 5 }
}'
)
Il existe la même fonction distinct()
, avec les mêmes possibilités. On peut ainsi vouloir les valeurs distinctes présentes dans un sous-ensemble de documents (respectant une contrainte particulière). Voici quelques exemples :
borough
), pour tous les restaurantsm$distinct(key = "borough")
m$distinct(
key = "cuisine",
query = '{ "borough": "Brooklyn" }'
)
m$distinct(
key = "grades.grade",
query = '{ "borough": "Brooklyn" }'
)
find()
Cette fonction permet donc de récupérer tout ou partie des documents, selon éventuellement un critère de restriction (dans le paramètre query
) et un critère de projection (dans le paramètre fields
). Pour n’avoir que le premier document, on utilise le paramètre limit
). Pour le tri, on utilise le paramètre sort
. Voici quelques exemples :
"street"
et "borough"
)m$find(query = '{ "name": "Shake Shack" }',
fields = '{ "address.street": 1, "borough": 1 }')
m$find(query = '{ "name": "Shake Shack" }',
fields = '{ "_id": 0, "address.street": 1, "borough": 1 }')
R
.m$find(query = '{"borough": "Queens", "grades.score": { "$gte": 50}}',
fields = '{"_id": 0, "name": 1, "grades.score": 1, "address.street": 1}',
limit = 10)
m$find(query = '{"name": "Shake Shack", "borough": {"$in": ["Queens", "Brooklyn"]}}',
fields = '{"_id": 0, "address.street": 1, "borough": 1}')
m$find(query = '{"borough": "Queens", "grades.score": { "$gt": 50}}',
fields = '{"_id": 0, "name": 1, "address.street": 1}',
sort = '{"address.street": -1, "name": 1}')
Bien évidemment, on peut faire des calculs d’agrégats, avec la fonction aggregate()
, prenant en paramètre donc le pipeline en chaîne de caractères. Voici encore quelques exemples :
m$aggregate(pipeline = '[
{"$limit": 10 }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$sort": { "name": 1 }}
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$sort": { "name": 1 }},
{ "$match": { "borough": "Brooklyn" }}
]')
m$aggregate(pipeline = '[
{ "$match": { "borough": "Brooklyn" }},
{ "$limit": 10 },
{ "$sort": { "name": 1 }}
]')
grades
)
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$unwind": "$grades" }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$unwind": "$grades" },
{ "$match": { "grades.grade": "B" }}
]')
$unwind
et $match
, le résultat est clairement différentm$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$match": { "grades.grade": "B" }},
{ "$unwind": "$grades" }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": { "name": 1, "borough": 1 } }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": { "address": 0, "grades": 0 } }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": { "name": 1, "borough": 1 , "street": "$address.street"} }
]')
grades
)m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": { "name": 1, "borough": 1, "nb_grades": { "$size": "$grades" } } }
]')
grades
est préent mais égal à NULL
)grades
m$aggregate(pipeline = '[
{ "$project": { "name": 1, "borough": 1, "nb_grades": { "$size": "$grades" } } },
{ "$sort": { "nb_grades": 1 }},
{ "$limit": 10 }
]')
grades
(indicé 0)m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": { "name": 1, "borough": 1, "grade": { "$arrayElemAt": [ "$grades", 0 ]} } }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": { "nom": { "$toUpper": "$name" }, "borough": 1 } }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$addFields": { "nb_grades": { "$size": "$grades" } } }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$project": {
"nom": { "$toUpper": "$name" },
"quartier": { "$substr": [ "$borough", 0, 3 ] }
} }
]')
m$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$addFields": { "quartier": { "$toUpper": { "$substr": [ "$borough", 0, 3 ] } } }},
{ "$project": {
"nom": { "$toUpper": "$name" },
"quartier": { "$cond": { "if": { "$eq": ["$borough", "Bronx"] }, "then": "BRX", "else": "$quartier" } },
"borough": 1
} }
]')
m$aggregate(pipeline = '[
{"$group": {"_id": "Total", "NbRestos": {"$sum": 1}}}
]')
m$aggregate(pipeline = '[
{"$group": {"_id": "$borough", "NbRestos": {"$sum": 1}}}
]')
m$aggregate('[
{ "$match": { "borough": "Queens" }},
{ "$unwind": "$grades" },
{ "$group": { "_id": "null", "score": { "$avg": "$grades.score" }}}
]')
m$aggregate('[
{ "$unwind": "$grades" },
{ "$group": { "_id": "$borough", "score": { "$avg": "$grades.score" }}},
{ "$sort": { "score": -1 }}
]')
$match
permet de supprimer les restaurants sans évaluations (ce qui engendrerait des moyennes = NA
)m$aggregate(pipeline = '[
{ "$project": {
"borough": 1, "street": "$address.street",
"eval": { "$arrayElemAt": [ "$grades", 0 ]}
} },
{ "$match": { "eval": { "$exists": true } } },
{ "$match": { "eval.score": { "$gte": 0 } } },
{ "$group": {
"_id": { "quartier": "$borough", "rue": "$street" },
"score": { "$avg": "$eval.score" }
}},
{ "$sort": { "score": 1 }},
{ "$limit": 10 }
]')
$addToSet
et $push
, on les applique sur les grades obtenus pour les 10 premiers restaurants
$addToSet
: valeurs distinctes$push
: toutes les valeurs présentesm$aggregate(pipeline = '[
{ "$limit": 10 },
{ "$unwind": "$grades" },
{ "$group": {
"_id": "$name",
"avec_addToSet": { "$addToSet": "$grades.grade" },
"avec_push": { "$push": "$grades.grade" }
}}
]')
Dans R