Tutoriel sur MongoDB

imt

1. Objectif

Un tout petit turoriel rapide pour découvrir MongoDB et l’utiliser sur notre service movie en REST. La base MongoDB du tutoriel va être deployée dans un conteneur Docker. Le fichier docker-compose.yml contient le nécessaire le deployer.

Il faut :

  • cloner/télécharger le repo de code https://github.com/IMTA-FIL/UE-AD-A1-TUTO-MONGO

  • installer l’extension vscode database client

  • installer Docker desktop (si ce n’est pas déjà fait) et exécuter docker compose up à la racine du repo

  • créer un environnement virtuel Python et installer le contenu du fichier requirements.txt

  • python3 -m venv venv

  • source venv/bin/activate

  • pip3 install -r requirements.txt

2. Se connecter à MongoDB

2.1. Mongo express

Pour vérifier que tout fonctionne bien, commencez par vous connecter sur votre navigateur à l’interface Mongo Express aussi déployée dans un conteneur : http://localhost:8081

Mongo Express a déjà été configuré pour se connecter à la base mongo dans l’autre conteneur, vous pouvez regarder le fichier docker-compose.yml

2.2. Database client vscode

Si vous utilisez vscode, vous pouvez aller dans l’extension database puis vous connecter à la base Mongo.

  • sélectionner la bon type de base

  • indiquer 127.0.0.1 (localhost) et laisser le port par défaut

  • indiquer le bon utilisateur et mot de passe (voir dans docker-compose.yml)

Vous pouvez ensuite faire des requêtes dans l’interface.

3. Utilisation de pymongo

Nous allons maintenant utiliser directement la base Mongo dans nos programmes Python. Pour cela la librairie `pymongo`a été installée dans les requirements.

Il faut d’abord importer pymongo, ainsi que bson pour pouvoir passer des formats pymongo vers json.

from pymongo import MongoClient
from bson.json_util import dumps

Nous allons maintenant nous connecter à la base de données dans le code Python directement.

client = MongoClient("mongodb://root:example@localhost:27017/")

On voit ici qu’on créée un client Mongo and se connectant à localhost sur le port 27017 et en utilisant le bon login et password.

Nous allons maintenant nous connecter à la base de données movies (qui sera créée si elle n’existe pas) et à la collection (équivalent à une table) du même nom (qui sera aussi créée si elle n’existe pas). C’est à partir de la collection que nous pouvons ensuite faire des requêtes. Ici nous faisons une insertion de données avec la fonction insert_many et nous lui donnons directement le tableau de films récupéré du fichier json.

db = client["movies"]
collection = db["movies"]
collection.insert_many(movies)

Vérifiez dans Mongo Express et dans vscode database que les données ont bien été ajoutées.

4. Modification des points d’entrée

4.1. Find

Nous allons dans un premier temps modifier la route GET /moviesbytitle.

@app.route("/moviesbytitle", methods=['GET'])
def get_movie_bytitle():
    movie = {}
    if request.args:
        req = request.args
        movie = collection.find_one({"title": str(req["title"])})
        res = make_response(dumps(movie),200)
        return res
    return make_response(jsonify({"error":"movie title not found"}),500)

Ici, au lieu de parcourir les films à la main dans le tableau movies nous allons chercher les données dans la base Mongo. Pour cela nous utilisons la fonction find_one qui consiste à chercher une entrée correspondante à la requête dans la collection movies. Notre requête est

{"title": str(req["title"])}

Cela signifie que nous cherchons une entrée (un film) dont le title est str(req['title']). Rappelons ici que req contient les arguments de la requête HTTP dans ce cas, à savoir le titre d’un film. Notons aussi qu’il est nécessaire de caster le paramètre explicitement en string pour un bon fonctionnement.

On voit ensuite que nous n’utilisons plus la fonction jsonify qui permet de sérialiser du json dans la réponse HTTP. A la place nous utilisons la fonction dumps issue du from bson.json_util import dumps. Cette fonction permet de transformer le format de sortie de la requête Mongo en json.

Testez ce point d’entrée modifié avec Insomnia ! Que remarquez-vous ?

4.2. Find avancé

Nous allons maintenant ajouter une route permettant de filtrer les films par notes.

@app.route("/movies/minrate/<rate>", methods=['GET'])
def get_movies_minrate(rate):
    mylist = list(collection.find({"rating": {"$gt": float(rate)}}))
    res = make_response(dumps(mylist),200)
    return res

Cette route prend en paramètre de chemin une note et retourne tous les films dont la note est strictement supérieure à celle donnée. Pour cela, nous faisons une requête find paramètrée par {"$gt": float(rate)}. Le $gt signifie "greater than".

Testez ce point d’entrée avec Insomnia !

4.3. Update

Enfin, nous allons modifier la route permettant de changer la note d’un film.

@app.route("/movies/<movieid>/<rate>", methods=['PUT'])
def update_movie_rating(movieid, rate):
    myquery = { "id": movieid }
    newvalues = { "$set": { "rating": float(rate) } }
    collection.update_one(myquery, newvalues)

    res = make_response(jsonify({"message":"rate updated"}),200)
    return res

Nous utilison la fonction update_one de pymongo pour faire une requête de mise à jour.

La requête est :

myquery = { "id": movieid }

Elle permet de trouver le film font l’ID est donné en paramètre.

La modification à apporter est spécifiée par

newvalues = { "$set": { "rating": float(rate) } }

Cela indique que nous allons attribuer la valeur float(rate) à la clé rating du film trouvé.

Finalement, nous appelons donc

collection.update_one(myquery, newvalues)

pour effectuer l’update.

vérifiez que la modification a bien été faite en base.