sobota 31. března 2012

Stavíme infrastrukturu kolem MongoDB - model

V poslední době jsem strávil nějaký čas psaním a vymýšlením konceptu persistentní vrstvy, nástrojů a jejich implementace, který nám umožní ukládat JSON dokumenty do NoSQL databáze MongoDB. Dokumenty i kolekce nemají žádné schéma. To má za následek, nebo alespoň u nás mělo, a divil bych se kdyby to někdo začal používat nějak sofistikovaněji, že moc nepřemýšlíte o struktuře dokumentů a prostě je ukládáte jak se vám zrovna hodí. To je velice příjemné a nějaký čas s tím vydržíte. Problém nastane ve chvíli kdy potřebujete ty dokumenty začít zpracovávat automaticky, například chcete udělat zálohu dokumentů vztahujících se k určité entitě v systému a nebo potřebujete zjistit, že odkazované dokumenty existují. Jinými slovy bez modelu popisujícího dokumenty, kolekce a jejich vztahy se neobejdete. Samozřejmě pokud si to nechcete dělat ručně v kódu pro každý typ dokumentu.

My jsme si data uložená v MongoDB popsali jednoduchým modelem reprezentovaným JSON dokumentem, který je sám v MongoDB uložený. Tento model slouží jednak pro nástroje, které dělají servisní operace, jako zálohy a jejich obnovu, validaci dat, čištění pohrobků apod.m a zároveň jím chceme řídit (zatím není implementováno) vlastní persistentní vrstvu, kterou ukládáme dokumenty.





Struktura modelu vypadá následovně.





DocumentStorageModel reprezentuje vlastní model. Má verzi, která nám umožňuje otestovat kompatibilitu dat s ním asociovaných. Typickým příkladem je záloha, ze které se snažím obnovit data, a rád bych se přesvědčil, že aktuální data a tedy i runtime je kompatibilní, a nepotkají mě nepříjemnosti po jejich obnovení. Verzí se zároveň řídí například migrace dat.

DocumentStorageModel obsahuje seznam kolekcí Collection. Každá kolekce má jméno, pod kterým existuje v MongoDB. Zároveň má dokumentaci Documentation. Ta obsahuje lidský srozumitelný popis toho, jaký typ dokumentu je v ní uložený. Dále release, ve kterém vznikla a URI template, pod kterým je vystavený na REST API. Záróveň může mít kolekce asociovaný ToolHints. Ten umožňuje popsat kolekci z pohledu nástrojů pro vytváření a validaci její definice přímo v MongoDB. Například dočasná data se vkládají do takzvané Capped collection, která automaticky odmazává nejstarší vložené dokumenty. Díky těmto hintům je možné ověřit, že je kolekce skutečně tímhle způsobem zkonfigurovaná.

Dokumenty a jejich struktura je popsaná v DataDefinition. Pro jejich validaci je možné definovat RelaxNG schéma (více o tom psal Lukáš Křečan v článku Converting JSON to XML). To se dá použít jednak v persistentni vrstvě pro validaci při změnách/zápisu a zároveň i pro verifikaci například po migraci dat.

Samostatnou kapitolou jsou relace. Šťastnější pojmenování by bylo reference v tomhle případě, aby to v člověku nevyvolávalo pocit, že se snažíme modelovat svět relačních databází. Dokumenty mezi sebou odkazujeme pomocí URI referencí. Máme celkem tři druhy relací: sama na sebe (SelfDocumentRelation), interní na jiný dokument v MongoDB (InternalDocumentRelation) a externí na dokumenty/entity uložené mimo náš systém (ExternalDocumentRelation). Každá relace má u sebe dva atributy cestu k fieldu, ve kterém je v dokumentu uložena a URI template, pomocí které je jí možné rozložit na jednotlivé segmenty. To se používá ve chvíli kdy potřebujeme zjistit příslušnost dokumentu k projektu nebo uživateli. Jednoduchým rozpársováním pomocí URI template víme konkrétního uživatele nebo projekt, ke kterému se dokument vztahuje.

Ukázka modelu

{
       "documentStorageModel":{
          "collections":[
             {
                "collection":{
                   "name":"subscriptions",
                   "affiliation":"PROJECT",
                   "resourceUriTemplate":"/gdc/projects/{project}/users/{user}/subscriptions/{subscription}",
                   "documentation":{
                      "description":"Contains notification subscriptions for given user in given project",
                      "releaseVersion":"R59"
                   },
                   "dataDefinition":{
                      "self":{
                         "selfDocumentRelation":{
                            "path":"subscription.meta.uri",
                            "uriTemplate":"/gdc/projects/{project}/users/{user}/subscriptions/{subscription}"
                         }
                      }
                   }
                }
             }
             ... další kolekce
             ],
             "version":"1.0.0"
        }
    }

Ukázka dokumentu popsaného modelem

{
     "subscription" : {
      "channels" : [
       "/gdc/account/profile/xxx/channelConfigurations/4f4b7e5ce4b0413360a0c4d5"
      ],
      "meta" : {
       "title" : "Test subscription",
       "author" : "/gdc/account/profile/xxx",
       "category" : "subscription",
       "updated" : "2012-02-27 14:00:12",
       "created" : "2012-02-27 14:00:12",
       "uri" : "/gdc/projects/FoodMartDemo/users/xxx/subscriptions/4f4b7e5ce4b0413360a0c4d7"
      },
      "message" : {
       "template" : {
        "expression" : "Hello World"
       }
      },
     }
    }

Z modelu je vidět, že v dokumentu by měl být field na cestě subscription.meta.uri a jeho hodnota by měla odpovídat URI template /gdc/projects/{project}/users/{user}/subscriptions/{subscription}. Z instance dokumentu je vidět, že tomu jak je viz /gdc/projects/FoodMartDemo/users/xxx/subscriptions/4f4b7e5ce4b0413360a0c4d7. Jednoduchou introspekcí této URI reference vidíme, že náleží k projektu FoodMartDemo.

Závěr

Cílem tohoto článku bylo nechat vás trochu nahlédnout do toho kam se v GoodData ubírá naše práce s NoSQL databází MongoDB. Potřeba modelu popisujícího data se ukázala jako nutnost. Na druhou stranu to není specifikum NoSQL světa, protože k podobné řešení, jsme již používali v HP SOA Systinet klasickou relační databází. Pokud máte podobné zkušenosti, a nebo naopak používáte jiný způsob k popisu dat a řízení nástrojů pro práci s nimi, určitě se podělte v komentářích pod tímto článkem.