Gratulacje, @name! To ostatnia lekcja Modułu 4. Darwin tutaj po raz ostatni w tym module!
Poznałeś/aś SQL - relacyjne bazy z tabelami, wierszami, kolumnami i sztywnymi schematami. Teraz czas na NoSQL - elastyczne bazy dokumentowe bez sztywnej struktury! 🍃📄
Analogia Safari: SQL to jak katalog z szufladkami i kartotekami - wszystko ma swoje miejsce. MongoDB to notatnik terenowy - zapisujesz obserwacje w dowolnej formie, bez sztywnego formatu! 📓🦁
NoSQL (Not Only SQL) to rodzina baz danych, które nie używają relacyjnego modelu tabel.
W tej lekcji: MongoDB - najpopularniejsza baza dokumentowa!
MongoDB przechowuje dane jako dokumenty BSON (Binary JSON):
1{
2 "_id": ObjectId("507f1f77bcf86cd799439011"),
3 "scientific_name": "Panthera leo",
4 "common_name": "Lew",
5 "population": 120,
6 "habitat": "sawanna",
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}Cechy:
| Koncepcja | SQL | MongoDB | |-----------|-----|---------| | Baza danych | Database | Database | | Tabela | Table | Collection | | Wiersz | Row | Document | | Kolumna | Column | Field | | Klucz główny | Primary Key |
_id (automatyczny) |
| JOIN | JOIN | Embedded docs / $lookup |
| Schemat | Sztywny | Elastyczny |PyMongo to oficjalny driver MongoDB dla Pythona.
1pip install pymongoUwaga: Potrzebujesz uruchomionego serwera MongoDB:
1from pymongo import MongoClient
2
3# Połączenie lokalne
4client = MongoClient("mongodb://localhost:27017/")
5
6# Lub MongoDB Atlas (cloud)
7# client = MongoClient("mongodb+srv://username:password@cluster.mongodb.net/")
8
9# Wybierz bazę danych
10db = client["safari_database"]
11
12# Wybierz kolekcję (jak tabela w SQL)
13species_collection = db["species"]1from pymongo import MongoClient
2from datetime import datetime
3
4client = MongoClient("mongodb://localhost:27017/")
5db = client["safari_database"]
6species = db["species"]
7
8# Dodaj jeden dokument
9lion = {
10 "scientific_name": "Panthera leo",
11 "common_name": "Lew",
12 "population": 120,
13 "habitat": "sawanna",
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"Dodano dokument ID: {result.inserted_id}")
25
26# Dodaj wiele dokumentów
27many_species = [
28 {
29 "scientific_name": "Loxodonta africana",
30 "common_name": "Słoń",
31 "population": 450,
32 "endangered": True
33 },
34 {
35 "scientific_name": "Gorilla gorilla",
36 "common_name": "Goryl",
37 "population": 230,
38 "endangered": True
39 }
40]
41
42result = species.insert_many(many_species)
43print(f"Dodano {len(result.inserted_ids)} dokumentów")1# Wszystkie dokumenty
2all_species = species.find()
3for doc in all_species:
4 print(doc["common_name"], doc["population"])
5
6# Jeden dokument
7lion = species.find_one({"common_name": "Lew"})
8print(lion)
9
10# Filtrowanie - WHERE
11endangered = species.find({"endangered": True})
12savanna = species.find({"habitat": "sawanna"})
13
14# Wiele warunków (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": "sawanna"},
24 {"habitat": "las"}
25 ]
26})
27
28# Operatory porównania
29# $gt - greater than (>)
30# $gte - greater than or equal (>=)
31# $lt - less than (<)
32# $lte - less than or equal (<=)
33# $ne - not equal (!=)
34# $in - in list
35
36large_population = species.find({"population": {"$gte": 200}})
37specific_habitats = species.find({"habitat": {"$in": ["sawanna", "las"]}})
38
39# Sortowanie
40sorted_species = species.find().sort("population", -1) # -1 = descending
41
42# Limit
43top_5 = species.find().limit(5)
44
45# Projekcja - wybierz tylko niektóre pola
46names_only = species.find({}, {"common_name": 1, "population": 1, "_id": 0})
47
48# Count
49count = species.count_documents({})
50endangered_count = species.count_documents({"endangered": True})1# Zaktualizuj jeden dokument
2species.update_one(
3 {"common_name": "Lew"}, # Filtr
4 {"$set": {"population": 125}} # Aktualizacja
5)
6
7# Zaktualizuj wiele dokumentów
8species.update_many(
9 {"habitat": "sawanna"},
10 {"$set": {"endangered": True}}
11)
12
13# Operatory aktualizacji:
14# $set - ustaw wartość
15# $inc - inkrementuj
16# $push - dodaj do tablicy
17# $pull - usuń z tablicy
18
19# Przykłady
20species.update_one(
21 {"common_name": "Lew"},
22 {
23 "$inc": {"population": 5}, # Zwiększ o 5
24 "$push": { # Dodaj obserwację
25 "observations": {
26 "date": "2024-01-25",
27 "location": "Ngorongoro",
28 "count": 10
29 }
30 }
31 }
32)1# Usuń jeden dokument
2species.delete_one({"common_name": "Test Species"})
3
4# Usuń wiele dokumentów
5species.delete_many({"population": 0})
6
7# Usuń wszystkie (OSTROŻNIE!)
8# species.delete_many({})1from pymongo import MongoClient
2from datetime import datetime
3from typing import List, Dict, Optional
4
5class SafariMongoDB:
6 """Zarządzanie bazą danych Safari w 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 """Dodaj gatunek"""
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 """Pobierz gatunek po 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 """Pobierz gatunek po nazwie"""
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 """Lista gatunków z filtrami"""
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 """Zaktualizuj gatunek"""
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 """Usuń gatunek"""
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 """Dodaj obserwację do gatunku"""
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 """Pobierz obserwacje gatunku"""
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 # === STATISTICS ===
102
103 def get_statistics(self) -> Dict:
104 """Statystyki bazy"""
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 """Zamknij połączenie"""
126 self.client.close()
127
128
129# === DEMONSTRACJA ===
130
131print("=== SAFARI MONGODB SYSTEM ===\n")
132
133db = SafariMongoDB()
134
135# 1. Dodaj gatunki
136print("1. Dodawanie gatunków...")
137lion_id = db.create_species(
138 "Panthera leo", "Lew", 120, "sawanna", True,
139 tags=["carnivore", "big cat", "africa"]
140)
141elephant_id = db.create_species("Loxodonta africana", "Słoń", 450, "sawanna", True)
142gorilla_id = db.create_species("Gorilla gorilla", "Goryl", 230, "las", True)
143
144print(f" Dodano 3 gatunki")
145
146# 2. Pobierz gatunek
147print("\n2. Pobieranie gatunku...")
148lion = db.get_species_by_name("Lew")
149print(f" {lion['common_name']}: {lion['population']} osobników")
150print(f" Tagi: {', '.join(lion.get('tags', []))}")
151
152# 3. Lista zagrożonych
153print("\n3. Lista zagrożonych gatunków...")
154endangered = db.list_species(endangered=True)
155for species in endangered:
156 print(f" - {species['common_name']}: {species['population']}")
157
158# 4. Aktualizacja
159print("\n4. Aktualizacja populacji...")
160db.update_species("Lew", population=125)
161lion = db.get_species_by_name("Lew")
162print(f" Nowa populacja: {lion['population']}")
163
164# 5. Dodaj obserwacje (zagnieżdżone w dokumencie!)
165print("\n5. Dodawanie obserwacji...")
166db.add_observation("Lew", "2024-01-15", "Serengeti", 12, "Pride with 2 cubs")
167db.add_observation("Lew", "2024-01-20", "Masai Mara", 8, "Male coalition")
168
169# 6. Pobierz obserwacje
170print("\n6. Obserwacje lwa...")
171observations = db.get_observations("Lew")
172for obs in observations:
173 print(f" - {obs['date']}: {obs['count']}x @ {obs['location']}")
174
175# 7. Statystyki
176print("\n7. Statystyki bazy...")
177stats = db.get_statistics()
178print(f" Łącznie gatunków: {stats['total_species']}")
179print(f" Zagrożone: {stats['endangered_count']}")
180print(" Według siedliska:")
181for habitat_stat in stats['by_habitat']:
182 print(f" - {habitat_stat['_id']}: {habitat_stat['count']} gatunków")
183
184db.close()
185print("\n✓ Demonstracja zakończona")Często: Używa się obu - SQL do danych transakcyjnych, MongoDB do logów, cache, sesji!
Gratulacje, @name! Ukończyłeś/aś Moduł 4: Przepływ Danych!
W tym module nauczyłeś/aś się:
Analogia Safari finalna: Teraz potrafisz zbierać dane (scraping), przesyłać je (HTTP), przechowywać (SQL/MongoDB) i udostępniać (REST API) - kompletny cykl życia danych w aplikacji Safari! 🌍📊🗄️
Jesteś gotowy/a na Moduł 5 i dalsze przygody z Darwinem! Do zobaczenia wkrótce! 🦁🐍✨