Přejít k navigační liště

Zdroják » Databáze » Kompletní průvodce po CouchDB – III

Kompletní průvodce po CouchDB – III

Články Databáze

Po dvou teoretičtějších částech konečně opravdu začínáme. V této kapitole Kompletního průvodce po CouchDB si ukážeme, jak k CouchDB přistupovat pomocí HTTP dotazů a jak spustit a použít zabudovaný administrační nástroj Futon. Vytvoříme si první dokument a podíváme se, jak pracovat s pohledy (views).

Nálepky:

Začínáme

Dřív než se pustíte do čtení, nalistujte si Přílohu D, Instalace a zjistěte si, jak postupovat ve svém operačním systému. Budete muset provést popisované kroky a nainstalovat si DCouchDB dřív než budete číst dál.

Pozn. překl.: Od doby vydání této knihy vývoj CouchDB pokročil, nyní je k dispozici mnohem pohodlněji pomocí instalátorů nebo instalačních balíčků pro většinu používaných OS a distribucí – viz CouchDB Wiki / Installation.

Tento text je součástí překladu knihy CouchDB: The Definitive Guide. Stejně jako autoři originálu oceníme věcné připomínky a pomoc s textem, za které předem děkujeme.

Všechny systémy běží!

Bleskově se podíváme na hlavní API CouchDB a použijeme k tomu utilitu  curl. Pamatujte, že to je pouze jedna možnost, jak komunikovat s CouchDB, další možnosti si ukážeme později. Zajímavé na curl je, že nám umožňuje řídit tvar HTTP dotazu a vidět přesně, co se děje „v zákulisí“ databáze.

Ujistěte se, že CouchDB stále běží, a zadejte:

curl http://127.0.0.1:5984/

Tento příkaz vyvolá požadavek GET na vaši lokální databázi. Odpověď bude vypadat nějak takto:

{"couchdb":"Welcome","version":"0.10.1"}

Nic světoborného. CouchDB jen říká „ahoj“ a oznamuje číslo verze.

Zkusme si získat seznam všech databází:

curl -X GET http://127.0.0.1:5984/_all_dbs

Přidali jsme pouze řetězec  _all_dbs.

Výsledek by měl vypadat nějak takto:

[]

To je naprosto v pořádku. Ještě jsme nevytvořili žádnou databázi, takže výsledkem je prázdné pole.

Příkaz curl posílá požadavky typu GET, pokud nespecifikujete jinak. Můžete poslat požadavek POST pomocí curl -X POST. Abychom si usnadnili práci s historií terminálu, budeme používat volbu -X i pro požadavky typu GET. Budeme-li chtít příště poslat POST, stačí nám změnit pouze tuto hodnotu.

HTTP ve skutečnosti dělá trochu víc práce, než je tady vidět. Pokud vás zajímají detaily toho, jak probíhá komunikace, použijte volbu -v (např. curl -vX GET), která vám ukáže, jak se curl pokouší připojit, jaké posílá hlavičky a jakou odpověď dostává od serveru. Je to skvělá pomůcka pro ladění.

Pojďme si vytvořit databázi:

curl -X PUT http://127.0.0.1:5984/baseball

CouchDB odpoví:

{"ok":true}

Čtení seznamu databází tentokrát už ukáže zajímavější výsledek:

curl -X GET http://127.0.0.1:5984/_all_dbs
["baseball"]

Na tomto místě bychom měli zmínit JavaScript Object Notation (JSON), což je formát, v němž CouchDB komunikuje. JSON je jednoduchý formát pro výměnu dat, založený na syntaxi JavaScriptu. Protože je JSON přirozeně kompatibilní s JavaScriptem, je ideálním klientem právě webový prohlížeč.

Hranaté závorky ( []) reprezentují seznamy (pole), složené závorky ( {}) dvojice klíč/hodnota. Klíče musí být řetězce, uzavřené do uvozovek( "), a hodnoty můžou být řetězce, čísla, booleovské hodnoty, seznamy nebo slovníky klíč/hodnota. Podrobnosti o JSON naleznete v  Příloze E: Základy JSON.

Vytvořme si další databázi:

curl -X PUT http://127.0.0.1:5984/baseball

CouchDB odpoví:

{"error":"file_exists","reason":"The database could not be created, the file already exists."}

Už máme databázi s tímto jménem, takže CouchDB hlásí chybu. Zkusme vytvořit databázi s jiným názvem:

curl -X PUT http://127.0.0.1:5984/plankton

CouchDB odpoví:

{"ok":true}

A na dotaz po všech databázích:

curl -X GET http://127.0.0.1:5984/_all_dbs

CouchDB odpoví:

["baseball", "plankton"]

Teď smažeme druhou databázi, ať máme základní operace z krku:

curl -X DELETE http://127.0.0.1:5984/plankton

CouchDB opět vrátí:

{"ok":true}

a seznam databází bude stejný jako před vytvořením druhé databáze:

curl -X GET http://127.0.0.1:5984/_all_dbs

CouchDB vrátí:

["baseball"]

Přeskočíme nyní práci s dokumenty a ukažme si jinou, a pravděpodobně snazší, cestu pro práci s CouchDB. Přesto mějte na paměti, že „na pozadí“ se stále provádějí v zásadě stejné operace, jaké jsme dosud dělali ručně. Vše se dělá pomocí GET, PUT, POST a DELETE požadavků na určitý URI.

Vítejte ve Futonu

Ukázali jsme si základy API, a teď pokročíme dál a budeme si hrát s Futonem, což je zabudovaný administrátorský nástroj. Futon poskytuje plný přístup ke všem vlastnostem CouchDB a usnadňuje některé úlohy. S Futonem můžeme vytvářet a mazat databáze, prohlížet a editovat dokumenty, spouštět MapReduce pohledy nebo spouštět replikaci databází.

Futon je webová aplikace, spouští se tedy z prohlížeče, a naleznete ji na adrese:

http://127.0.0.1:5984/_utils/

Měli byste vidět něco více či méně podobného obrázku 1. V dalších kapitolách se zaměříme na použití CouchDB se serverovými jazyky, jako jsou Ruby a Python, ale tato kapitola je zároveň skvělou ukázkou toho, jak CouchDB dokáže „z podstaty“ hostovat webovou aplikaci, která nepotřebuje ke svému běhu na serveru nic víc než webserver, který je zabudovaný v databázi. Možná je to způsob, jakým byste chtěli tvořit své vlastní webové aplikace.

První věc, kterou bychom měli s CouchDB udělat, je spustit zabudované testy, čímž ověříme, že instalace je v pořádku. Pokud budou testy OK, můžeme počítat s tím, že podivné chování naší aplikace je způsobené námi, nikoli špatně nainstalovanou databází. Zároveň nás červená vlaječka v testovací sadě Futonu varuje, že bychom měli zkontrolovat instalaci dřív, než se pokusíme něco dělat, čímž si ušetříme mnohá zklamání z toho, že věci nefungují tak, jak jsme čekali.

Obr. 1 – úvodní obrazovka Futonu

Některá síťová nastavení mohou způsobit chybu testů replikace, když k nim budete přistupovat přes localhost. Zkuste je spustit přes číselnou adresu, tedy  http://127.0.0­.1:5984/_util­s/.

Přepněte do testovací sady kliknutím na “Test Suite” v postranním panelu, a klikněte „run all“ – spustí se tím všechny připravené testy. Obrázek 2 ukazuje Futon se spuštěnými testy.

Obr. 2 – Futon testuje instalaci

Testovací sada běží v prohlížeči, ne jen v databázi, takže testuje nejen to, zda je databáze funkční, ale i to, že funguje správně připojení, což může být užitečné v případě, že máte nějakou agresivní proxy nebo jiný HTTP middleware.

Pokud vám testovací sada zahlásí velký počet chyb, budete muset instalaci opravit. Informace naleznete mj. v příloze D.

Když testy doběhnou správně, máte ověřené, že je instalace CouchDB v pořádku a můžete začít s databází pracovat a podívat se, co Futon nabízí dál.

Vaše první databáze a první dokument

Vytvoření databáze je ve Futonu prosté. Klikněte na úvodní stránce na „Create Database“. Když se vás Futon zeptá na jméno, zadejte třeba hello-world a klikněte na tlačítko Create.

Po vytvoření databáze zobrazí Futon seznam dokumentů. Na počátku bude prázdný  (viz obr. 3), můžeme tedy začít s vytvořením prvního dokumentu. Klikněte na  odkaz „Create Document“ a pak na tlačítko Create. Nechte políčko s ID dokumentu prázdné; CouchDB vygeneruje ID za vás.

Pro naši ukázku je postačující vygenerované UUID. Až budete psát své aplikace, použijte raději vlastní UUID. Pokud budete totiž spoléhat na to, že vám ID vygeneruje databáze, a budete muset zadat POST dvakrát, protože např. při prvním spojení spadne, můžete skončit s dvěma dokumenty, aniž byste se jakkoli dozvěděli o tom prvním, protože pouze ten druhý bude potvrzený. Pokud si vygenerujete vlastní UUID, k podobné situaci nedojde.

Futon ukáže nový dokument, ale jediné položky, co v něm uvidíte, budou _id a _rev. Novou položku vytvoříte kliknutím na “Add Field”. Nazvěme novou položku hello. Kliknutím na zelenou fajfku (nebo stiskem klávesy Enter) ukončíme vytváření pole hello. Dvojklikem na oblast s hodnotou (původně null) ji můžete editovat.

Pokud zadáte jako novou hodnotu třeba slovo world, dostanete při kontrole (po kliknutí na zelenou ikonku) chybu. Hodnoty v CouchDB musí být platné JSON řetězce. Takže uzavřete řetězec do uvozovek: "world" a tím jej změníte v platnou JSON hodnotu. Teď už by neměl být s jejím uložením žádný problém. Můžete experimentovat s dalšími JSON hodnotami: [1, 2, "c"] nebo {"foo":"bar"}. Jakmile vložíte hodnoty do dokumentu, poznamenejte si aktuální hodnotu atributu _rev a klikněte na “Save Document.” 

Obr. 3 – prázdná databáze ve Futonu

Obr 4. Dokument „Hello world“ ve Futonu

Zjistíte, že se hodnota atributu _rev změnila. Později si probereme detaily podrobněji, ale pro tuto chvíli je pro nás nejdůležitější, že _rev funguje jako bezpečnostní pojistka při ukládání dokumentů. Dokud se vy a CouchDB shodnete na tom, které číslo revize _rev daného dokumentu je aktuální, můžete úspěšně ukládat jakékoli změny.

Futon také nabízí možnost zobrazit vnitřní JSON reprezentaci dat, což může být někdy stručnější a snazší k přečtení, podle toho, s jakými daty pracujete. Pokud chcete vidět JSON reprezentaci svého dokumentu, klikněte na záložku „Source“. Výsledek by měl vypadat nějak takto:

Obr 5. JSON zápis dokumentu “hello world” ve Futonu

Dotazy pomocí MapReduce

Tradiční relační databáze vám umožňují spustit libovolný dotaz nad správně strukturovanými daty. CouchDB, na rozdíl od těchto databází, používá předdefinované funkce map a reduce způsobem, který je znám jako MapReduce. Tyto funkce jsou v podobných situacích velmi flexibilní, protože se dokáží přizpůsobit odlišnostem v dokumentech a indexy pro každý dokument mohou být počítány nezávisle a paralelně. Kombinace funkcí map a reduce se v terminologii CouchDB nazývá pohled

Pro ty, co jsou zvyklí pracovat s relačními databázemi, může být MapReduce zpočátku trochu problém. Namísto určení toho, které řádky z kterých tabulek mají být součástí výsledné sady údajů a ponechání práce na databázi (včetně výběru nejefektivnějšího způsobu), jsou dotazy pomocí reduce založené na jednoduchých požadavcích na data „od-do“ proti indexu, vytvořenému vaší funkcí map.

Funkce map jsou zavolány jednou pro každý dokument, a dokument je jim předán jako argument. Funkce si může vybrat, zda dokument přeskočí a nebude jej zpracovávat, nebo jestli pošle k dalšímu zpracování nějaká data (funkcí emit). Data jsou ve formátu klíč/hodnota a z jednoho dokumentu jich může být i víc. Funkce map nesmí záviset na jakýchkoli informacích mimo zpracovávaný dokument (zapomeňte tedy na globální proměnné apod.) Díky této nezávislosti může CouchDB generovat pohledy postupně a paralelně.

Pohledy si CouchDB ukládá jako řádky, které jsou setříděné podle klíče.  To zjednodušuje získávání dat z určitého rozsahu klíčů, i když záznamů jsou tisíce či miliony. Když píšete map funkci, vaším prvotním cílem je „vybudovat index“, jehož primárním účelem je usnadnit vám vyhledávání dat podle nějakého klíče.

Než začneme experimentovat s MapReduce, potřebujeme mít v databázi nějaká data. Vytvoříme si dokumenty, v nichž budou ceny různého zboží v různých obchodech. Udělejme si dokumenty pro jablka, pomeranče a banány. (Nechte CouchDB vygenerovat vlastní hodnoty pro _id a _rev.) Použijte Futon k vytvoření dokumentů, které budou mít výslednou strukturu zhruba podobnou této:

{
    "_id" : "bc2a41170621c326ec68382f846d5764",
    "_rev" : "2612672603",
    "item" : "apple",
    "prices" : {
        "Fresh Mart" : 1.59,
        "Price Max" : 5.99,
        "Apples Express" : 0.79
    }
}

Tento dokument po vložení do DB bude ve Futonu zobrazen zhruba takto:

Obr 6. Příklad – dokument s cenami jablek ve Futonu

Dobrá, jablka máme hotová, pojďme vložit nějaké pomeranče:

{
    "_id" : "bc2a41170621c326ec68382f846d5765",
    "_rev" : "2612672603",
    "item" : "orange",
    "prices" : {
        "Fresh Mart" : 1.99,
        "Price Max" : 3.19,
        "Citrus Circus" : 1.09
    }
}

A nakonec ještě pár banánů:

{
    "_id" : "bc2a41170621c326ec68382f846d5766",
    "_rev" : "2612672603",
    "item" : "banana",
    "prices" : {
        "Fresh Mart" : 1.99,
        "Price Max" : 0.79,
        "Banana Montana" : 4.22
    }
}

Představte si, že chystáte pohoštění, ale klient je velký škudlil. Vytvoříme si proto pohled, který nám ukáže jednotlivé druhy ovoce, seřazené podle ceny v daných obchodech. Klikněte na “hello-world”, tím se vrátíte do přehledu databáze, a z rozbalovacího seznamu “select view” vyberte “Temporary view…” Tím je vytvořen nový (dočasný) pohled. Výsledek by měl vypadat nějak takto:

Obr 7. Dočasný pohled ve Futonu

Upravte funkci map, na levé straně, tak, aby vypadala takto:

function(doc) {
    var store, price, value;
    if (doc.item && doc.prices) {
        for (store in doc.prices) {
            price = doc.prices[store];
            value = [doc.item, store];
            emit(price, value);
        }
    }
}

Tuto JavaScriptovou funkci spustí CouchDB při výpočtu pohledu pro každý dokument v databázi. Tentokrát necháme funkci reduce prázdnou.

Klikněte na „Run“ a měli byste vidět výsledek podobný tomu na dalším obrázku, kde jsou různé druhy ovoce, seřazené podle ceny. Pěkné, ale funkce map by nám byla mnohem užitečnější, kdyby výsledky seskupila podle druhu ovoce, takže ceny banánů by byly pěkně pod sebou atd. Řadicí algoritmus CouchDB dovoluje na místě klíče použít jakýkoli JSON objekt. V tomto případě třeba vypíšeme pole [item, price], takže výsledné hodnoty budou seskupené podle typu zboží a srovnané podle ceny.

Obr 8. Výsledek spuštění pohledu ve Futonu

Upravme si tedy funkci map:

function(doc) {
    var store, price, key;
    if (doc.item && doc.prices) {
        for (store in doc.prices) {
            price = doc.prices[store];
            key = [doc.item, price];
            emit(key, store);
        }
    }
}

Nejprve kontrolujeme, zda dokument má pole, která chceme použít. CouchDB dokáže efektivně pracovat se situací, kdy několik ojedinělých funkcí map selže, ale když začnou selhávat pravidelně (například kvůli chybějícímu poli v dokumentu nebo kvůli jiné JS výjimce), zastaví CouchDB zpracování daného pohledu, aby zbytečně neplýtvala zdroji. Z tohoto důvodu je potřebné zkontrolovat existenci každé položky, kterou budeme používat. V tomto případě naše map funkce přeskočí první dokument, „hello world“, aniž by vytvořila jakýkoli výstup nebo skončila s chybou. Výsledkem tohoto dotazu by mělo být něco takového:

Obr 9. Výsledek pohledu se seskupováním hodnot podle typu a ceny

Jakmile zjistíme, že dokument obsahuje pole, která potřebujeme (typ zboží a nějaké ceny), projdeme iterací ceny a vypíšeme dvojice klíč/hodnota. Klíč je pole druhu zboží a ceny, čímž je dáno řazení indexu. Hodnotou je pak jméno obchodu, kde se dané zboží za danou cenu prodává.

Řádky jsou seřazeny podle klíčů – v tomto příkladu podle druhu ovoce a podle ceny. Tato metoda komplexního řazení je srdcem vytváření užitečných indexů v CouchDB.

MapReduce může být poměrně velkou výzvou, obzvlášť pokud jste několik let zvyklí na relační databáze a jejich dotazy. Důležitá věc, kterou byste si měli pamatovat, je, že funkce map vám umožňuje vybrat a seřadit data z dokumentů podle hodnoty, jakou si vyberete, a že CouchDB je navržena tak, že poskytuje rychlý a efektivní přístup k takto indexovaným datům.

Spouštění replikace

Futon dokáže nastavit replikaci mezi dvěma lokálními databázemi, mezi lokální a vzdálenou databází, nebo mezi dvěma vzdálenými databázemi. Ukážeme si, jak replikovat data z jedné lokální databáze do jiné, což bude pro nás jednoduchý způsob, jak si vytvořit zálohu databáze při hraní s příklady.

Nejprve si vytvoříme prázdnou databázi jako cíl replikace. Vraťte se do přehledu a vytvořte databázi hello-replication. Klikněte na položku “Replicator” v postranním panelu a vyberte hello-world jako zdroj a hello-replication jako cíl. Klikem na “Replicate” svou databázi replikujete. Výsledek bude vypadat nějak takto:

Obr. 10 – Spouštění replikace ve Futonu

Pro větší databáze bude tento proces pomalejší. Je důležité, abyste nechali okno prohlížeče otevřené po dobu replikace. Alternativně můžete spustit replikaci pomocí  curl nebo jiného HTTP klienta, který dokáže udržet spojení po dlouhou dobu. Pokud klient spojení zavře, budete muset replikaci spustit znovu. Naštěstí CouchDB naváže přerušenou replikaci tam, kde předtím skončila, nezačíná znovu od začátku.

Shrnutí

Ukázali jsme si většinu vlastností Futonu a už víte, jak se podívat na dokumenty a pracovat s nimi, což se bude hodit v dalších kapitolách, když budeme tvořit jednoduchou aplikaci. Přístup použitý ve Futonu, tedy čistý HTML+JS na straně klienta, poskytovaný přímo zabudovaným HTTP serverem, ukazuje, jak lze tvořit plně vybavené webové aplikace jen pomocí HTTP API a zabudovaného web serveru.

Ale než se dostaneme k tvorbě aplikací, musíme se podívat ještě jednou na HTTP API – a tentokrát pod zvětšovacím sklem. Vezměte do ruky curl, pohodlně se usaďte na gauč a relaxujte…

Komentáře

Subscribe
Upozornit na
guest
7 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Petr

Asi je to tím, že jsem s NoSQL databázemi neměl ještě čest, ale vrtá mi hlavou, proč je vlastně mapreduce nad dokumenty rychlejší než dotaz nad relační databází.
Přece klasický DB engine dokáže zpracovávat data také paralelně, ne?

JakubS

CouchDB si při uložení dokumentu inkrementálně přepočítá pohledy (indexy). Když se na takový pohled dotážete tak server jen odešle připravené výsledky.

SQL prochází indexy a počítá výsledky až při dotazu.

Snad jsem se nedopustil příliš velkého zjednodušení.

Palo

Nad SQL databazou ked dam:
select * from xxx where id=123;
kde id je primary key cize ma unique index tak tiez ziskate z indexu priamo „ofset“ na disku kde ten riadok je.
Pri
select * from xxx where name like ‚K%‘;
si CouchDB asi tiez nepomoze s indexom.

Co prosim Vas znamena veta: „Když se na takový pohled dotážete tak server jen odešle připravené výsledky.“? On ako vie co sa budem pytat? Alebo sa bavime len o nejako ekvivalente materializovaneho view?

Ako uz raz niekto napisal. Pri NoSQL databazach pri porovnani s SQL to nema ziadne vyhody. Len vymenite sadu znamych problemov za sadu neznamych problemov.

Franta

SQL databáze místo toho mají materializované pohledy a persistentní virtuální sloupečky.

Franta

Ad „Protože je JSON přirozeně kompatibilní s JavaScriptem, je ideálním klientem právě webový prohlížeč.“

Jak se v takovém případě řeší uživatelská oprávnění?

Franta

Pochopil jsem to správně, že místo:

GRANT UPDATE ON tabulka1 TO uživatel1;

se píše:

function(newDoc, oldDoc, userCtx) {
  if (newDoc.author) {
    if(newDoc.author != userCtx.name) {
      throw("forbidden": "You may only update documents with author " +
        userCtx.name});
    }
  }
}

a není to ekvivalentní, protože vylučujeme, co uživatel nemůže, místo abychom jen deklarovali, co může?

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.