cours-2023-2024 | Documents de mes cours pour l'année 2023-2024 | FX Jollois
Pour se connecter à un serveur Mongo qui fonctionne sur la même machine que Python, vous devez ne rien indiquer dans la fonction MongoClient(), comme ci-dessous.
import pymongo
client = pymongo.MongoClient()
db = client.SAE
Ce code permet de se connecter à la BD SAE du serveur Mongo. Si celle-ci n’existe pas, elle ne sera pour autant pas encore créée.
Dans cet exemple, nous allons utiliser la petite base de données Gymnase2000, disponible au format SQLite sur ce lien. Voici son schéma relationnel

Dans ce schéma, après analyse, on peut décider de créer 2 collections :
GymnasesGymnasesPour se connecter à la base, on ré-utilise le code vu précédemment.
import sqlite3
import pandas
# Création de la connexion
conn = sqlite3.connect("Gymnase2000.sqlite")
On doit tout d’abord récupérer les informations des 2 tables Gymnases et Seances, que l’on va stocker dans des DataFrames.
Attention : on va tout de suite récupérer le nom du sport en chaîne de caractères. On supprimera l’identifiant du sport ultérieurement.
gymnases = pandas.read_sql_query(
    "SELECT * FROM Gymnases;", 
    conn
)
seances = pandas.read_sql_query(
    "SELECT * FROM Seances INNER JOIN Sports USING (IdSport);", 
    conn
)
gymnasesMaintenant, nous devons ajouter la liste des séances comme colonne du DataFrame gymnases. Pour cela, si nous considérons le gymnase avec l’identifiant 1, nous pouvons déjà récupérer l’ensemble de ces séances comme suit.
id = 1
seances.query('IdGymnase == @id')
Ensuite, pour supprimer les colonnes IdGymnase et IdSport, on peut procéder ainsi.
seances.query('IdGymnase == @id').drop(columns = [["IdGymnase", "IdSport"]])
Enfin, pour transformer le résultat (qui est un DataFrame) en un dictionnaire python, nous utilisons la fonction to_dict(), avec en paramètre orient égal à records, ceci permet de faire un tableau de dictionnaires.
seances.query('IdGymnase == @id').drop(columns = ["IdGymnase", "IdSport"]).to_dict(orient = "records")
Ce code peut maintenant s’intégrer dans une list comprehension pour créer une liste de dictionnaires pour chaque gymnase.
liste = [seances.query('IdGymnase == @id').drop(columns=["IdGymnase", "IdSport"]).to_dict(orient = "records") for id in gymnases.IdGymnase]
liste
On peut maintenant ajouter ceci au DataFrame gymnases, comme ci-dessous.
gymnases = gymnases.assign(Seances = liste)
gymnases.head()
gymnases à MongoGrâce à la même fonction to_dict() et la fonction insert_many() sur une collection, nous allons pouvoir intégrer les données à Mongo. Le code suivant va donc créer la BD SAE (car vide pour le moment), puis la collection Gymnases, et ensuite y placer toutes les données.
db.Gymnases.insert_many(gymnases.to_dict(orient = "records"))
Vous pouvez aller voir le résultat dans Compass. Vous pouvez aussi tester directement en interrogeant Mongo pour le nombre de documents de la collection :
db.Gymnases.count_documents({})
Et aussi sur le contenu de la collection :
list(db.Gymnases.find())
Si jamais vous aviez fait une erreur, vous pouvez supprimer une collection (voire un BD) soit directement dans Compass, soit en passant par python. Le code ci-dessous permet de supprimer la collection Gymnases par exemple.
db.Gymnases.drop()
SportifsA FAIRE
Supposons que vous avez créer la collection Sportifs, avec comme champs IdSportif. Si nous souhaitons récupérer les informations l’entraineur pour chaque séance, pour chaque gymnase, nous pouvons exécuter le code suivant :
pandas.DataFrame(list(db.Gymnases.aggregate([
    { "$limit": 1 },
    { "$unwind": "$Seances" },
    { "$lookup": {
       "from": "Sportifs",
       "localField": "Seances.IdSportifEntraineur",
       "foreignField": "IdSportif",
       "as": "Entraineur"
     }}
])))
A noter : le résultat d’un lookup est forcément un tableau, même s’il n’y a qu’une seule valeur. A vous de faire le travail pour l’extraire dans un littéral simple si vous le souhaitez (par exemple avec $first).
A FAIRE : récupérer les informations du conseiller pour chaque sportif.
Dans Compass, vous allez charger les données présentes dans le fichier movies.json. Pour cela, dans le logiciel, suivez les étapes suivantes :
SAEmovies par exemple)Les données sont maintenant dans Mongo. On peut voir le contenu du premier document comme suit.
db.movies.find_one()
movies = sqlite3.connect("Movies.sqlite")
On va commencer par créer une table faisant la correspondance entre l’identifiant spécifique à Mongo (et peu facile à gérer par ailleurs) et un identifiant créé automatiquement.
corresp = pandas.DataFrame(list(db.movies.find({}, { "_id": 1 })))
corresp = corresp.assign(idFilm = range(1, len(corresp) + 1))
corresp
Nous voyons par exemple qu’il y a le champs genres contenant une liste de genres pour un film. La bonne façon de faire pour passer en relationnel serait de :
genres contenant deux colonnes : idGenre (à créer) et libGenrefilm_genre contenant deux colonnes : idFilm (à créer aussi éventuellement) et idGenreOn peut donc avoir la liste des genres avec la commande suivante.
db.movies.distinct("genres")
Avec un peu de manipulation python, on peut créer la table genres comme ci-dessous.
liste_genres = db.movies.distinct("genres")
genres = pandas.DataFrame({
    "id_genre": range(1, len(liste_genres)+1),
    "genre": liste_genres
})
genres
On peut donc déjà l’intégrer à la BD SQLite
genres.to_sql('genres', movies, index = False)
Pour créer la deuxième table, nous devons déjà récupérer la liste des genres pour chaque film. Ensuite, on enlève les données manquantes (i.e. des films sans genres). Et on récupère les nouveaux identifiants de films créés avant. On supprime au final la colonne _id.
df_genres = pandas.DataFrame(list(db.movies.find({}, { "genres": 1 }))) \
    .dropna() \
    .merge(corresp) \
    .drop(columns = ["_id"])
df_genres
On doit maintenant décomposer la colonne genres pour avoir un genre par ligne. En ajustant le code vu précédemment en cours, on arrive à ceci.
film_genres = pandas.concat([pandas.DataFrame(g).assign(idFilm = i) for (i, g) in zip(df_genres.idFilm, df_genres.genres) if g])
film_genres.columns = ["genre", "idFilm"]
film_genres
Au final, en réalisant une jointure avec le DataFrame précédent, on arrive à créer la table voulue.
film_genre = pandas.merge(film_genres, genres).drop(columns = "genre").sort_values(by = "idFilm")
film_genre
On l’intègre elle aussi dans la BD SQLite.
film_genre.to_sql('film_genre', movies, index = False)
Vous pouvez ouvrir votre base de données avec DBeaver (par exemple) pour voir le contenu de celle-ci. Il devrait s’y trouver les 2 tables précédemment exportées.
cast) de la même façon que les genres, ainsi que les pays (champs countries), les directeurs (directors), les scénaristes (writers) et les langues (languages) ;rating intégrant les valeurs du champs rated ;type ;awards doit devenir 3 colonnes de la table Film à produire au final (wins, nominations et text) ;imdb intégrant les informations du champs imdb et donc l’identifiant du film concerné ;tomatoes.