Dokumentová databáza MongoDB¶
Cieľom cvičenia je naučiť sa základom práce s dokumentovou databázou MongoDB. Na cvičení budete pracovať s dátami, ktoré popisujú hodnotenie reštaurácií vo formáte JSON. Jeden záznam bude vyzerať takto:
{
"address": {
"building": "1007",
"coord": [ -73.856077, 40.848447 ],
"street": "Morris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{ "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 },
{ "date": { "$date": 1378857600000 }, "grade": "A", "score": 6 },
{ "date": { "$date": 1358985600000 }, "grade": "A", "score": 10 },
{ "date": { "$date": 1322006400000 }, "grade": "A", "score": 9 },
{ "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 }
],
"name": "Morris Park Bake Shop",
"restaurant_id": "30075445"
}
Pre pripojenie a dopytovanie budete používať Python knižnicu pymongo.
!pip install pyOpenSSL==23.0.0
!pip install pymongo
import pymongo # nainštalujeme a naimportujeme knižnicu klienta
# pripojenie na databázový server bežiaci v prostredí DataLab
client = pymongo.MongoClient('mongodb://mongo:27017/', username='student', password='student')
db = client['datalab'] # pripojenie na databázu
db.list_collection_names() # zobrazíme všetky kolekcie v databáze
db.restaurants.estimated_document_count() # celkový počet dokumentov v kolekcii `restaurants`
Dopytovanie¶
Základné typy dopytov sa vykonávajú pomocou príkazu db.[kolekcia].find([dopyt]). Volanie vráti objekt kurzora, pomocou ktorého môžete zistiť počet dokumentov vo výsledku a prechádzať jednotlivé záznamy. Nasledujúci príkaz zobrazí reštaurácie v štvrti Manhattan:
result = db.restaurants.find({ "borough": "Manhattan" })
for doc in result.limit(5): # prejdeme iba prvých 5 dokumentov zahrnutých do výsledku
print(doc)
Ak chceme zistiť iba počet záznamov, môžeme použiť db.[kolekcia].count_documents([dopyt]).
db.restaurants.count_documents({ "borough": "Manhattan" })
V dopytoch sa môžete odkazovať aj na vnorené objekty, napr. na smerovacie číslo vnorené v objekte adresy:
db.restaurants.count_documents({ "address.zipcode": "10075" })
Odkazovanie cez . je možné použiť aj keď sú vnorené objekty v poli:
db.restaurants.count_documents({ "grades.grade": "B" }) # počet reštaurácií s hodnotením `B`
Okrem vyhľadávania podľa hodnôt môžete použiť porovnávanie pomocou príkazov menší $lt alebo väčší $gt. Nasledujúci dopyt vráti reštaurácie s hodnotením viac než 30:
db.restaurants.count_documents({ "grades.score": { "$gt": 30 } })
Zložitejšie dopyty sa vytvárajú zadaním viacerých vlastností v objekte dopytu (logická spojka a). Napr. nasledujúci dopyt vráti Talianske reštaurácie so smerovacím číslom 10075:
result = db.restaurants.find({ "cuisine": "Italian", "address.zipcode": "10075" })
for doc in result:
# dáta sú reprezentované ako objekty s ktorými sa pracuje ako s mapou
print(doc['name']) # vypíšeme názvy reštaurácií
Logická podmienka alebo sa zapisuje samostatným príkazom $or, nasledujúci príklad vráti reštaurácie ktoré ponúkajú talianske jedlá, alebo majú smerovacie číslo 10075:
db.restaurants.count_documents({ "$or": [ { "cuisine": "Italian" }, { "address.zipcode": "10075" } ] })
Výsledky je možné usporiadať príkazom sort. Nasledujúci príkaz zoradí všetky objekty najprv podľa štvrti vzostupne a potom podľa čísla zostupne:
result = db.restaurants.find().sort([("borough", 1), ("address.zipcode", -1)])
for doc in result.limit(5): # pre prvých 5 dokumentov vo výsledku
print(doc['borough'], doc['address']['zipcode']) # vypíšeme názov štvrte a smerovacie číslo v adrese
Agregácie¶
Pomocou agregácií môžeme vytvoriť zložité dopyty, ktoré dáta filtrujú a zoskupujú podľa zadaných kritérií. Pri agregáciách je dopyt rozdelený na postupnosť základných operácií, ktoré sú potom aplikované postupne na objekty v kolekcii. Pri spracovaní môžu byť dáta filtrované (príkaz $match), transformované (príkaz $project), usporiadané (príkaz $sort) a agregované (príkaz $group).
Základný formát príkazu pre agregovanie dát je db.[kolekcia].aggregate( [ [príkaz1], [príkaz2], [...] ] ).
Napr. nasledujúci príkaz najprv odfiltruje iba tie objekty z kolekcie, ktoré sú vo štvrti Queens a varia Brazílsku kuchyňu, potom ich zoskupí podľa smerovacieho čísla, a nakoniec vypočíta koľko je reštaurácií pre každé smerovacie číslo:
result = db.restaurants.aggregate([
{ "$match": { "borough": "Queens", "cuisine": "Brazilian" } },
{ "$group": { "_id": "$address.zipcode" , "count": { "$sum": 1 } } }
])
for count in result:
print(count)
V príkaze $group môžete namiesto jednoduchého počtu objektov v skupine určiť ďalšie agregačné funkcie ako napr. priemer $avg (v príklade je uvedená len časť dopytu s príkazom $group):
{ "$group": { "_id": "$address.zipcode" , "avg_grade": { "$avg": "$grades.score" } } }
Úloha 4.1¶
Spočítajte, koľko je Talianskych reštaurácií na Manhattane s hodnotením B.
Úloha 4.2¶
Usporiadajte Brazílske reštaurácie v Queens podľa smerovacieho čísla zostupne a podľa názvu vzostupne.
Úloha 4.3¶
Prečítajte si dokumentáciu k agregačnému príkazu $unwind. Spočítajte celkové skóre pre všetky reštaurácie.
Úloha 4.4¶
Prečítajte si dokumentáciu k agregačnému príkazu $unwind. Spočítajte celkové skóre pre reštaurácie v Queens a zoskupte ich podľa hodnotenia grade.
Úloha 4.5¶
Prečítajte si dokumentáciu k agregačnému príkazu $unwind. Spočítajte celkové skóre pre reštaurácie v Queens, zoskupte ich podľa smerovacieho čísla a vráťte 10 smerovacích čísel s najväčším skóre.
Úloha 4.6¶
Vytvorte si geografický index pre vlastnosť address.coord príkazom db.restaurants.create_index(["address.coord", "2d"]) . Prečítajte si dokumentáciu k príkazu $geoNear. Vráťte 10 najbližších reštaurácií v okolí geografickej polohy [ -73.856077, 40.848447 ].