Au ciné avec Mongo II
[30 min]
Soit les données suivantes représentant des films de cinéma et des avis d'utilisateurs concernant ces films.
db.Cinema.drop()
db.Cinema.insert(
{
nom:"Goodfellas",
annee:1990,
realisateur:{nom:"Scorsese", prenom:"Martin"},
})
db.Cinema.insert(
{
nom:"The Godfather",
annee:1972,
realisateur:{nom:"Coppola", prenom:"Francis Ford"},
})
db.Cinema.insert(
{
nom:"Million Dollar Baby",
realisateur:{nom:"Eastwood", prenom:"Clint"},
})
db.Cinema.insert(
{
nom:"Gran Torino",
annee:2008,
realisateur:{nom:"Eastwood", prenom:"Clint"},
})
db.Cinema.find()
db.User.drop()
db.User.insert(
{
"pseudo":"Stph",
"liked" :
[
{"film":ObjectId("590c366d70f50381c920ca71"),"star":3},
{"film":ObjectId("590c366d70f50381c920ca72"),"star":1}
]
}
)
db.User.insert(
{
"pseudo":"Luke",
"liked" :
[
{"film":ObjectId("590c366d70f50381c920ca71"),"star":2}
]
}
)
db.User.insert(
{
"pseudo":"Tuco",
"liked" :
[
{"film":ObjectId("590c366d70f50381c920ca73"),"star":3}
]
}
)
db.User.find()
Question
Critiquer cette insertion mobilisant les identifiants des films ? Pourquoi n'est ce pas reproductible ? Imaginez deux solutions.
Solution
Les identifiants sont ici contrôlés par le système, ils sont fixés lors de l'insertion, donc les identifiants seront différents à chaque insertion.
Solution 1
On peut fixer les identifiants que l'on a besoin de référencer manuellement (mais il faudra en gérer nous même l'unicité dans ce cas) :
db.Cinema.drop()
db.Cinema.insert(
{
_id:ObjectId("590c366d70f50381c920ca71"),
nom:"Goodfellas",
annee:1990,
realisateur:{nom:"Scorsese", prenom:"Martin"},
})
db.Cinema.insert(
{
_id:ObjectId("590c366d70f50381c920ca72"),
nom:"The Godfather",
annee:1972,
realisateur:{nom:"Coppola", prenom:"Francis Ford"},
})
db.Cinema.insert(
{
_id:ObjectId("590c366d70f50381c920ca73"),
nom:"Million Dollar Baby",
realisateur:{nom:"Eastwood", prenom:"Clint"},
})
db.Cinema.insert(
{
_id:ObjectId("590c366d70f50381c920ca74"),
nom:"Gran Torino",
annee:2008,
realisateur:{nom:"Eastwood", prenom:"Clint"},
})
db.Cinema.find()
On se placera dans le cadre de cette solution 1 pour la suite de l'exercice.
Solution 2
On peut trouver une clé naturelle pour faire les références, par exemple le titre du film et l'année de production.
db.User.drop()
db.User.insert(
{
"pseudo":"Stph",
"liked" :
[
{"film":{nom:"Goodfellas",annee:1990},"star":3},
{"film":{nom:"The Godfather",annee:1972},"star":1}
]
}
)
db.User.insert(
{
"pseudo":"Luke",
"liked" :
[
{"film":{nom:"Goodfellas",annee:1990},"star":2}
]
}
)
db.User.insert(
{
"pseudo":"Tuco",
"liked" :
[
{"film":{nom:"Gran Torino",annee:2008},"star":3}
]
}
)
db.User.find()
On pourrait aussi dans le cadre de cette seconde solution écrire un programme JavaScript qui interroge la base de donnés avec la clé naturelle et récupère la clé artificielle pour l'utiliser comme référence.
Question
Pourquoi trouver les titres de ces films n'est pas trivial avec Mongo (si, comme dans l'énoncé initial on a pas stocké le nom du film comme clé de référencement dans la collection User) ?
Solution
Comme Mongo ne gère pas de jointure, on va devoir la réaliser au niveau de la couche cliente applicative.
Interroger MongoDb avec la requête précédente
Récupérer et dédoublonner la liste des identifiants de films
Boucler sur ces identifiants, créer une requête pour chaque identifiant et interroger MongoDB
Par exemple :
db.Cinema.find({"_id":ObjectId("574ddaa372ace929efdaa46f")})
Complément : distinct()
Pour récupérer une liste de valeurs distinctes, on pourra cette requête :
db.User.distinct("liked.film", {"liked.star":3})
Cette requête renvoie directement un tableau et non un pointeur sur une liste d'enregistrements.
Complément : findOne()
Pour récupérer une entrée par son identifiant on préférera cette requête :
db.Cinema.findOne(ObjectId('574ddaa372ace929efdaa46f'))
Question
On cherche à présent les identifiants des films qui sont aimés au moins une fois avec 3 étoiles.
On essaie cette requête : db.User.find({"liked.star":3}, {_id:0, "liked.film":1})
, mais elle ne renvoie pas le résultat escompté.
Expliquez pourquoi ? Proposez des solutions.
Solution
La requête renvoie des utilisateurs et non des avis, et donc on va trouver tous les films qui ont été aimés par un utilisateur qui a aimé au moins un film avec 3 étoiles.
Solution 1 : Conception
Ici on a créé une collection User d'utilisateurs, mais on ne gère que le pseudo de ces derniers, on aurait pu à la place créer une collection d'avis, cela aurait permis d'exécuter la requête recherchée.
Solution : JavaScript
On modifie la requête précédente pour conserver le nombre d'étoiles : db.User.find({"liked.star":3}, {_id:0, "liked.film":1, "liked.star":1})
et on écrit un programme JavaScript qui filtre uniquement les films ayant star à 3.