Disclaimer: Článek je překladem článku How to move from MySQL to CouchDB, part II z blogu CouchOne. Překlad i s originálními ilustracemi vychází s laskavým svolením společnosti CouchOne. Autor článku pracuje v CouchOne jako člen výboru a viceprezident pro dokumentaci, v minulosti psal dokumentaci k MySQL. Minulý týden vyšel překlad první části.
Tentokrát začneme tím, že si ukážeme, jak zadávat tyto pohledy a určité další prvky SQL dotazů, jako jsou klauzule WHERE a GROUP BY, a podíváme se na vlastní postup přenosu dat a aplikační logiky na CouchDB.
Zamyslete se nad dotazy, které budete provádět
Jak jsme už viděli, tvorba dotazů na CouchDB se ve skutečnosti skládá ze dvou kroků. Prvním z nich je vytvoření pohledu, který umožní vyhledávání a vybírání dokumentů v databázi. Druhou částí je URL a hodnoty klíče, které chceme z pohledu vybrat. To znamená, že při plánování, jak získat data z CouchDB databáze, je potřeba si promyslet, podle čeho budete data vyhledávat, a to vám pomůže určit strukturu pohledu, který bude potřeba vytvořit.
CouchDB používá pohledy k vytvoření seznamů dokumentů z databáze, a výstupem pohledu je klíč a k němu příslušná hodnota. Jak klíče, tak i tyto hodnoty mohou mít formu jakékoliv JSON hodnoty. Klíč je ve výstupu podstatný proto, že je základem pro mechanismy vyhledávání, třídění a stránkování.
Například funkce vytvářející pohled, který vrátí všechny dokumenty receptů z databáze, jejichž klíčem bude název receptu, by mohla vypadat takto:
function(doc) { if (doc.type == 'recipe' && doc.title !== null) { emit(doc.title, doc); } }
Takovéto funkci se říká mapovací funkce – mapuje údaje z dokumentů na formát, který má být použit. Nepovinný druhý krok se nazývá reduce, a je obdobou agregačních funkcí a klauzule GROUP BY
v MySQL. (Jde o známý algoritmus map/reduce – pozn.překl.)
Při přechodu z MySQL bude mít vliv na to, jak budete data ukládat, i samotný návrh pohledů. Ty navrhujeme tak, aby odpovídaly datům, která chcete vypisovat nebo se na ně dotazovat. Vraťme se k našemu příkladu s recepty: na MySQL byste sestavili dotaz, který hledá mrkev v tabulce přísad, a získali byste tak seznam nalezených receptů. Ve formě SQL by vypadal takto:
SELECT recipe.id, recipe.title FROM ingredients join (recipe) on (ingredients.recipeid = recipe.id) where ingredient = 'carrot'
V CouchDB lze stejného výsledku dosáhnout vytvořením pohledu, který vrátí jeden řádek pro každou přísadu z našeho dokumentu s recepty takto:
function(doc) { if (doc.type == 'recipe' && doc.title !== null) { for id in doc.ingredients { emit(doc.ingredients[id].ingredient, doc); } } }
V dotazovém URL, které slouží pro zpřístupnění pohledu, byste pak zadali požadovanou hodnotu klíče, v našem případě mrkev. Například:
http://couchdb:5984/recipes/_design/recipes/_view/by_ingredient?key=%22carrot%22
Nezapomínejte, že klíčem může být jakákoliv hodnota JSON, a hodnoty klíče, které do dotazu zadáváte, musí být tedy správně enkódované. Jak je vidět z tohoto příkladu, pohled tvoří základ dotazu a samotný přístup k vyhledávání je v režii databáze.
Používání pohledů tímto způsobem vede k tomu, že většina aplikací bude nakonec tvořena mnoha různými pohledy. Postup vytváření pohledů se přitom vlastně neliší od tvorby základních dotazů v aplikaci, a následné optimalizaci těchto dotazů a indexů pro vrácení požadovaného výstupu. Hlavní rozdíl je v tom, že definice pohledu není dotazem v aplikačním kódu, ale je uložena v databázi.
Agregace
V MySQL se agregace (seskupení) používají na mnoha různých místech. Agregace je dosaženo kombinací klauzule GROUP BY
a funkcí, které shromažďují či shrnují údaje. V našem příkladu s recepty můžeme provést dotaz, který vrátí ke každé přísadě počet receptů, v kterých je obsažena. Například dotaz:
SELECT ingredient,count(recipeid) FROM ingredients GROUP BY ingredient
vrátí seznam přísad a ke každé z nich počet receptů, do kterých daná přísada patří. Ve výsledné aplikaci se takto mohou objevit „nejčastěji používané přísady do receptů“, v jiných aplikacích to zase mohou být „nejoblíbenější příspěvky na blogu“, apod.
V CouchDB se k seskupení použije reduce funkce. Navazuje na původní mapovací funkci a zúží výsledky, které mapovací funkce vrátí.
Rozšíření dotazů
Složitější dotazy, při nichž se vyhledává ve podle více datových položek, jsou jen otázkou vytvoření vhodného pohledu, a pak správného zadání hledaných údajů v parametrech dotazu jako vyhledávacího klíče. Pokud mají být vráceny jen údaje v určitém rozmezí, lze použít k zadání tohoto rozsahu počáteční a koncový klíč. Pokud si například vytvoříte pohled, jehož klíčem bude čas přípravy jednotlivých receptů, získáte z něj seznam všech receptů, jejichž příprava zabere 5 až 25 minut, takto:
http://couchdb:5984/recipes/_design/recipes/_view/by_cooktime?startkey=%225%22&endkey=%2225%22
Jedná se o obdobu dotazu:
SELECT title FROM recipe WHERE cooktime >= 5 AND cooktime <= 25
Seřazení výsledků
Řazení výsledků se provádí automaticky podle klíče generovaného každým pohledem. Pokud tedy chcete vypsat seznam receptů seřazených abecedně podle názvu, stačí si vytvořit pohled, který vrací jako klíč název receptu. To znamená, že
http://couchdb:5984/recipes/_design/recipes/_view/by_title
je obdobou
SELECT title FROM recipe ORDER BY title
Pro seřazení výsledků sestupně se v MySQL použije klíčové slovo DESC
SELECT title FROM recipe ORDER BY title DESC
V CouchDB se v URL zadá parametr descending
http://couchdb:5984/recipes/_design/recipes/_view/by_title?descending=true
Stránkování
V MySQL lze dosáhnout stránkování pomocí kombinace klauzulí LIMIT a OFFSET, například takto získáte prvních deset záznamů z našeho dotazu na názvy receptů:
SELECT title FROM recipe ORDER BY title LIMIT 10
Dalších 10 záznamů:
SELECT title FROM recipe ORDER BY title LIMIT 10 OFFSET 10
V CouchDB se používá podobný postup, kdy se limit a skip zadávají jako parametry dotazu. Prvních deset záznamů tedy získáte takto:
http://couchdb:5984/recipes/_design/recipes/_view/by_title?limit=10
A dalších 10 záznamů:
http://couchdb:5984/recipes/_design/recipes/_view/by_title?limit=10&skip=10
Načtení celého základního objektu
V ukázkové databázi receptů v MySQL je potřeba pro zobrazení jednho receptu načíst údaje z více tabulek. V CouchDB platí, že jakmile máte ID dokumentu daného receptu, tak máte přístup k celé struktuře záznamu o tomto receptu tak, jak byl do databáze uložen.
Potřeba zjednodušit mechanismus načítání takovýchto složitých objektů vedla u MySQL k mnoha různým pomocným projektům, které mohou pomoci zvýšit dostupnost dat (např. jejich cachováním v paměti). I při použití CouchDB zůstává nutnost načíst údaje z databáze, ale načtení celého záznamu receptu je možné v rámci jedné operace, namísto několika dotazů a převedení dat do struktury použité pro jejich zobrazení.
Jakmile máte ID dokumentu, stačí k získání obsahu celého dokumentu v CouchDB jediná operace. Pokud například získáte ID dokumentu Aromaticroastchicken z našeho pohledu podle přísad, lze pak celý dokument receptu zobrazit pomocí jediného web requestu:
GET http://couchdb:5984/recipes/Aromaticroastchicken
Výsledkem jsou data receptu jako JSON:
{ "title" : "Aromatic roast chicken", "preptime" : "15", "servings" : "6", "totaltime" : "120", "subtitle" : "A couscous stuffing, and aromatic spices with fragrant lime and roasted garlic, turn a simple roast chicken into something special.", "cooktime" : "105", "_id" : "Aromaticroastchicken", "_rev" : "1-c76ba47a4848af8f965d3940efb118df" "keywords" : [ "diet@dairy-free", "cook method.hob, oven, grill@oven", "diet@peanut-free", "special collections@cheffy recommended", "diet@corn-free", "special collections@weekend meal", "occasion@entertaining", "special collections@very easy", "diet@yeast-free", "diet@shellfish-free", "special collections@feeling spicy!", "main ingredient@poultry", "diet@demi-veg", "meal type@main", "diet@egg-free", "diet@cow dairy-free" ], "ingredients" : [ ... ], "method" : [ ... ], }
Stačí jeden dotaz, jeden požadavek na databázi, aby vrátila vše potřebné pro zobrazení celého receptu na stránce. Samozřejmě zde může být zahrnutý i další obsah, jako komentáře uživatelů nebo další metadata, vše uložené v jednom dokumentu, dostupné pomocí jednoho dotazu.
Přesun dat
Výstupní formát dat je základem pro další závažné rozhodnutí související s přechodem. Je třeba vzít v potaz, jak se bude s údaji pracovat. Pokud jsou údaje, které pochází v MySQL z více zdrojových tabulek, zobrazovány najednou, mívá smysl sloučit tyto informace a ukládat je v CouchDB jako jeden dokument.
Jak už bylo uvedeno, mnoho urychlujících mechanismů, používaných ve spojení s MySQL, funguje na základě toho, že si cachují data z více tabulek, jakmile byly jednou načteny, a tím urychlují nahrávání komplexních datových struktur.
Stejný princip platí i v CouchDB, pokud vezmeme v potaz výše zmíněné pravidla omezující strukturu. Pokud chcete podle určitých kritérií získávat dokumenty z databáze, musíte zajistit, že struktura dat dovoluje vytvořit pohled umožňující vyhledávání.
Například ve struktuře našeho receptu nemá smysl ukládat přísady jako jediné textové pole a tím ztratit podrobnosti o obsahu. To je obdobou uložení všeho do jediného textového sloupce v MySQL a vede ke stejným problémům: zhoršuje možnosti vyhledávání a dotazování podle těchto údajů, a omezuje možnosti zobrazení dat.
Samotný postup přesunu dat je vcelku jednoduchý – načíst data z MySQL databáze, vytvořit dokument pro každý hlavní objekt v databázi, a pak tyto dokumenty zapsat do CouchDB databáze.
Dokumenty v CouchDB jsou ukládány podle svého ID. ID může být libovolný řetězec, což znamená, že můžete použít jakýkoliv údaj jednoznačný pro vás nebo vaší aplikací bez toho, abyste ho museli tento hledat v některém pohledu. V naší všudypřítomné databázi receptů byste takto mohli použít název receptu, například „Rybí polévka“. Omezením tohoto přístupu (stejně jako unikátních indexů v MySQL) je, že nelze uložit dva dokumenty se stejným ID.
Stejně jako funguje klauzule AUTO_INCREMENT v MySQL, tak i CouchDB dokáže vygenerovat automaticky UUID pro každý nový dokument. To ale není povinné, takže můžete mít v jedné databázi jak dokumenty s konkrétním ID, tak i s automaticky vygenerovaným ID, což umožňuje přiřadit předem zvolená ID význačným instancím a dokumentům, a pro zbývající data použít automaticky generované ID.
Rozhraní k CouchDB
MySQL se nepoužívá jen ve webových aplikacích, a já zde nebudu tvrdit totéž ani o CouchDB, ale stojí za to si zapamatovat, že přístup ke CouchDB probíhá pomocí webového rozhraní, které je založené na koncepci REST. Dokumenty jsou načítány za pomocí URL, a možnosti pohledů a dotazů se zadávají jako parametry tohoto URL, stejně jako u jiné CGI nebo podobné webové aplikace.
Pro většinu programovacích jazyků a platforem jsou k dispozici rozhraní, které zjednodušují postup ukládání informace v databázi CouchDB. Existují nástroje pro Perl, Python, Javu, PHP a mnoho dalších. I pokud dojde k nepravděpodobné situaci, že pro vámi vybranou platformu nebude existovat vhodná knihovna, postačuje ke komunikaci s CouchDB pouze schopnost provádět HTTP dotazy.
U většiny webových aplikací v rámci obvyklých platforem, obzvláště pokud používáte AJAX, vypadá postup dotazování dost podobně jako na následujícím obrázku. Váš dotaz pochází z jQuery, je zpracován aplikací, která komunikuje s MySQL databází a vrátí správně údaje v požadované formě.
Při používání CouchDB můžete část aplikační logiky přesunout na samotnou CouchDB databázi, když pomocí návrhových dokumentů získáte data ve správném formátu už z CouchDB.
Když si vše shrneme
Hlavním rozdílem mezi CouchDB a MySQL je to, v jaké podobě jsou data uložené a jak se k nim přistupuje. Pro určité datové struktury poskytuje CouchDB jednodušší řešení problému načtení dat celého jednoho objektu, jak jsme to viděli v předcházejících příkladech. Recepty jsou pěkným příkladem, kde hlavním prvkem báze dat je recept jako celek (náš dokument). Potřeba vyhledávat údaje o receptu a zobrazovat seznamy je požadavkem pouze na prezentaci dat, nejde o přímý požadavek na to, jak mají být data uložená.
Snad vám tyto dva příspěvky daly určitou představu, jak lze přesunout data z MySQL na CouchDB a jak modelovat data v CouchDB a vyvíjet aplikační kód tak, aby celý systém fungoval stejně jako ve vaší existující aplikaci založené na databázi MySQL.
Přehled komentářů