Databáze, které nejsou založené na relačním datovém modelu (tj. nejsou relační). Dovedou zpracovávat obrovské množství dat v reálném čase a zpravidla jsou distribuované a otevřené. V databázích se obvykle neřeší transakční zpracování (ACID), ale data jsou snadno dostupná vždy i v částečně konzistentním stavu (BASE).
Přibližně od roku 2008 ke dnešnímu dni existují stovky NoSQL databázových systémů pro různé způsoby využití. Během tohoto času došlo k jejich základnímu rozdělení, minimálně na tyto 4 základní kategorie:
Seznam aktuálních NoSQL databází naleznete na webu nosql-database.org.
Za vývojem databáze MongoDB je společnost MongoDB Inc., která také stojí za vývojem ovladačů pro připojení k databázi. Databáze MongoDB je šířena s otevřený zdojovým kódem a je multiplatformní, kterou si můžeme stáhnout z adresy https://www.mongodb.com/try/download/community.
Stáhneme si odpovídající verzi Community Serveru pro naši platformu (pro MS Windows 10 x64 vybereme verzi Windows 64-bit) v podobě zip archivu,
který rozbalíme do adresáře, kam máme právo zápisu. Pro komunikaci s databází potřebuje také nějakého klienta, na začátek si vystačíme se MongoDB Shellem,
který je dostupný ke stažení na adrese https://www.mongodb.com/products/shell.
Oba stažené archivy rozbalíme do stejného místa, tj. adresář bin
bude obsahovat všechny spustitelné programy. Pro komfortnější práci
je vhodné zahrnout cestu do adresáře bin
do systémové (nebo uživatelské) proměnné PATH
.
Pokud spuštění MongoDB serveru hlásí chyby ohledně chybějících knihoven, je potřeba instalovat balík Microsoft Visual C++ 2015-2022 Redistributable,
který je dostupný v adresáři bin
MongoDB serveru v podobě souboru vc_redist.x64.exe
.
Datové úložiště je nutné založit na disku, kde máme právo zápisu, vhodným kandidátem je lokální disk D, kde založíme odpovídající adresář pro úložiště,
např. D:\Database\MongoDB\data
a adresář D:\Database\MongoDB\log
pro logy. Zjištění, jakou verzi MongoDB používáme, provedeme příkazem:
mongod.exe --version
a systém odpoví
db version v6.0.4 Build Info: { "version": "6.0.4", "gitVersion": "44ff59461c1353638a71e710f385a566bcd2f547", "modules": [], "allocator": "tcmalloc", "environment": { "distmod": "windows", "distarch": "x86_64", "target_arch": "x86_64" } }
MongoDB databázový systém je možné používat jak na jediném uzlu, tak distribuovaně na několika uzlech. V případě lokální databáze systém nabízí zjednodušený způsob založení a konfigurace uzlu
mongod.exe --dbpath "D:\\Database\\MongoDB\\data" --port 8000 mongod.exe --dbpath "D:\\Database\\MongoDB\\data" --port 8000 --logpath "D:\\Database\\MongoDB\\log\\mongod.log" mongod.exe --dbpath "D:\\Database\\MongoDB\\data" --port 8000 --logpath "D:\\Database\\MongoDB\\log\\mongod.log" --logappend mongod.exe --dbpath "D:\\Database\\MongoDB\\data" --port 8000 --logpath "D:\\Database\\MongoDB\\log\\mongod.log" --logappend --directoryperdb
a jeho spuštění. Uzel komunikuje na portu 8000. Činnost uzlu ukončíme buď kombinací kláves CTRL+C
nebo příkazem db.shutdownServer()
nad databází admin
z MongoDB Shellu. Protože při vytváření databáze na lokálním uzlu lze zadat spoustu rozličných parametrů, je výhodnější vše uložit
do konfiguračního souboru, např. mongod.conf
(viz níže), který umístíme do adresáře D:\Database\MongoDB
a databázi založit či se připojit
již k existující provedeme příkazem:
mongod.exe --config "D:\\Database\\MongoDB\\mongod.conf"
Po shození lokálního uzlu je možné smazat obsah adresáře D:\Database\MongoDB\data
.
Konfigurační soubor je psán ve skriptovacím jazyce YAML, který nepodporuje tabulátor (odsazení je nutné vyjádřit mezerami) a také nelze soubor uložit v UTF-8 kódování, ale jen v ASCII. Náš konfigurační soubor může vypadat takto:
storage: dbPath: "D:\\Database\\MongoDB\\data" directoryPerDB: true systemLog: destination: file path: "D:\\Database\\MongoDB\\log\\mongod.log" logAppend: true net: port: 8000
Dokumenty ukládané do MongoDB databáze jsou psány ve formátu JSON a fyzicky jsou ukládány ve formátu BSON, což je binární podoba JSON doplněná o datové typy.
Zjednodušeně řečeno, dokument (JSON objekt) je uzavřen do složených {}
závorek, jehož obsahem je zpravidla množina dvojic tvaru
název: hodnota
Hodnotou může být:
true
, false
, null
, číslo nebo řetězec uzavřený v uvozovkách:
{ "nazev": "MongoDB" }
{ "predmet": { "zkratka": "KIV/DB2", "nazev": "Databázové systémy 2", "kredity": 6 } }
[]
závorkách:
{ "dokumenty": [ { "nazev": "MongoDB" }, { "predmet": { "zkratka": "KIV/DB2", "nazev": "Databázové systémy 2", "kredity": 6 } } ] }
Dvojice v dokumentu jsou oddělené čárkou. Každý takový JSON dokument, uložený v MongoDB databázi, obdrží unikátní číslo, dostupné v atributu _id
.
Jednoduchou konzolovou klientskou aplikací poslouží program MongoDB Shell. Tímto programem je možné se připojit k běžícímu databázovému uzlu,
vytvářet a používat databáze a pracovat s jejich daty. Data (dokumenty) se obvykle ukládají do tzv. kolekcí (collections), které vzdáleně připomínají tabulku.
MongoDB Shell se spouští programem mongosh.exe
. K databázi umístěné na lokální uzlu se připojíme takto:
mongosh.exe --host localhost --port 8000
Objeví se verze MongoDB s informací, že jsme se připojili k databázi test
.
Current Mongosh Log ID: 6405b6f595156a167d66cb4f Connecting to: mongodb://localhost:8000/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.8.0 Using MongoDB: 6.0.4 Using Mongosh: 1.8.0... >
Pokud nechceme lokálně instalovat MongoDB server, můžeme využít serveru společnosti MongoDB Inc. a připojit se k němu prostřednictvím MongoDB Web Shellu. Tento nástroj je využíván v dokumentaci produktu, kde si uživatelé mohou naživo vyzkoušet jednotlivé příkazy.
MongoDB Web Shell spustíme na adrese mws.mongodb.com, kde musíme následně kdekoliv kliknout myší. Tím se pro aktuální spojení vytvoří nový server a budeme k němu následně připojeni. S touto webovou aplikací jsou spojena následující tlačítka:
Další příkazy ukazují, jaké databáze jsou k dispozici a jak zvolenou databázi vybrat, resp. vytvořit a zjistit, jakou databázi právě používám:
> show dbs admin 0.000GB config 0.000GB local 0.000GB > use local switched to db local > use db2 switched to db db2 > db db2
Dokumenty se do databáze vkládají prostřednictvím tzv. kolekcí (collections). Vložit lze buď jeden dokument příkazem insertOne()
nebo více dokumentů najednou příkazem insertMany()
. Vkládání dokumentů do kolekce predmety
pak vypadá takto:
> db.predmety.insertOne( json_doc ) > db.predmety.insertMany( [json_doc1, json_doc2, ...] )
I když definice formátu JSON vyžaduje, aby v dvojici nazev: hodnota
byl uzavřen v uvozovkách, MongoDB tento požadavek nevyžaduje
a dané uvozovky doplňuje automaticky. Každý vkládaný dokument obdrží atribut "_id"
s unikátní hodnotou. V archivu MongoDB data
najdete tři soubory, které dohromady do kolekce predmety
vkládají celkem 7 JSON dokumentů. Více detailů o vkládání
dokumentů se dočtete v manuálu MongoDB.
Zajímavostí je skutečnost, že samotný MongoDB Shell u názvů atributů každého dokumentu žádné uvozovky nezobrazuje.
Pokud chceme získat uložené dokumenty nebo je vyhledávat podle různých kritérií, potřebujeme znát, v jakých kolekcích jsou uložené. Výčet kolekcí dokumentů zíkáme příkazem:
> show collections
Získání všech dokumentů nebo pouze jediného dokumentu uložených v kolekci predmety
:
> db.predmety.find() > db.predmety.findOne()
odpovídají jednoduchým dotazům
SELECT * FROM predmety; SELECT * FROM predmety LIMIT 1;
Obě uvedené funkce mohou mít až tři nepovinné parametry ve tvaru JSON dokumentu. Pokud není daný parametr uveden, vždy je brán v potaz prázdný JSON dokument.
To znamená, že volání příkazu db.predmety.find()
je shodné jako db.predmety.find({}, {}, {})
.
Pokud chceme vyplnit, jen druhý a/nebo třetí parametr, je nezbytné uvést první (a druhý) parametr v podobě prázdného JSON dokumentu. Parametry funkcí mají tento význam:
Jak ukazovaly první dotazy, funkce
find()
i
findOne()
nabízí kompletní JSON dokumenty ve své odpovědi.
Pokud budeme chtít z dokumentu vracet jen některé atributy, je třeba dotaz doplnit o tzv. projekční dokument, ve kterém vyjmenujeme
1
,0
, nebo je neuvedeme (neplatí pro _id
).> db.predmety.find( {}, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1 } ) > db.predmety.find( {}, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } )
Kritéria výběru dokumentů se formulují ve tvaru JSON dokumentu. Podmínka jednoduché selekce, kdy vybíráme pouze aktivní předměty vypadá takto:
{ aktivni: true }
:
> db.predmety.find( { aktivni: true } ) > db.predmety.find( { aktivni: true }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } )
Další dotazy zjišťují, zda máme k dispozici předměty za požadovaný počet kreditů:
> db.predmety.find( { kredity: 4 }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } ) > db.predmety.find( { kredity: 5 }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } ) > db.predmety.find( { kredity: 6 }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } )
Pokud chceme definovat několik podmínek v konjunkci, sestavíme v argumentu funkce find()
dokument obsahující dvojice oddělené čárkou, které definují
naše kritéria, nebo použijeme operátor $and
. Např. chceme všechny aktivní předmety, za které student při jeho úspěšném absolvování obdrží právě 6 kreditů:
> db.predmety.find( { aktivni: true, kredity: 6 }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } ) > db.predmety.find( { $and: [ { aktivni: true }, { kredity: 6 } ] }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } )
Řešení podmínek v disjunkci je trochu komplikovanější. Je potřeba použít operátor $or
, na který aplikujeme podmínky, které jsou definovány v poli dokumentů. Změníme v podmínce požadovaný počet kreditů na 5:
> db.predmety.find( { $or: [ { aktivni: true }, { kredity: 5 } ] }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } )
Další operátory, jako např. porovnávání, najdete dostupné v dokumentaci. Vyzkoušejte následující dotazy.
> db.predmety.find( { aktivni: true, kredity: { $gte: 5 } }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } ) > db.predmety.find( { $or: [ { aktivni: true }, { kredity: { $gte: 5 } } ] }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } ) > db.predmety.find( { zkratka: { $in: [ "KIV/DB1", "KIV/DB2", "KIV/DB3" ] } }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } ) > db.predmety.find( { zkratka: { $regex: /KIV/ } }, { zkratka: 1, nazev: 1, kredity: 1, aktivni: 1, _id: 0 } )
Vyhledání předmětů, které nabízí zápis typu A
(povinný) řešíme stejně, jako kdyby hodnota atributu zapis
byla jen konstanta, ne pole hodnot. Níže uvedené dotazy budou zobrazovat celé nalezené JSON dokumenty:
> db.predmety.find( { zapis: "A" } ) > db.predmety.find( { zapis: [ "A" ] } ) > db.predmety.find( { zapis: [ "A", "C" ] } ) > db.predmety.find( { $and: [ { zapis: "A" }, { zapis: "C" } ] } )
Další jednoduchý typ dotazu hledá takové předměty, u kterých nejsou uvedeni učitelé:
> db.predmety.find( { ucitele: null } ) > db.predmety.find( { ucitele: { $exists: false } } )
Definice filtrační podmínky, která se týká atributu vnořeného dokumentu vyžaduje kvalifikovaný název vnořeného atributu uzavřeného v uvozovkách
nebo apostrofech. Stejná podmínka platí i v případě, že vnořený dokument je prvkem pole. Chceme získat všechny předměty, které vyučuje
Martin
:
> db.predmety.find( { "ucitele.jmeno": "Martin" } )
Více detailů o získávání dokumentů se dočtete v manuálu MongoDB.
Aktualizace dokumentu/ů lze v MongoDB databázi realizovat třemi různými příkazy. První dva z příkazů umí nastavit např. nové hodnoty vybraným atributům, a to buď jen pro první nalezený dokument, a nebo pro všechny dokumenty, které splňují filtrační podmínku. Z neaktivních předmětů uděláme aktivní a změníme jim počet kreditů:
> db.predmety.updateOne( { aktivni: false }, { $set: { aktivni: true, kredity: 6 } } ) > db.predmety.updateMany( { aktivni: false }, { $set: { aktivni: true, kredity: 6 } } )
Třetí příkaz pro aktualizaci dokumentů provede náhradu existujícího dokumentu za nový. Nahrazován bude jen první nalezený dokument, který vyhovuje filtrační podmínce. V našem případě vrátíme předmět KIV/DBH do původního stavu:
> db.predmety.replaceOne( { zkratka: "KIV/DBH" }, { zkratka: "KIV/DBH", nazev: "Databázové systémy pro humanitní obory", kredity: 5, aktivni: false, semestr: ["LS"] } )
Filtrační podmínka má stejný tvar, jako podmínka u výše zmiňovaného příkazu find()
. Více detailů o aktualizaci dokumentů se dočtete
v manuálu MongoDB.
Smazání dokumentu/ů lze v MongoDB databázi realizovat dvěma různými příkazy. První z příkazů smaže jen jeden dokument, který vyhovuje zadané filtrační podmínce.
Druhý příkaz maže všechny dokumenty, které vybírá zadáná filtrační podmínka. Opět zde platí, že filtrační podmínka má stejný tvar, jako podmínka u výše zmiňovaného
příkazu find()
.
> db.predmety.deleteOne( { aktivni: true } ) > db.predmety.deleteMany( { aktivni: true } ) > db.predmety.deleteMany( {} )
Dotazy v MongoDB databázi nemusí být formulovány jen funkcemi find()
a findOne()
, ale je možné pro složitější dotazy využít
funkci aggregate()
.
Tato funkce nabízí celou paletu možných dotazů, které jsou skládány do pole z jednotlivých fází
(stage),
u kterých záleží na pořadí - tvoří tzv. pipeline.
Pro formulaci agregovaných dotazů je nutné použít fázi
$group
,
která má povinný atribut _id
, jehož různé hodnoty určují počet skupin.
V agregovaném výrazu lze použít mimo jiné známé agregované funkce z jazyka SQL.
Představení a použití známých agregovaných dotazů ukzují následující dotazy:
db.predmety.aggregate( [ { $group: { _id: "$semestr", soucet: { $sum: "$kredity" }, minimum: { $min: "$kredity" }, maximum: { $max: "$kredity" }, prumer: { $avg: "$kredity" } } } ] ) db.predmety.aggregate( [ { $group: { _id: "$kredity", pocet: { $count: {} } } } ] )
Druhý dotaz počítá četnosti dokumentů, ve kterých je uvedena stejná hodnota atributu kredity
. Pokud bychom nechtěli pracovat se všemi dokumenty,
ale jen s některými, je nutné na začátek vložit fázi
$match
,
která poslouží jako filtrační podmínka, kterou známe z funkce find()
.
db.predmety.aggregate( [ { $match: { semestr: "ZS" } }, { $group: { _id: "$kredity", pocet: { $count: {} } } } ] )
Z jazyka SQL známe konstrukci ORDER BY
, která dovolila výsledek dotazu seřadit podle požadovaných sloupců v odpovídajícím směru.
Chceme-li výsledek dotazu funkce aggregate()
seřadit, vložíme do jeho zápisu fázi
$sort
.
db.predmety.aggregate( [ { $match: { semestr: "ZS" } }, { $group: { _id: "$kredity", pocet: { $count: {} } } }, { $sort: { pocet: -1, _id: 1} } ] )
Pokud ve funkci aggregate()
vložíme fázi $match
až za fázi $group
, tak to neznamená filtrační podmínku,
ale podmínku nad agregovaným výrazem, který odpovídá konstrukci HAVING
z jazyka SQL. Abychom mohli zobrazit nějaká data,
odstraníme první fázi $match
, tj. filtraci dokumentů.
db.predmety.aggregate( [ { $group: { _id: "$kredity", pocet: { $count: {} } } }, { $match: { pocet: { $gte: 2 } } }, { $sort: { pocet: -1, _id: 1} } ] )
Pokud chceme ve svém dotazu zobrazovat data ze dvou či více kolekcí současně, musíme opět použít funkci aggregate()
.
Než k tomu přistoupíme, musíme si vytvořit novou kolekci, která bude vhodná pro spojení s kolekcí predmety.
Poslouží nám k tomu agregovaný dotaz, který bude doplněn o dvě nové fáze.
db.predmety.aggregate( [ { $unwind: "$semestr" }, { $group: { _id: "$semestr", maximum: { $max: "$kredity" }, minimum: { $min: "$kredity" } } }, { $out: "semestr" } ] )
Ke spojování dvou kolekcí do jednoho dotazu použijeme fázi
$lookup
,
která bude procházet dokumenty jedné kolekce a k ní bude dohledávat odpovídající dokumenty kolekce druhé.
Obecně se předpokládá, že každému dokumentu procházené kolekce bude odpovídat více dokumentů druhé kolekce , tj. mezi kolekcemi platí
vztah typu 1:N, který známe z E-R-A modelů.
db.semestr.aggregate( [ { $lookup: { from: "predmety", localField: "_id", foreignField: "semestr", as: "predmety" } } ] )
Chceme-li pro spojované dokumenty obou kolekcí stanovit nějakou podmínku, musíme fázi $lookup
doplnit o atribut let
pro definici proměnné, obsahující hodnotu atributu dokumentu procházené kolekce a pole pipeline
, ve kterém stanovíme požadovanou podmínku.
V této podmínce se k výše definované proměnné přistupuje skrze "$$proměnná"
.
db.semestr.aggregate( [ { $lookup: { from: "predmety", localField: "_id", foreignField: "semestr", let: { max_kreditu: "$maximum" }, pipeline: [ { $match: { $expr: { $eq: [ "$kredity", "$$max_kreditu" ] } } } ], as: "predmety" } } ] )
Poslední dotaz, který si ukážeme, řeší zobrazení hodnoty, která byla spočítána/odvozena z několika atributů. Nejen k zobrzení této hodnoty použijeme fázi
$project
funkce aggeregate()
, funkce find()
a findOne()
to ve svém projektovém JSON dokumentu zapsat neumí.
db.semestr.aggregate( [ { $project: { maximum: 1, minimum: 1, diff: { $subtract: ['$maximum','$minimum'] } } } ] )
MongoDB Compass je multiplatformní grafické uživatelské prostředí pro práci s MongoDB databází, které plně nahrazuje méně komfortního klienta MongoDB Shell. Také za jeho vývojem stojí společnost MongoDB Inc..
Na adrese https://www.mongodb.com/try/download/compass je ke stažení aktuální verze programu pro různé platformy. Pro operační systém MS Windows je instalační balíček dostupný také ve formátu ZIP, který použijeme.
Po spuštění programu je nutné vytvořit nové spojení, kterým se připojíme k běžícímu MongoDB serveru. Náš server beží na lokálním počítači a komunikuje na portu 8000, proto do kolonky URI vložíme tento řetězec:
mongodb://localhost:8000/
Další parametry pro komunikaci jsme na serveru nenastavili, proto stačí stisknout tlačítko Save & Connect a můžeme program používat.
Z nabídky po levé straně vybereme databázi, kterou cheme používat (db2
) a z databáze kolekci,
nad kterou chceme formulovat dotaz (predmety
). Otevře se nová záložka s dokumenty vybrané kolekce.
Vykonání dotazu funkcí find()
provedeme z nabídky Documents, kde do editačního políčka Filter vložíme jen JSON dokument,
kterým formulujeme podmínku selekce. Vykonání dotazu zařídíme stiskem tlačítka Find.
Chceme-li specifikovat např. projekci dotazu, musíme před vykonáním dotazu specifikovat další volby, které zobrazíme přes odkaz More Options.
Pokročilé dotazy, které volají funkci aggregate()
, se postupně sestavují pod nabídkou Aggregations. To znamená,
že každá fáze dotazu se tvoří samostatně, stiskem tlačítka + Add Stage. Vedle definice každé fáze je hned vidět její výsledek,
přesněji výsledek dotazu po dané fázi. To znamená, že výsledek celého dotazu je vidět vedle definice poslední fáze dotazu.
V dolní části programu je vidět jeden řádek uvozený slovem MONGOSH
. Když na něj klepnete, ukáže se konzole kleinta MongoDB Shellu, kterou můžeme
plnohodnotně používat. Pokud skrze tuto konzoli budou nějak měněna data v databázích, je nutné/vhodné je znovu načtení vyvolat z nabídky spojení
položkou Reload Data.
Copyright © 2023 Martin Zíma