mongoDB – dokumentová databáza 1 – inštalácia a dopytovanie

Úvod

mongoDB je dokumentovo orientovaná databáza so zameraním na flexibilné ukladanie dát, horizontálnu škálovateľnosť a jednoduchosť použitia. Samotné jadro je implementované v C, ale existujú k nej ovládače v mnohých typicky používaných jazykoch.

V porovnaní s klasickými relačnými databázami je mongoDB založené na značne odlišnej mentalite. Dáta v relačných databázach sú uložené v záznamoch, t. j. riadkoch tabuliek, kde každá „bunka“ patrí do konkrétneho stĺpca s daným názvom a dátovým typom. Tabuliek býva samozrejme temer vždy viacero a sú zoskupené do schém. Pri návrhu tabuliek treba brať do úvahy jedno zo základných obmedzení relačného návrhu — v bunke sa môže nachádzať len jedna hodnota a v prípade, že potrebujeme ukladať hierarchie hodnôt (napr. študenta, ktorý má adresu pozostávajúcu z ulice, popisného čísla a mesta), musíme zvoliť návrh využívajúci viacero tabuliek poprepájanými číselnými odkazmi (teda cudzími kľúčmi).

Analógiou záznamu v mongoDB je dokument, na ktorý sa dá dívať ako objekt, ktorý má niekoľko atribútov s danými hodnotami. Na rozdiel od relačných tabuliek však atribút môže obsahovať aj komplexné dáta — presne tak, ako inštančná premenná objektu môže obsahovať iný objekt. Reprezentovať zložené dáta je potom omnoho jednoduchšie.

Skupina záznamov je združená v kolekcii (približne zodpovedajúcej tabuľke) a sada kolekcií je reprezentovaná databázou.

Dokumenty v kolekcii nemusia rovnakú štruktúru. To je rozdiel oproti relačným tabuľkám, kde záznamy v jednej tabuľke musia mať rovnaký počet atribútov (s výnimkou, keď niektorý atribút je null) a dokonca sa musia zhodovať dátové typy. Bezschémovosť dokumentov je vlastnosť, ktorá sa hodí v prípade dynamických databáz — ak sa napr. v polovici vývoja projektu rozhodnete pridať do nových dokumentov dodatočný atribút, nie je to problém a nemusíte vykonávať typické úpravy schémy tabuľky cez ALTER TABLE. Samozrejme, na druhej strane to obetuje konzistenciu dát, ktorú je potrebné riešiť na strane aplikácie.

Použitie mongoDB

MongoDB sa svojou povahou hodí najmä pre účely archivovania a ukladania logovacích záznamov. Je vhodné pre prípady, kde je treba vykonávať vkladania a dopytovania v reálnom čase, ak je samotná konzistencia dát menšou prioritou. Množstvo nasadení mongoDB je v prostrediach, kde je dôležitá škálovateľnosť — príkladom je sociálna sieť Foursquare, ktorá používa mongoDB na ukladanie dát o zaujímavých miestach (pomerne statické dáta), ale aj o aktivite používateľov (obrovské množstvo vkladaní).

Na druhej strane má mongoDB značné medzery v prípadoch, kde je dôraz na transakčnosť (bankové systémy), a v prípadoch, kde je omnoho lepšie použiť statické analytické nástroje s použitím SQL.

Stiahnutie a inštalácia

Ukážme si inštaláciu na systéme Windows. Z webu si stiahnime ZIP súbor pre príslušnú platformu.

Ak máte 64bitový Windows, stiahnite si 64bitovú verziu. MongoDB používa súbory mapované do pamäte, kde 32bitové procesy sú obmedzené na 2.5GB alokáciu.

Po otvorení ZIPu zistíte, že sa v ňom nachádza len niekoľko málo EXE súborov, z ktorých nás zatiaľ zaujíma mongo.exe a mongod.exe. Tento druhý súbor predstavuje databázový server, teda démona, ktorý spravuje ukladanie a dopytovanie dát. Prvý súbor je klient určený pre príkazový riadok.

Je to podobné ako v prípade databázy mySQL, kde mysqld.exe predstavuje databázový server a mysql.exe klienta.

Rozbaľme celý ZIP do vhodného adresára, napr. do C:\mongodb.

Spustenie servera

Ak by sme pustili mongod.exe priamo, ničoho rozumného by sme sa nedočkali. Server totiž predpokladá, že dáta budú uložené v koreňovom adresári aktuálneho disku, presnejšie v \data\db, ale tento adresár sám nevytvorí.

Oveľa lepšou možnosť je vytvoriť si vlastný adresár (napr. C:\mongodb\db) a použiť parameter --dbpath, ktorým doň nasmerujeme server.

C:\mongodb\>mkdir db
C:\mongodb\>mongod --dbpath db

Server by sa v tejto chvíli mal spustiť a odvisnúť, teda zablokovať celú konzolu. Ak sa tak nestalo a server sa zhodil späť do príkazového riadka, skontrolujeme existenciu adresára a prípadné nastavenia firewallu.

Pripojenie sa k serveru

Na pripojenie k serveru vieme použiť klienta z príkazového riadka.

C:\mongodb\>mongo

Mongo klient nás privíta klasickým promptom v duchu nie nepodobnému shellu. Tento shell predstavuje v jadre vyhodnocovač javascriptových príkazov. Hneď po spustení sa pripojí k implicitnej databáze test, ktorá sa však vytvorí až s prvým zápisom.

MongoDB shell version: 2.0.1
connecting to: test
>
Vypísať všetky dostupné databázy môžeme použitím príkazu show dbs. Zobraziť aktuálnu databázu vieme príkazom db.

Pripojenie k vlastnej databáze

Ak sa chceme pripojiť k inej databáze, stačí použiť príkaz use

use zoo

Vkladanie do databázy (insert) a výber (find)

Objektová notácia

Ako sme spomínali na začiatku, mongoDB obsahuje databázu, ktorá obsahuje kolekcie dokumentov. V predošlom príklade sme sa pripojili k databáze zoo. Teraz skúsme vložiť nový dokument zodpovedajúci zvieraťu do kolekcie zvierata.

Pri práci so stavebnými prvkami mongoDB treba povedať dôležitú zásadu. Keďže štandardným vyjadrovacím jazykom klienta je JavaScript, ktorý je objektovo orientovaný, pri prácí s klientom budeme používať klasickú bodkovú notáciu pre prístup k metódam a vlastnostiam (properties) a tiež bežnú syntax pre prácu s metódami.

Z toho vyplýva, že dokumenty, kolekcie i databázy sú objekty, ktoré majú niekoľko metód a vlastností, ku ktorým budeme pristupovať pomocou JavaScriptu.

Zoznam objektov dostupných v JavaScripte shellu je k dispozícii na http://api.mongodb.org/js/.

Inými slovami, kým k relačným databázam pristupujeme pomocou SQL, v mongoDB je štandardným dopytovacím jazykom JavaScript.

Ak chceme vytvoriť nový dokument, použijeme syntax vzdialene podobnú deklarácii máp/slovníkov z iných jazykov:

hroch = { meno : "hroch Karol", vek : 10}

Vytvorili sme premennú hroch, kde názvy atribútov (kľúče) nie sú uvádzané v úvodzovkách, a v ukážke máme dva atribúty: reťazcové meno a celočíselný vek.

Vložiť hrocha do kolekcie je jednoduché. Použijeme na to zabudovaný objekt db, ktorý má vlastnosti prislúchajúce jednotlivým databázam. Kolekcia zvierata je objekt s viacerými metódami, spomedzi ktorých využijeme metódu insert() na vkladanie:

db.zvierata.insert(hroch)
Ak kolekcia zvierata neexistuje, vytvorí sa práve tejto chvíli.

Jednoduchý výber pomocou find()

Vypísať obsah databázy môžeme volaním metódy find() na kolekcii:

> db.zvierata.find()
{ "_id" : ObjectId("4f1593940f30cd6681788ce0"), "meno" : "hroch Karol", "vek" : 10 }

Ak kolekcia obsahuje viac než 20 prvkov, find() vypíše len posledných 20 a výpis ukončí lakonickým has more. Viac prvkov môžeme vypísať zadaním

it

čím sa vypíše ďalších 20 prvkov.

Identifikátor objektov _id

Všimnime si, že find() vypíše obsah databázy v javascriptovej notácii.

Zároveň si všimnime, že vložený objekt dostal nový atribút _id. Tento špeciálny atribút zodpovedá jednoznačnému identifikátoru dokumentu v databáze a ak ho vkladaný objekt ešte neobsahuje, mongoDB ho pri vkladaní vygeneruje a dosadí automaticky. Identifikátor _id je analógiou primárneho kľúča z relačných databáz.

Identifikátor _id môže byť ľubovoľného typu — nielen štandardného typu `ObjectId`. O vlastných možnostiach sa porozprávame neskôr.

Vkladanie skráteným zápisom

Vkladanie môžeme realizovať aj jednoriadkovým zápisom:

db.zvierata.insert({meno : "nosorožec Štefan", "vek" : 45})

Následný výpis vráti dva objekty:

> db.zvierata.find()
{ "_id" : ObjectId("4f1593940f30cd6681788ce0"), "meno" : "hroch Karol", "vek" : 10 }
{ "_id" : ObjectId("4f1597c60f30cd6681788ce2"), "meno" : "nosorozec stefan", "vek" : 45 }

Vložme teraz do databázy ďalšie objekty

db.zvierata.insert({meno : "had Boris", "vek" : 7})
db.zvierata.insert({meno : "baran Marián", "vek" : 3})

Počítanie prvkov v kolekcii

Spočítať prvky v kolekcii môžeme jednoducho — zavolaním metódy count() na kolekcii:

db.zvierata.count()

V našom príklade je výsledkom 2.

Vymazanie všetkých prvkov kolekcie

Ak chceme premazať celú kolekciu, použime remove().

db.zvierata.remove()

Táto metóda má viacero parametrov, pomocou ktorých môžeme premazať objekty podľa výberu (podobne ako v SQL DELETE FROM ... WHERE...). Spomenieme ich neskôr.

Programové vkladanie viacerých prvkov

Keďže používame JavaScript, radostne môžeme využívať celý jeho arzenál a doslova písať jednoduchšie či zložitejšie programy. Predstavme si, že máme k dispozícii text, ktorý chceme rozsekať na jednotlivé slová a tie vložiť do kolekcie tak, že každé slovo bude tvoriť samostatný dokument (táto úloha by sa dala rozšíriť na počítanie výskytu slov v obsiahlom texte).

text = "Hroch Karol na bicykli uhana, nosorozec Stefan uz je tu, had Boris hlad si veru zahana, zozral nam barana Mariana"
Kvôli chybe v mongoDB klientovi (v JIRA ide o chybu SERVER-272) nie je možné zadávať v konzole znaky s diakritikou. Každý pokus zlyhá s hláškou o neplatnom znaku UTF-8. Chyba bude opravená vo verzii 2.1.0. Samotná databáza samozrejme podporuje diakritiku bez problémov.

Rozsekanie textu je jednoduché: premenná text je typu String a v JavaScripte je metóda split(), ktorá dokáže rozdeliť reťazec na jednotlivé položky, ktoré vráti v poli. Musíme len uviesť oddeľovač jednotlivých položiek; v našom prípade je to medzera. Vyskúšajme si to nasledovným príkladom:

text.split(" ") 

Postupnosť dokumentov vieme vytvoriť jednoducho: for-cyklom prelezieme výsledok splitu a každý prvok pridáme do kolekcie termy.

termy = text.split(" ")
for(termIndex in termy) db.termy.insert({term: termy[termIndex]})

V kolekcii zároveň dynamicky vytvoríme objekt s jediným atribútom term, ktorého hodnota bude príslušné slovo.

Na rozdiel od Javy, C#, či Pythonu je for-in cyklus zvláštny. Neiteruje cez jednotlivé elementy poľa, ale cez jeho indexy. Z tohto dôvodu premenná termIndex obsahuje postupne čísla 0… 20 (počet slov v poézii) a prístupom termy[termIndex] vytiahneme aktuálny prvok poľa.

Ak ste fanúšikmi klasických céčkovských for cyklov, vyššie uvedený kód sa dá prepísať ako:

for(var i = 0; i < termy.length; i++) db.termy.insert({termy : termy[i]})

V kolekcii db.termy sa teda objavia všetky jednotlivé slová.

Výber find() na základe kritérií

Vyhľadávanie na základe kritérií je podobné filozofii query by example (QBE). Namiesto skonštruovania booleovskej podmienky (SELECT * FROM zvierata WHERE meno = 'hroch Karol') vytvoríme ukážkový objekt s niekoľkými vyplnenými atribútmi a mongoDB vráti všetky objekty, ktoré sa zhodujú s príkladom.

Ak chceme všetkých hrochov, vytvorme ukážku:

ukazkovyHroch = {meno : "hroch Karol"}

a vyhľadajme podľa nej

db.zvierata.find(ukazkovyHroch)

Výsledkom je

{ "_id" : ObjectId("4f1593940f30cd6681788ce0"), "meno" : "hroch Karol", "vek" : 10 }

Vyhľadávanie s rozsahmi

Ak chceme nájsť zvieratá staršie ako 10 rokov, použijeme opäť vyhľadávanie s ukážkou, akurát namiesto konštantnej hodnoty použijeme vnorený objekt s podmienkou

db.zvierata.find( { "vek" : { $gt : 10 } } )

Výraz { $gt : 10 } zodpovedá podmienke > 10 (mnemotechnicky: Greater Than).

Ak chceme hľadať zvieratá staršie než 10, ale mladšie než 20 rokov, použime zložený výraz využívajúci $gt (väčšie než) a $lt (menšie než):

db.zoo.find( { "vek" : { $gt : 10, $lt : 20 } })

Vyhľadávanie so zloženými podmienkami

Disjunkcia (OR)

Používanie booleovských podmienok je o niečo komplikovanejšie než obvykle. Od verzie 1.6 možno používať operátor $or pre logické alebo (disjunkciu). Operátor berie ako parameter pole dvoch či viacerých položiek, s ktorými sa má hľadať zhoda:

Hrocha Karola alebo hada Mariána nájdeme takto:

db.zvierata.find( { $or : [
    { meno : "hroch Karol" },
    { meno : "baran Marian" }
]
})
Dokumentácia tvrdí, že optimalizátor dopytov je v prípade vnorených $or dopytov menej efektívny ako v prípade dopytov, ktoré sú na vrchole (tak ako v našom prípade).

Operátor $in

Uvedený príklad môžeme poľahky prepísať do podoby, kde nepoužijeme operátor disjunkcie. Stačí použiť iný operátor — $in — ktorý testuje, či prvok patrí do zoznamu iných prvkov.

mena = ["hroch Karol", "nosorozec Stefan"]
db.zoo.find ( {meno : {$in : mena}} )

Konjunkcia (and)

Konjunkcia je k dispozícii od verzie 2.0 a používa sa podobne ako disjunkcia OR.

Zvieratá staršie ako 10 a mladšie ako 20 rokov vieme nájsť aj takto:

db.zvierata.find( { $and : {
        { vek : { $gt : 10 } }, 
        { vek : { $gt : 10 } }
}})

Vyhľadávanie pomocou regulárnych výrazov

V databáze možno vyhľadávať v podreťazcoch použitím klasických regulárnych výrazov podľa perlovskej syntaxe PCRE. Všetkých hrochov možno nájsť nasledovne

db.zvierata.find( { meno : /hroch/i })

Toto nájde nielen hroch Karol, ale aj prípadného lev z Hrochote. Regulárne výrazy podporujú vyhľadávanie bez rozdielu veľkých a malých písmen (príznak i) či vyhľadávanie na viacerých riadkoch (m), kde znak ^ zodpovedá začiatku reťazca a dolár koncu reťazca bez ohľadu na znaky nových riadkov.

Ak chceme nájsť len hrochov, kde ukotvíme výraz na začiatok riadku a budeme rozlišovať veľkosť písmen.

db.zvierata.find( { meno : /^hroch/ })

Takéto prefixové vyhľadávanie dokonca využíva indexy (o nich azda niekedy nabudúce).

Vyhľadávanie regulárnymi výrazmi má aj alternatívnu syntax, ktorá podporuje viacero možností a umožňuje tiež kombinovať regulárne výrazy s inými operátormi.

    db.zvierata.find( { meno : { $regex: "hroch", $options: "i"} })

Dodatočné možnosti sú popísané v dokumentácii. Za všetky spomeňme len s, ktorá zmení správanie bodky: v tomto režime sa s bodkou bude zhodovať aj znak nového riadka.

Ostatné operátory

Operátorov je k dispozícii omnoho viac. Najlepšie je nahliadnuť do dokumentácie.

Výber find() a kurzory

Metóda find() v našich použitiach vypisovala prvých 20 dokumentov, ktoré spĺňali príslušnú podmienku. V skutočnosti vracia find() objekt, tzv. kurzor.

Kurzor ako pole

V shelli mongoDB sa vieme na kurzor pozerať ako na klasické pole, čo je užitočné v mnohých jednoduchých prípadoch. Pole je indexované od nuly, čiže druhé zviera vypíšeme cez:

var zvierata = db.zvierata.find()
zvierata[1]

Táto konštrukcia má tri zádrhele:

  • musíme použiť kľúčové slovo var pri priradení do premennej. Ak tak neurobíme, shell rovno vypíše obsah premennej a druhý riadok (zvierata[1]) nevypíše nič. Súvisí to s technickou implementáciou kurzorov, o ktorej sa zmienime nižšie. To je pôvodca aj druhého zádrhela:
  • ak by sme chceli vypísať druhé zviera ešte raz, shell opäť nevypíše nič.
  • tretí zádrhel spočíva v efektivite. V prípade obsiahlej kolekcie a vysokého indexu načíta mongoDB celú kolekciu od začiatku až do prvku so špecifikovaným elementom, čo môže byť pamäťovo náročné. V takom prípade je omnoho lepšie zminimalizovať počet nájdených prvkov vhodnou podmienkou.

Zádrhele môžeme zatiaľ vyriešiť tak, že prevedieme kurzor na skutočné pole (a nebudeme ho používať ako objekt, ktorý sa na pole podobá), použime metódu toArray().

zvierata = db.zvierata.find().toArray()

Klasické použitie kurzora

V skutočnosti je metafora kurzora podobná ako v prípade kurzora myši — je to akási šípka, ktorá v každej chvíli ukazuje na jeden dokument vo výslednej kolekcii. Kurzor na začiatku ukazuje na prvý dokument z výsledku a vieme ho postupne posúvať na druhý, tretí … až kým neprejdeme celý záznam.

Kurzorom sa vieme posúvať len dopredu po výslednej kolekcii dokumentov — môže sa to zdať nepohodlné, ale na druhej strane je jeho použitie mimoriadne efektívne: databáza totiž nemusí vrátiť celú kolekciu dokumentov naraz (a zaberať tak pamäť), ale stačí jej postupne jednotlivé dokumenty dávkovať.

Kurzor má dve významné metódy:

  • hasNext() vráti true, ak kurzor ukazuje na nejakú položku. Ak je kurzor na konci výslednej sady dokumentov, vráti false.
  • next() vráti dokument, na ktorý práve ukazuje kurzor.
Idea kurzora je veľmi podobná filozofii iterátora známeho z viacerých programovacích jazykov. ResultSet známy z JDBC v Jave je koniec-koncov tiež akýsi kurzor.

Kurzor vieme použiť na vypísanie všetkých dokumentov z kolekcie (i v prípade, že je ich viac než 20)

kurzor = db.zvierata.find();
while (kurzor.hasNext()) printjson(kurzor.next());

Funkcia printjson() vypíše daný objekt v tradičnej javascriptovej notácii (JSON).

Zádrhele pri kurzoroch ako poliach

Ak si spomenieme na tri vyššie spomenuté zádrhele, v tejto chvíli budeme vedieť, prečo kurzor ako pole správal tak čudne. Vo chvíli, keď kurzor preiteruje cez celú výslednú kolekciu, pamäť, ktorú zaberali položky, sa uvoľní a na prejdené položky sa zabudne. Nasledovné riadky teda fungujú nesprávne:

zvierata = db.zvierata.find()
zvierata[1]

Shell priradí do premennej zvierata kurzor, lenže rovno ho preiteruje… čím nastaví kurzor na poslednú položku. Výpis zvierata[1] už nedáva zmysel, pretože sa kurzorom nevieme vrátiť späť na prvú pozíciu.

Rovnako nebude celkom podľa predstáv fungovať toto:

var zvierata = db.zvierata.find()
zvierata[1]
zvierata[1]

Použitím var sa síce obsah kurzora nevypíše, ale prvý riadok pristúpi k prvku s indexom 1 (teda druhému prvku) a posunie kurzor. Druhý identický riadok už nevypíše nič — kurzor sa totiž nevie vrátiť k predošlým prvkom.

Ako sme spomínali vyššie, prístup s použitím toArray() takéto problémy nemá. Na druhej strane, môže to spôsobovať problémy s pamäťovou náročnosťou.

Výpis vybraných polí

V SQL vieme obmedziť výpis tabuľky len na niektoré stĺpce (operácia projekcie, v SQL vyjadrená cez SELECT stlpec1, stlpec2, ... FROM tabulka). Ako vypísať len niektoré atribúty objektov?

Skrátený zápis je jednoduchý. Keďže next() rovno vracia dokument, na ktorý ukazuje kurzor, môžeme rovno volať jeho inštančné premenné

kurzor = db.zvierata.find();
while (kurzor.hasNext()) printjson(kurzor.next().meno);

Kurzor a funkcionálna mentalita

Štandardné použitie kurzora uvedené vyššie je založené na konštrukciách z procedurálnych jazykov — použijeme cyklus while, a kombináciu hasNext() a next(). mongoDB ponúka aj alternatívny prístup založený viac na funkcionálnom prístupe.

Kurzor má metódu forEach, ktorý zoberie ako parameter jednu funkciu, ktorá sa zavolá pre každý dokument z výslednej kolekcie. Dokumenty v tvare JSON môžeme vypísať aj takto:

db.zvierata.find().forEach(printjson)

Pre každý dokument z výslednej kolekcie sa zavolá funkcia printjson, ktorá dostane do svojho parametra aktuálny dokument. Ukážkový dokument vo výpise vyzerá nasledovne:

{
    "_id" : ObjectId("4f1597c60f30cd6681788ce2"),
    "meno" : "nosorozec stefan",
    "vek" : 45
}

Ak chceme vypísať len mená zvierat, môžeme to spraviť dvoma riadkami. V prvom zadeklarujeme funkciu, ktorá pre objekt na vstupe vypíše hodnotu jeho vlastnosti meno:

meno = function(x) { print(x.meno) }

V druhom riadku túto funkciu použijeme ako argument pre forEach

db.zvierata.find().forEach(meno)

Samozrejme, to všetko vieme napísať na jeden riadok

db.zvierata.find().forEach( function(x) { print(x.meno) } ) 

Limitovanie výsledkov

Ak volanie find() vracia množstvo výsledkov, hodí sa niekedy obmedziť ich počet. Sú situácie, keď počet dokážeme obmedziť podmienkou v ukážkovom objekte, ale to nie vždy stačí. Typická úloha môže vyžadovať vypísanie prvých troch zvierat.

V SQL je často používaný klauzula LIMIT a podobnú funkcionalitu ponúka aj mongoDB.

db.zvierata.find().limit(3).forEach(printjson)

Dokumentácia odporúča volať limit() vždy, keď to prichádza do úvahy — jeho použitie môže zvýšiť výkon. Ak použijete limit 0, znamená to vrátenie všetkých objektov.

Preskakovanie výsledkov

Vo webových aplikáciách sa veľmi často hodí stránkovanie — jedna stránka obsahuje napr. 10 dokumentov a klient chce jednotlivé skupiny zobrazovať postupne.

Vypísať druhú desiatku dokumentov môžeme nasledovne:

db.zvierata.find().skip(10).limit(10)

Pri skip()e však platí podobná zásada ako pri indexovanom prístupe ku kurzorom. mongoDB musí preliezť celú kolekciu od začiatku až po požadované miesto, čo môže byť náročné na výpočet i prístup k disku. Namiesto toho sa odporúča používanie rozsahových dopytov, o ktorých sme sa zmienili vyššie.

Počítanie pri obmedzenom počte prvkov

Vyššie sme spomínali, že pomocou count() vieme vrátiť počet prvkov, ktoré zodpovedajú dopytu. Počet zvierat mladších než 10 rokov vieme zistiť nasledovne:

db.zvierata.find({ $vek : { $lt : 10 }}).count()

Ak používame klauzuly limit() či skip(), počítanie prvkov ich ignoruje.

db.zvierata.find().skip(10).limit(1).count()

Predošlý výraz stále vráti rovnaký počet ako klasický neobmedzený find(). Ak chceme zobrať do úvahy obmedzenia, pridajme do count() parameter true.

db.zvierata.find().skip(10).limit(1).count()

Bokom poznamenajme, že dokumentácia vystríha pred použitím count() na poli, ktoré bolo získané z kurzora. Takýto výpočet je mimoriadne pamäťovo náročný a navyše pomalý:

db.zvierata.find().toArray().count()

Priame vrátenie výsledku s jedným dokumentom

V rámci syntaktického cukru ponúka shell k dispozícii metódu findOne(), ktorá má rovnaké možnosti ako bežný find(), ale vracia odlišné hodnoty. Namiesto kurzora vráti rovno výsledný dokument alebo null, ak je kolekcia prázdna. Ak obsahuje kolekcia viacero prvkov, výsledkom je prvý z nich.

Interne je findOne() ekvivalentný volaniu

db.zvierata.find().limit(1)

Takto vieme nájsť dokument podľa identifikátora

db.zvierata.findOne( { _id: ObjectId("4f1593940f30cd6681788ce0")})

Triedenie dokumentov pomocou sort()

Ak chceme triediť dokumenty vo výslednej kolekcii podobne ako to možno robiť v SQL klauzulou ORDER BY, použime metódu sort() na kurzore. Jej parametrom je objekt s názvami stĺpcov a kritériami triedenia.

Ukážka zotriedi zvieratá podľa mena zostupne

db.zvierata.find().sort( { meno: -1 } )

Záporné číslo v kritériu udáva zostupné triedenie (ascending), kladné číslo zase vzostupné (descending).

Samozrejme, triediť môžeme aj podľa viacerých kritérií. Utriediť podľa klesajúceho veku a následne podľa mena môžeme nasledovne:

db.zvierata.find().sort( { vek: -1, meno: 1 } )

Dokumentácia odporúča používať sort() v kombinácii s limit()om. Ak kolekcia neobsahuje limit() a príslušná vlastnosť podľa ktorej sa triedi, nie je v indexe (o indexoch možno neskôr), prebehne triedenie všetkých objektov v rámci pamäte, čo môže mať vplyv na výkonnosť. Pri použití limit()u prebehne optimalizované triedenie.

Nabudúce

V ďalšom dieli si popíšeme možnosti aktualizácie dát.

Literatúra

V článku bola hojne používaná dokumentácia v angličtine na stránkach mongoDB. Začať možno tutoriálom a z neho pokračovať ďalšími odkazmi.