Grafová databáze

Grafové databáze jsou databáze, kde se data ukládají ve formě grafu, což lépe odpovídá reálným vztahům mezi objekty. Jako objekty si představme jakékoliv objekty reálného světa, které lze seskupit do tzv. typů (v objektově orientovaném programování používáme pojem třídy).

Grafová databáze se skládá ze dvou základních částí:

Uzly (nodes)

  • též vrcholy
  • představují konkrétní objekty z reálného světa
  • jsou jednoho typu
  • mají vlastnosti

Hrany (edges)

  • reprezentují vztahy mezi uzly
  • mají směr
  • jsou jednoho typu
  • spojnice, které dávají datům kontext

Anatomie uzlu

Uzel je základní objekt (entita), která nese data:

Příklad:

p:Person {
  firstName: "Jan",
  lastName: "Syrový",
  birthDate: "2008-07-01",
  eyeColour: "blue"
}

Vlastnosti jednotlivých uzlů ani typů uzlů nejsou nikde napevno nadefinované. Proto každý uzel může mít různé vlastnosti podle toho, co potřebujeme evidovat. Na druhou stranu z důvodu snadného výběru uzlů silně doporučuji mít stejná data uložená ve stejných vlastnostech.

p:Person{
  firstName: "Lukáš",
  lastName: "Novák",
  birthDate: "2008-04-15",
  maxBench: 115,
  maxBenchDate: "2026-04-12"
}
p2:Person{
  krestni: "Dan",
  prijmeni: "Schlagsahne",
  birthDate: "2008-12-01",
  longestTrip: 2050,
  longestTripYear: 2025
}

Na příkladu výše vidíte dva uzly typu Person, u kterých si můžete všimnout odlišnosti názvů jednotlivých vlastností. To, že Lukáš má vlastnosti maxBench a Dan longestTrip je naprosto v pořádku – u každého uzlu mohu sledovat různé vlastnosti. To, že křestní jméno u Lukáše je ale uloženo ve vlastnosti firstName a u Dana ve vlastnosti krestni, nám v budoucnu může způsobovat problémy při práci s uzly.

Anatomie hrany

Hrana spojuje jednotlivé uzly. Každá hrana má:

(p:Person {firstName:"Jan"}-[f:friendOf {since:"2023-09-01"}]->(p2:Person {firstName:"Lukáš"})

Průchod grafem

Na rozdíl od tradičních relačních databází, kde se vztahy odvozují prostřednictvím spojení primárních a cizích klíčů a pomocí JOINů, ukládají grafové databáze vztahy nativně. To znamená, že vazby jsou přímou součástí samotné datové struktury, což umožňuje mnohem rychlejší dotazování a přirozenější navigaci mezi uzly. Fyzicky jsou totiž hrany reprezentovány jako ukazele (pointery) v paměti – tím odpadá nutnost prohledávat tabulky a indexy – databázový systém při dotazu doslova „přeskočí“ z jednoho uzlu na druhý v konstantním čase (tzv. index-free adjacency).

Tento proces přechodu z jednoho uzlu do druhého pomocí hran se nazývá průchod grafem (graph traversal). Tyto průchody dokážou s vysokou účinností odhalovat skryté vzory, hledat nejkratší cesty nebo identifikovat shluky (clustery) v datech.

Tento posun v myšlení – od pevných tabulkových dat k síti propojených dat – umožňuje zkoumat zcela nové vrstvy otázek. Namísto filtrování statických záznamů tedy grafová databáze umožňuje sledovat návaznosti a vidět, jak se jednotlivé uzly navzájem ovlivňují v reálném čase.

Použití grafové databáze

Grafovou databázi používáme v případě, že vztahy mezi jednotlivými objekty jsou stejně důležité nebo důležitější než objekty samy. Příklady použití:

Výhoda ukládání hran pomocí ukazatelů v paměti se naplno projeví ve chvíli, kdy data tvoří složitou síť a potřebujeme se ptát na hlubší souvislosti. Typické příklady:

Srovnání relační a grafové databáze

Zkusme si nejprve srovnat jednotlivá složení relační databáze a grafové databáze. Víme, že relační databáze se skládá z relací (entit), které jsou popisované atributy a které obsahují jednotlivé záznamy. Grafová databáze se skládá z uzlů (vždy konkrétního typu) a hran, které uzly propojují.

vlastnostrelační databázegrafová databáze
seskupení objektůrelace s pevnou strukturou dattypy uzlů, volná struktura dat
objekty reálného světazáznamy relacíuzly
popisy objektůatributy relacívlastnosti uzlů
propojení objektůvztahy pomocí primárních a cizích klíčů, realizace podle typu kardinalithrany
popis propojení objektůvlastnosti vztahů pomocí atributů v rozkladové relacivlastnosti hran

Pojďme si porovnat obecné chování jednotlivých typů databází, samozřejmě s jistou dávkou zobecnění:

vlastnostrelační databázegrafová databáze
strukturapevná struktura relací tvořená konkrétními atributy určených datových typůuzly a hrany bez schématu
propojeníJOINy (výpočetně náročné)nativní ukazatele (rychlé)
složité dotazyrychlost klesá společně s počtem JOINůkonstantní rychlost průchodu
rychlost dotazu s hloubkou dotazu 3+~2000 ms
(musí procházet indexy databázových tabulek)
~50 ms
(sleduje ukazatele v paměti)

neo4j

Neo4j je nejpoužívanější grafová databáze na světě. Místo tabulek pracuje přímo s objekty a jejich vazbami.

Neo4j pro lokální instalaci si můžete stáhnout z oficiálního webu zde: https://neo4j.com/download/

Vytvoření uzlu

CREATE (p:Person {firstName: "Jaroslav", lastName: "Kolenatý", birthDate: "2008-12-14", maxBench: 125}) RETURN p;

Za příkazem CREATE si definujeme novou proměnnou p, která je platná pouze po dobu vykonávání příkazu. Používáme ji v druhé části příkazu (RETURN p), který grafové databázi říká, aby poté, co uzel vytvoří, nám jej ještě ukázala.

Ve složených závorkách vidíme jednotlivé key-value páry, kdy nastavujeme vlastnosti a hodnoty konkrétnímu vytvářenému uzlu.

Výběr uzlu

MATCH (p:Person {lastName: "Kolenatý"}) RETURN p;
MATCH (p:Person) WHERE elementId(p) = "4:8d77cc4c-2eeb-44e2-815e-a1f5c9cfed15:1" RETURN p;

V prvním případě vidíme výběr všech uzlů typu Person, jejichž atribut lastName má hodnotu Kolenatý.
Ve druhém případě vidíme výběr konkrétního uzlu typu Person na základě automaticky stanoveného ID.

Vytvoření hrany mezi uzly

Pro vytvoření hrany si nejprve musíme vybrat dva konkrétní uzly. Doporučuji zpočátku si napřed zobrazit pomocí příkazu MATCH uzly, které chceme propojit, abychom propojovali skutečně pouze tyto dva uzly. Například:

MATCH (p:Person {firstName:"Lukáš"}), (e:Employer {name:"Google"}) RETURN p, e;

Nyní máme jistotu, že se nevybraly jiné uzly než přesně ty, které chceme propojit. Můžeme tedy do příkazu doplnit vytvoření hrany:

MATCH (p:Person {firstName:"Lukáš"}), (e:Employer {name:"Google"}) 
CREATE (p)-[w:worksAt]->(e)
RETURN p, w, e;

Nastavení dalších vlastností

Potřebujeme-li nastavit další vlastnosti uzlu či hrany, postupujeme tak, že jej nejprve vybereme a následně použijeme příkaz SET:

MATCH (n:Person {lastName:"Syrový"}) SET n.maxBench = 95 RETURN n;
MATCH (p:Person {firstName:"Lukáš"})-[w:worksAt]->(e:Employer {name:"Google"}) SET w.since = "2026-01-02" RETURN p, w, e;

Smazání uzlu

Také v případě, že chceme uzel smazat, jej nejprve vybereme pomocí příkazu MATCH a následně zadáme příkaz DELETE:

MATCH (n:Person {firstName: "Smažák"}) DELETE n;
MATCH (p:Person) WHERE elementId(p)="4:8d77cc4c-2eeb-44e2-815e-a1f5c9cfed15:3" DELETE p;

Další výběry

MATCH (n:Person) RETURN n;
MATCH (p)-[f:friendOf]->(p2) RETURN p,f,p2;

První výběr vybere všechny uzly typu Person. V praxi bychom jej asi takto příliš nepoužili – velké množství uzlů by nám zahltilo paměť. Druhý výběr vybere všechny uzly, které mezi sebou mají hranu typu friendOf.

Pokud chceme například vybrat pouze uzly, které mají mezi sebou vzájemné vazby (jeden je kamarád druhého a druhý prvního), můžeme využít například:

MATCH (p)-[f:friendOf]->(p2),
      (p2)-[f2:friendOf]->(p)
RETURN p, f, f2, p2;

Pokud chceme vybrat přátele mých přátel, které já nemám za přátele, zeptáme se:

MATCH (ja:Person {fName:"Otokar", lName:"Březina"})-[:friendOf]->(p2)-[:friendOf]->(p3)
WHERE NOT (ja)-[:friendOf]->(p3) 
RETURN DISTINCT p3;

Podcast

Podcast byl vygenerován službou NotebookLM na podkladě této výukové stránky.

V terminologii OOP bychom uzel mohli nazvat _____

instancí třídy (!pozor! nikoliv samotnou třídou)

Musejí mít všechny uzly stejného typu v grafové db stejné vlastnosti?

Ne, vlastnosti nejsou nikde napevno nadefinované. Každý uzel může mít různé vlastnosti.

Který příkaz slouží k vyhledávání uzlů nebo hran?

MATCH

Jakému objektu grafové db byste přiřadili relaci z relační db?

Typ uzlu (label).

Co v grafovém modelu definují hrany?

Vztahy mezi konkrétními uzly (například uzel „Jan“ se kamarádí s uzlem „Anežka“).

Jak se jmenuje NoSQL jazyk používaný grafovou db neo4j?

Cypher

K jakému objektu v relační db byste přiřadili uzel v grafové db?

Záznam v relaci (pozor – nikoliv celá relace).

Jaký příkaz se používá k aktualizaci nebo přidání vlastnosti existujícímu uzlu nebo hraně?

SET

Který příkaz se používá k vytvoření nového uzlu?

CREATE

Proč jsou grafové databáze efektivnější pro hluboké dotazy než relační?

Sledují přímé ukazatele v paměti (pointery) namísto procházení klíčů a spojování tabulek pomocí JOINů.

Jak byste definovali vlastnosti v grafové databázi?

Pár key–value (klíč–hodnota) ukládající metadata jak o uzlech, tak o hranách.

Co v grafovém modelu představuje uzel?

Konkrétní objekt, jako například konkrétní osobu, produkt, nábytek nebo účet.