Există un set restrâns de probleme pentru care modelul relațional chiar e o potrivire proastă, iar simptomul e mereu același: query-urile tale cresc o pădure de clauze JOIN, fiecare făcând încă un pas pe un lanț de chei străine, iar planul de execuție trece de la acceptabil la îngrozitor pe undeva pe la al cincilea hop. Exemplul la care apelează toată lumea e „prietenii prietenilor prietenilor”, dar aceeași formă apare în detecția de fraudă, motoarele de recomandare, knowledge graphs și orice sistem unde relațiile dintre entități contează la fel de mult ca entitățile însele. Lecția asta e despre familia de baze de date construite exact pentru forma asta.
Titlul e scurt: când relațiile reprezintă 80% din query-urile tale, o bază de date graf le face triviale și rapide. Când relațiile reprezintă 20% din query-urile tale, o bază de date relațională cu CTE-uri recursive e ok. Zona de mijloc e acolo unde stau deciziile interesante.
Modelul de date
O bază de date graf stochează două lucruri: noduri și muchii.
Un nod e o entitate. Un utilizator, un produs, o companie, o tranzacție. Are o etichetă (tipul) și un set de proprietăți (perechi cheie-valoare). Un nod tipic ar putea fi (:Person {name: "Narcis", age: 33}), unde Person e eticheta și blocul cu acolade sunt proprietățile.
O muchie e o relație între două noduri. Are un tip, o direcție și opțional propriile proprietăți. O muchie tipică ar putea fi (narcis)-[:KNOWS {since: 2018}]->(maria), însemnând o relație direcționată KNOWS de la narcis către maria, cu o proprietate since care înregistrează când a început relația.
Nu există o schemă separată în sensul relațional. Schema e graful însuși. Poți adăuga noi tipuri de noduri, noi tipuri de relații, noi proprietăți după bunul plac. Forma datelor e ce pui în ele, iar baza de date e fericită să le traverseze.
flowchart LR
U1[User: Narcis] -- KNOWS --> U2[User: Maria]
U2 -- KNOWS --> U3[User: Luca]
U1 -- BOUGHT --> P1[Product: Espresso machine]
U2 -- BOUGHT --> P1
U3 -- LIKED --> P2[Product: Italian moka pot]
U2 -- LIKED --> P2
Un graf mic ca ăsta îți permite să întrebi, într-un singur query: „ce produse sunt apreciate sau cumpărate de oameni pe care prietenii mei îi cunosc dar despre care eu încă nu știu.” Acel query, în SQL, e un multi-self-join peste un tabel users, un tabel friendships și un tabel purchases, cu un pas recursiv care extinde lanțul de prietenii. Într-o bază de date graf, sunt patru rânduri.
Limbajul de interogare
Limbajul dominant pentru property graphs e Cypher, originar din Neo4j și acum adoptat (cu variații minore) de AuraDB, Memgraph, AWS Neptune (în modul property-graph) și extensia Apache AGE pentru Postgres. Caracteristica definitorie a lui Cypher e că sintaxa pentru potrivirea unui pattern de graf arată ca un desen ASCII al pattern-ului.
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)
WHERE a.name = "Narcis" AND a <> c
RETURN c.name
Asta găsește prietenii prietenilor lui Narcis, excluzând-o pe Narcis însăși. Clauza MATCH desenează pattern-ul: un nod Person a, conectat printr-o muchie KNOWS la un nod Person b, conectat printr-o altă muchie KNOWS la un nod Person c. WHERE filtrează. RETURN proiectează.
Citirea Cypher îți ia zece minute să te obișnuiești și apoi devine naturală. Scrierea de traversări complexe (căi de lungime variabilă, drumuri minime, traversări ponderate) e dramatic mai concisă decât echivalentul SQL. Limbajul e motivul principal pentru care bazele de date graf par ergonomice pentru workload-urile pe care le țintesc.
W3C definește și SPARQL, un limbaj de interogare pentru triple stores RDF, care e un model de graf diferit (triplete subiect-predicat-obiect, fără proprietăți pe muchii, schemă descrisă separat). SPARQL e standardul în lumea semantic-web și a knowledge graphs academice. E un limbaj excelent pentru ce face, iar majoritatea oamenilor care construiesc baze de date graf pentru aplicații astăzi aleg Cypher în detrimentul SPARQL pentru că modelul property-graph se mapează mai natural pe tipurile de obiecte care le pasă aplicațiilor.
Unde strălucesc bazele de date graf
Cazurile de utilizare în care modelul graf câștigă fără ambiguitate sunt cele în care traversarea de relații pe mai mulți pași e întrebarea centrală.
Motoare de recomandare
„Utilizatorilor ca tine le-a plăcut și X” e problema canonică de recomandare, iar în esența ei e o traversare de graf. Găsește utilizatorii conectați la mine printr-un anumit semnal (achiziții similare, rating-uri similare, graf social similar), uită-te la ce s-au angajat acești utilizatori, scoate la suprafață item-urile pe care nu le-am văzut încă. Într-o bază de date graf asta e o operație într-un singur query. Într-o bază de date relațională e un job batch care materializează o matrice de similaritate peste noapte.
Detecția de fraudă
Frauda financiară apare adesea ca pattern-uri în graful de relații care sunt invizibile la nivelul tranzacțiilor individuale. Un cerc de conturi care își transferă bani între ele în cerc. Un cont care primește brusc transferuri de la douăzeci de surse noi. Un device fingerprint partajat între conturi care n-ar trebui să se cunoască. Astea sunt pattern-uri de graf, iar detectarea lor în timp real e exact ce fac bazele de date graf deosebit de bine. Băncile și procesatorii de plăți sunt cei mai constanți utilizatori de baze de date graf din motivul ăsta.
Rețele sociale
Friend-of-a-friend, conexiuni mutuale, drum minim între doi utilizatori, identificarea de influenceri (noduri cu centralitate mare). Astea sunt problemele de manual de traversare de graf și sunt exact ce arată funcționalitățile de rețea socială.
Knowledge graphs
Wikidata, Google Knowledge Graph și o recoltă tot mai mare de knowledge graphs de întreprindere stochează entități (oameni, locuri, concepte, evenimente) cu relații bogate, tipizate, între ele. Întrebările pe care le pui unui knowledge graph (dă-mi toate filmele italiene regizate de femei în anii 1970, cu actorii care au apărut în ele) sunt traversări multi-hop peste muchii tipizate. O bază de date graf e store-ul natural.
Grafuri de rețea și de dependențe
Dependențe software, topologie de infrastructură, lanțuri de aprovizionare, ierarhii organizaționale. Orice unde întrebarea „ce depinde de X, tranzitiv” e o întrebare reală. Astea sunt grafuri, iar o bază de date graf face din întrebarea tranzitivă o primitivă în loc de un eseu.
Alternativa relațională
Dacă problema ta arată ca un graf dar e mică, sau are doar o mână de tipuri de relații, sau e în mare parte tabulară cu câteva query-uri în formă de graf deasupra, poți face treaba în SQL cu common table expressions recursive (CTE-uri).
Un CTE recursiv în Postgres arată așa pentru friend-of-a-friend:
WITH RECURSIVE friends(person_id, depth) AS (
SELECT b.person_id, 1
FROM friendships b
WHERE b.friend_id = (SELECT id FROM persons WHERE name = 'Narcis')
UNION ALL
SELECT b.person_id, f.depth + 1
FROM friendships b
JOIN friends f ON b.friend_id = f.person_id
WHERE f.depth < 3
)
SELECT DISTINCT p.name
FROM friends f
JOIN persons p ON p.id = f.person_id
WHERE f.depth = 2;
Asta merge. Nu e frumos și nu e ce citește un inginer într-o după-amiază aglomerată, dar dă răspunsul corect pentru două sau trei hop-uri și grafuri de dimensiuni mici-spre-medii. Unde se prăbușește e când graful e mare, adâncimea e nelimitată sau începi să adaugi constrângeri pe calea însăși (doar muchii cu pondere > X, doar anumite tipuri de muchii, drum minim ponderat). Fiecare dintre astea e câteva clauze suplimentare într-un query de graf și un strat suplimentar de complexitate în CTE-ul recursiv.
Rezumatul cinstit: CTE-urile recursive SQL sunt ok până la două sau trei hop-uri pe un graf moderat. Dincolo de asta, costul cognitiv al SQL crește mai repede decât costul cognitiv al echivalentului Cypher, iar performanța începe să se degradeze.
Costul: un exemplu rezolvat
Ia query-ul „găsește toate produsele cumpărate de prietenii prietenilor lui Narcis, cu numărul de prieteni care au cumpărat fiecare produs, sortate descrescător după număr.”
În Cypher:
MATCH (n:Person {name: "Narcis"})-[:KNOWS*2]-(friend:Person)-[:BOUGHT]->(p:Product)
WHERE friend <> n
RETURN p.name, count(DISTINCT friend) AS buyers
ORDER BY buyers DESC
LIMIT 10
Patru rânduri de substanță. Se citește ca o descriere a problemei.
În Postgres cu CTE-uri recursive:
WITH RECURSIVE friend_chain(person_id, depth) AS (
SELECT id, 0 FROM persons WHERE name = 'Narcis'
UNION ALL
SELECT f.friend_id, fc.depth + 1
FROM friendships f
JOIN friend_chain fc ON f.person_id = fc.person_id
WHERE fc.depth < 2
),
fof AS (
SELECT DISTINCT person_id
FROM friend_chain
WHERE depth = 2
AND person_id <> (SELECT id FROM persons WHERE name = 'Narcis')
)
SELECT p.name, count(DISTINCT pu.person_id) AS buyers
FROM fof
JOIN purchases pu ON pu.person_id = fof.person_id
JOIN products p ON p.id = pu.product_id
GROUP BY p.name
ORDER BY buyers DESC
LIMIT 10;
O pagină de SQL. Funcționează. E mai greu de citit, mai greu de modificat și pe un graf de milioane de utilizatori și zeci de milioane de relații, va fi mai lent decât versiunea Cypher, uneori cu o marjă mare.
Peisajul produselor
Neo4j: alegerea dominantă
Neo4j e baza de date graf la care apelează prima oară majoritatea echipelor și pe bună dreptate. Există din 2007, limbajul Cypher a fost inventat acolo, documentația e excelentă, comunitatea e mare, iar tooling-ul (Neo4j Browser, Bloom, biblioteca Graph Data Science) e matur. Există o ediție community open-source și o ediție enterprise cu clustering, securitate și funcționalități operaționale.
Compromisul, ca în majoritatea poveștilor de tip „alegerea dominantă cu un singur produs”, e că Neo4j e propria sa bază de date. O rulezi separat, faci backup separat, o scalezi separat. Pentru workload-uri unde graful e inima aplicației, e ok. Pentru workload-uri unde graful e o funcționalitate printre multe altele, e overhead operațional.
AWS Neptune
Neptune e baza de date graf gestionată de la AWS. Suportă atât property graphs cu Gremlin/openCypher cât și RDF cu SPARQL. Suportul dual-model e neobișnuit și util dacă ai un picior în ambele lumi. Pitch-ul e cel standard al bazei de date gestionate: mai puțină povară operațională dacă ești deja pe AWS. Funcționalitatea și performanța sunt competitive cu Neo4j, deși implementarea openCypher nu e 100% completă față de Cypher-ul de la Neo4j.
Memgraph
Memgraph e o bază de date graf in-memory, scrisă în C++, cu un limbaj de interogare compatibil Cypher. Pentru că e in-memory, e semnificativ mai rapidă decât bazele de date graf bazate pe disc pentru workload-urile care încap în RAM. Pitch-ul e pentru workload-uri de graf streaming (detecție de fraudă în timp real, recomandare în timp real) unde latența contează și setul de lucru încape în memorie.
Postgres + Apache AGE
Apache AGE e o extensie Postgres care adaugă stocare de graf și un limbaj de interogare similar Cypher peste o bază de date Postgres standard. Avantajul e că păstrezi o singură bază de date. Poți amesteca query-uri relaționale și query-uri de graf în aceeași tranzacție și nu rulezi un al doilea store. Dezavantajele sunt că AGE e mai puțin bogat în funcționalități decât Neo4j, performanța nu e la fel de bună pentru workload-uri grele de graf, iar ecosistemul e mai mic.
Pentru o echipă care deja rulează Postgres și vrea să adauge funcționalitate modestă de graf fără overhead operațional, AGE e o alegere rezonabilă. Pentru o echipă unde graful e central, Neo4j e răspunsul mai bun.
Decizia
Regula de decizie, distilată:
- Dacă relațiile sunt nucleul aplicației tale (detecție de fraudă, motor de recomandare, knowledge graph, rețea socială), folosește o bază de date graf dedicată. Neo4j e default-ul sigur.
- Dacă ai unul sau două query-uri în formă de graf peste o aplicație în mare parte relațională, scrie-le ca CTE-uri recursive în baza ta de date relațională existentă și trăiește cu stânjeneala.
- Dacă ești între, și ești deja pe Postgres, încearcă Apache AGE înainte să adaugi o a doua bază de date.
- Dacă ești pe AWS și nu vrei să rulezi nimic singur, Neptune e ok.
Cea mai comună greșeală e opusul lecției: echipele apelează la o bază de date graf pentru că bazele de date graf sunt interesante, decid că totul e un graf și ajung cu un sistem unde baza de date graf face 5% din muncă și 50% din povara operațională. Majoritatea datelor sunt tabulare. Folosește graf store-ul pentru problema reală de graf. Folosește store-ul relațional pentru rest.
Ce urmează
Lecția următoare e despre motoarele de căutare, unde forma datelor e iarăși diferită (text, cu relevance scoring) și store-ul dominant (Elasticsearch și alternativele care au crescut alături de el) face lucruri cu care nicio bază de date general-purpose nu se poate compara. După aceea părăsim grădina zoologică a stocării și trecem la stratul operațional al Modulului 3: replicare, sharding, partiționare și aspectele practice ale rulării acestor sisteme în producție.
Citări și lecturi suplimentare
- Documentația Neo4j,
https://neo4j.com/docs/(consultat 2026-05-01). Referința pentru Cypher, modelul operațional și biblioteca Graph Data Science. - Referința limbajului Cypher (openCypher),
https://opencypher.org/(consultat 2026-05-01). Specificația limbajului întreținută de comunitate. - Ian Robinson, Jim Webber, Emil Eifrem, „Graph Databases” (O’Reilly, ediția a 2-a 2015). Manualul fundamental. Mai vechi dar tot cea mai bună introducere în modelul de date și pattern-urile de design.
- Documentația AWS Neptune,
https://docs.aws.amazon.com/neptune/(consultat 2026-05-01). Modelul dual property-graph și RDF, cu note operaționale despre serviciul gestionat. - Documentația Memgraph,
https://memgraph.com/docs(consultat 2026-05-01). Arhitectura in-memory și cazul de utilizare streaming-graph. - Documentația Apache AGE,
https://age.apache.org/age-manual/master/index.html(consultat 2026-05-01). Extensia Postgres și capabilitățile sale curente.