Cours Python · Module 4: Données, API et bases de données

NoSQL - MongoDB dans la jungle

9 min de lecture
Dans cette leçon7

Félicitations ! C'est la dernière leçon du module 4. Ici Darwin, pour la dernière fois dans ce module !

Tu as appris SQL - les bases de données relationnelles avec des tables, des lignes, des colonnes et des schémas rigides. Il est maintenant temps de parler de NoSQL - des bases de données de documents flexibles, sans structure rigide !

Analogie safari : SQL est comme un catalogue avec des tiroirs et des fiches - tout a sa place. MongoDB est un carnet de terrain - tu y consignes tes observations sous n'importe quelle forme, sans format rigide !

Qu'est-ce que NoSQL ?

NoSQL (Not Only SQL) est une famille de bases de données qui n'utilisent pas le modèle relationnel des tables.

Types de bases de données NoSQL :

  1. Documents (MongoDB, CouchDB) - documents de type JSON
  2. Clé-valeur (Redis, DynamoDB) - simples paires clé: valeur
  3. En colonnes (Cassandra, HBase) - colonnes au lieu de lignes
  4. Graphes (Neo4j, ArangoDB) - nœuds et relations

Dans cette leçon : MongoDB - la base de données de documents la plus populaire !

MongoDB - base de données de documents

MongoDB stocke les données sous forme de documents BSON (Binary JSON) :

1{
2  "_id": ObjectId("507f1f77bcf86cd799439011"),
3  "scientific_name": "Panthera leo",
4  "common_name": "Lion",
5  "population": 120,
6  "habitat": "savanna",
7  "endangered": true,
8  "observations": [
9    {"date": "2024-01-15", "location": "Serengeti", "count": 12},
10    {"date": "2024-01-20", "location": "Masai Mara", "count": 8}
11  ],
12  "tags": ["carnivore", "big cat", "africa"]
13}

Caractéristiques :

  • Schéma flexible - chaque document peut avoir des champs différents
  • Structures imbriquées - objets et tableaux
  • Scalabilité horizontale
  • Rapide pour certains cas d'usage

MongoDB vs SQL

ConceptSQLMongoDB
Base de donnéesDatabaseDatabase
TableTableCollection
LigneRowDocument
ColonneColumnField
Clé primairePrimary Key_id (automatique)
JOINJOINDocuments imbriqués / $lookup
SchémaRigideFlexible

PyMongo - MongoDB en Python

PyMongo est le driver officiel de MongoDB pour Python.

Installation

1pip install pymongo

Remarque : tu as besoin d'un serveur MongoDB en cours d'exécution :

Se connecter à MongoDB

1from pymongo import MongoClient
2
3# Connexion locale
4client = MongoClient("mongodb://localhost:27017/")
5
6# Ou MongoDB Atlas (cloud)
7# client = MongoClient("mongodb+srv://username:password@cluster.mongodb.net/")
8
9# Sélectionner la base de données
10db = client["safari_database"]
11
12# Sélectionner la collection (comme une table en SQL)
13species_collection = db["species"]

CRUD dans MongoDB

CREATE - ajouter des documents

1from pymongo import MongoClient
2from datetime import datetime
3
4client = MongoClient("mongodb://localhost:27017/")
5db = client["safari_database"]
6species = db["species"]
7
8# Ajouter un seul document
9lion = {
10    "scientific_name": "Panthera leo",
11    "common_name": "Lion",
12    "population": 120,
13    "habitat": "savanna",
14    "endangered": True,
15    "created_at": datetime.utcnow(),
16    "observations": [
17        {"date": "2024-01-15", "location": "Serengeti", "count": 12},
18        {"date": "2024-01-20", "location": "Masai Mara", "count": 8}
19    ],
20    "tags": ["carnivore", "big cat", "africa"]
21}
22
23result = species.insert_one(lion)
24print(f"ID du document ajouté : {result.inserted_id}")
25
26# Ajouter plusieurs documents
27many_species = [
28    {
29        "scientific_name": "Loxodonta africana",
30        "common_name": "Elephant",
31        "population": 450,
32        "endangered": True
33    },
34    {
35        "scientific_name": "Gorilla gorilla",
36        "common_name": "Gorilla",
37        "population": 230,
38        "endangered": True
39    }
40]
41
42result = species.insert_many(many_species)
43print(f"{len(result.inserted_ids)} documents ajoutés")

READ - récupérer des documents

1# Tous les documents
2all_species = species.find()
3for doc in all_species:
4    print(doc["common_name"], doc["population"])
5
6# Un seul document
7lion = species.find_one({"common_name": "Lion"})
8print(lion)
9
10# Filtrage - WHERE
11endangered = species.find({"endangered": True})
12savanna = species.find({"habitat": "savanna"})
13
14# Plusieurs conditions (AND)
15results = species.find({
16    "endangered": True,
17    "population": {"$gt": 100}  # $gt = greater than (>)
18})
19
20# OR
21results = species.find({
22    "$or": [
23        {"habitat": "savanna"},
24        {"habitat": "forest"}
25    ]
26})
27
28# Opérateurs de comparaison
29# $gt - supérieur à (>)
30# $gte - supérieur ou égal (>=)
31# $lt - inférieur à (<)
32# $lte - inférieur ou égal (<=)
33# $ne - différent de (!=)
34# $in - dans une liste
35
36large_population = species.find({"population": {"$gte": 200}})
37specific_habitats = species.find({"habitat": {"$in": ["savanna", "forest"]}})
38
39# Tri
40sorted_species = species.find().sort("population", -1)  # -1 = décroissant
41
42# Limite
43top_5 = species.find().limit(5)
44
45# Projection - sélectionner seulement certains champs
46names_only = species.find({}, {"common_name": 1, "population": 1, "_id": 0})
47
48# Compte
49count = species.count_documents({})
50endangered_count = species.count_documents({"endangered": True})

UPDATE - mettre à jour des documents

1# Mettre à jour un seul document
2species.update_one(
3    {"common_name": "Lion"},  # Filtre
4    {"$set": {"population": 125}}  # Mise à jour
5)
6
7# Mettre à jour plusieurs documents
8species.update_many(
9    {"habitat": "savanna"},
10    {"$set": {"endangered": True}}
11)
12
13# Opérateurs de mise à jour :
14# $set - définir une valeur
15# $inc - incrémenter
16# $push - ajouter à un tableau
17# $pull - retirer d'un tableau
18
19# Exemples
20species.update_one(
21    {"common_name": "Lion"},
22    {
23        "$inc": {"population": 5},  # Augmenter de 5
24        "$push": {  # Ajouter une observation
25            "observations": {
26                "date": "2024-01-25",
27                "location": "Ngorongoro",
28                "count": 10
29            }
30        }
31    }
32)

DELETE - supprimer des documents

1# Supprimer un seul document
2species.delete_one({"common_name": "Test Species"})
3
4# Supprimer plusieurs documents
5species.delete_many({"population": 0})
6
7# Tout supprimer (ATTENTION !)
8# species.delete_many({})

Exemple safari - système MongoDB complet

1from pymongo import MongoClient
2from datetime import datetime
3from typing import List, Dict, Optional
4
5class SafariMongoDB:
6    """Gestion de la base de données Safari dans MongoDB"""
7
8    def __init__(self, connection_string: str = "mongodb://localhost:27017/"):
9        self.client = MongoClient(connection_string)
10        self.db = self.client["safari_database"]
11        self.species = self.db["species"]
12
13    # === SPECIES ===
14
15    def create_species(self, scientific_name: str, common_name: str,
16                      population: int = 0, habitat: str = "",
17                      endangered: bool = False, tags: List[str] = None) -> str:
18        """Ajouter une espèce"""
19        species_doc = {
20            "scientific_name": scientific_name,
21            "common_name": common_name,
22            "population": population,
23            "habitat": habitat,
24            "endangered": endangered,
25            "tags": tags or [],
26            "observations": [],
27            "created_at": datetime.utcnow(),
28            "updated_at": datetime.utcnow()
29        }
30
31        result = self.species.insert_one(species_doc)
32        return str(result.inserted_id)
33
34    def get_species(self, species_id: str) -> Optional[Dict]:
35        """Récupérer une espèce par ID"""
36        from bson import ObjectId
37        return self.species.find_one({"_id": ObjectId(species_id)})
38
39    def get_species_by_name(self, common_name: str) -> Optional[Dict]:
40        """Récupérer une espèce par nom"""
41        return self.species.find_one({"common_name": common_name})
42
43    def list_species(self, endangered: Optional[bool] = None,
44                    habitat: Optional[str] = None,
45                    min_population: int = 0) -> List[Dict]:
46        """Lister les espèces avec des filtres"""
47        query = {"population": {"$gte": min_population}}
48
49        if endangered is not None:
50            query["endangered"] = endangered
51        if habitat:
52            query["habitat"] = habitat
53
54        return list(self.species.find(query).sort("common_name", 1))
55
56    def update_species(self, common_name: str, **kwargs) -> bool:
57        """Mettre à jour une espèce"""
58        if not kwargs:
59            return False
60
61        kwargs["updated_at"] = datetime.utcnow()
62
63        result = self.species.update_one(
64            {"common_name": common_name},
65            {"$set": kwargs}
66        )
67        return result.modified_count > 0
68
69    def delete_species(self, common_name: str) -> bool:
70        """Supprimer une espèce"""
71        result = self.species.delete_one({"common_name": common_name})
72        return result.deleted_count > 0
73
74    # === OBSERVATIONS ===
75
76    def add_observation(self, common_name: str, observation_date: str,
77                       location: str, count: int, notes: str = "") -> bool:
78        """Ajouter une observation à une espèce"""
79        observation = {
80            "date": observation_date,
81            "location": location,
82            "count": count,
83            "notes": notes,
84            "recorded_at": datetime.utcnow()
85        }
86
87        result = self.species.update_one(
88            {"common_name": common_name},
89            {"$push": {"observations": observation}}
90        )
91        return result.modified_count > 0
92
93    def get_observations(self, common_name: str) -> List[Dict]:
94        """Récupérer les observations d'une espèce"""
95        species = self.species.find_one(
96            {"common_name": common_name},
97            {"observations": 1, "_id": 0}
98        )
99        return species.get("observations", []) if species else []
100
101    # === STATISTIQUES ===
102
103    def get_statistics(self) -> Dict:
104        """Statistiques de la base de données"""
105        pipeline = [
106            {
107                "$group": {
108                    "_id": "$habitat",
109                    "count": {"$sum": 1},
110                    "total_population": {"$sum": "$population"}
111                }
112            },
113            {"$sort": {"count": -1}}
114        ]
115
116        by_habitat = list(self.species.aggregate(pipeline))
117
118        return {
119            "total_species": self.species.count_documents({}),
120            "endangered_count": self.species.count_documents({"endangered": True}),
121            "by_habitat": by_habitat
122        }
123
124    def close(self):
125        """Fermer la connexion"""
126        self.client.close()
127
128
129# === DÉMONSTRATION ===
130
131print("=== SYSTÈME MONGODB SAFARI ===\n")
132
133db = SafariMongoDB()
134
135# 1. Ajouter des espèces
136print("1. Ajout des espèces...")
137lion_id = db.create_species(
138    "Panthera leo", "Lion", 120, "savanna", True,
139    tags=["carnivore", "big cat", "africa"]
140)
141elephant_id = db.create_species("Loxodonta africana", "Elephant", 450, "savanna", True)
142gorilla_id = db.create_species("Gorilla gorilla", "Gorilla", 230, "forest", True)
143
144print(f"   3 espèces ajoutées")
145
146# 2. Récupérer une espèce
147print("\n2. Récupération d'une espèce...")
148lion = db.get_species_by_name("Lion")
149print(f"   {lion['common_name']} : {lion['population']} individus")
150print(f"   Tags : {', '.join(lion.get('tags', []))}")
151
152# 3. Lister les menacées
153print("\n3. Liste des espèces menacées...")
154endangered = db.list_species(endangered=True)
155for species in endangered:
156    print(f"   - {species['common_name']} : {species['population']}")
157
158# 4. Mise à jour
159print("\n4. Mise à jour de la population...")
160db.update_species("Lion", population=125)
161lion = db.get_species_by_name("Lion")
162print(f"   Nouvelle population : {lion['population']}")
163
164# 5. Ajouter des observations (imbriquées dans le document !)
165print("\n5. Ajout des observations...")
166db.add_observation("Lion", "2024-01-15", "Serengeti", 12, "Pride with 2 cubs")
167db.add_observation("Lion", "2024-01-20", "Masai Mara", 8, "Male coalition")
168
169# 6. Récupérer les observations
170print("\n6. Observations du lion...")
171observations = db.get_observations("Lion")
172for obs in observations:
173    print(f"   - {obs['date']} : {obs['count']}x @ {obs['location']}")
174
175# 7. Statistiques
176print("\n7. Statistiques de la base...")
177stats = db.get_statistics()
178print(f"   Nombre total d'espèces : {stats['total_species']}")
179print(f"   Menacées : {stats['endangered_count']}")
180print("   Par habitat :")
181for habitat_stat in stats['by_habitat']:
182    print(f"     - {habitat_stat['_id']} : {habitat_stat['count']} espèces")
183
184db.close()
185print("\nDémonstration terminée")

Quand utiliser MongoDB vs SQL ?

Utilise MongoDB quand :

  • Schéma flexible (les données varient d'un enregistrement à l'autre)
  • Structures imbriquées (données de type JSON)
  • Prototypage rapide (pas de migrations de schéma)
  • Scalabilité horizontale
  • Données non relationnelles

Utilise SQL quand :

  • Schéma rigide (données structurées)
  • Relations complexes (nombreux JOIN)
  • Les transactions ACID sont critiques
  • Reporting et analyses
  • Applications métier traditionnelles

Souvent : les deux sont utilisés ensemble - SQL pour les données transactionnelles, MongoDB pour les logs, le cache, les sessions !

Résumé - Module 4 terminé !

Félicitations ! Tu as terminé le module 4 : Le flux de données !

Dans ce module, tu as appris :

Leçon 1 : Les formats de données

  • JSON, CSV, XML, YAML
  • Sérialisation et désérialisation
  • Conversion entre formats

Leçon 2 : HTTP et requests

  • Le protocole HTTP (GET, POST, PUT, DELETE)
  • Les codes de statut (200, 404, 500)
  • La bibliothèque requests
  • Gestion des erreurs, timeouts, sessions

Leçon 3 : API REST

  • Les principes REST (ressources, méthodes, sans état)
  • Les endpoints RESTful
  • Pagination, filtrage, tri
  • Les bonnes pratiques d'API

Leçon 4 : Web scraping

  • BeautifulSoup4 (find, select)
  • L'extraction de données depuis le HTML
  • Selenium pour le JavaScript
  • L'éthique du scraping

Leçon 5 : SQL et SQLite

  • Les bases de données relationnelles
  • SQL : CREATE, INSERT, SELECT, UPDATE, DELETE
  • JOIN, WHERE, ORDER BY
  • Python sqlite3

Leçon 6 : SQLAlchemy ORM

  • Les modèles et classes Python
  • CRUD avec l'ORM
  • Les relations (One-to-Many)
  • L'API de requête

Leçon 7 : NoSQL MongoDB

  • La base de données de documents
  • PyMongo
  • Le schéma flexible
  • MongoDB vs SQL

Analogie safari finale : Tu sais maintenant collecter des données (scraping), les transmettre (HTTP), les stocker (SQL/MongoDB) et les partager (API REST) - un cycle de vie complet des données dans une application Safari !

Tu es prêt pour le module 5 et de nouvelles aventures avec Darwin ! À très bientôt !

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. La principale différence entre NoSQL et SQL :

  2. 2. Dans MongoDB, les données sont stockées sous forme de :

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Insère le document animal = {'name': 'Tygrys', 'weight': 200} dans la collection 'animals'

  • Éditeur de code

    Trouve tous les documents où weight > 100

  • Clique dans l'ordre

    Clique les éléments dans le bon ordre :

  • Tri vertical

    Remets dans l'ordre les étapes de construction d'un client d'API REST :

  • Éditeur de code

    Crée une classe SafariAPIClient avec les méthodes : get_animals(), get_animal_by_id()

  • Éditeur de code

    Crée une classe DatabaseManager avec les méthodes : save_animal(), get_all_animals(), search()

  • Éditeur de code

    Crée un Safari Data Hub complet avec un menu, la récupération depuis l'API, l'enregistrement en base de données et la recherche

  • Tri vertical

    Remets dans l'ordre les étapes de travail avec SQLAlchemy :

  • Tri vertical

    Remets dans l'ordre les étapes du cycle de vie complet des données :

Articles utiles