Neo4j – Wprowadzenie do prawdziwych relacji między danymi

Kiedy pierwszy raz usłyszałem o Neo4j, pomyślałem, że tego typu bazy danych są używane tylko do budowania silników rekomendacji. Moja percepcja zmieniła się kiedy zobaczyłem prezentację o GAAND Stack (GraphQL, Apollo, Angular, Neo4j Database). Podczas tego szkolenia zdałem sobie sprawę, że jest o wiele więcej przypadków użycia tego narzędzia. Zaraz po szkoleniu postanowiłem “zanurkować” nieco głębiej w tę technologię. Okazuje się, że Neo4j to potężne narzędzie między innymi dzięki szybkości oraz sposobowi reprezentacji danych.

Są dwa powody, dla których powstał ten artykuł. Pierwszym jest stworzenie kompletnej instrukcji tworzenia GRAND Stack (to samo co GAAND tylko z React.js zamiast Angulara). Drugim powodem jest ustrukturyzowanie wiedzy o Neo4j jako przygotowanie do profesjonalnego certyfikatu z tej technologii (* już posiadam certyfikat w momencie gdy tłumaczę artykuł z innego mojego bloga).

Co to jest grafowa baza danych?

Krótko mówiąc, grafowa baza danych to baza, która używa grafu do zapisywania danych i połączeń między nimi. Dokładnie tak samo jak w grafie takie bazy danych mają węzły (nodes) oraz krawędzie (edges), które mogą być jednostronnie (unidirectional) lub dwustronnie (bidirectional) skierowane.

Tak ustukturyzowane dane są bardzo łatwe do zrozumienia dla ludzi oraz pozwalają na szybkie wyszukiwanie dużo lepiej niż inne struktury danych.

Struktura bazy danych Neo4j

Przykładowy graf

Co to jest węzeł?

Węzeł (node) jest obiektem który reprezetuje pojedynczą encję. Węzeł może przechowywać dane w postaci właściwości (properties).

Węzeł z Neo4j

Co to jest relacja?

Relacja to niekoniecznie trafne tłumaczenie angielskiego słowa relation, ale chodzi o połączenie między węzłami. W grafie będzie to po prostu krawędź. Jak sama nazwa mówi, jest ona elementem bazy danych reprezentującą związek, jaki zachodzi między dwoma węzłami. Relacja, podobnie jak węzeł, może mieć właściwości oraz musi mieć dokładnie jeden typ.

Relacje w Neo4j

Co to jest typ relacji?

Typ relacji definiuje jaką rolę jeden węzeł pełni względem innego i wyjaśnia, dlaczego dwa węzły są ze sobą połączone.

Typ relacji w Neo4j

Co to jest etykieta?

Etykieta (label) jest używana do przypisywania węzłów do różnych grup. Węzeł może mieć dowolną ilość etykiet.

O etykietach możemy myśleć jak o nazwach tabel z relacyjnej bazy danych takiej jak np. MySQL. Etykiety definiują rodzaje węzłów. W poniższym przypadku mamy dostępne dwa typy: Person oraz Movie.

Etykiety w Neo4j

Co to jest właściwość?

Właściwości są właściwie zwykłymi danymi, które mogą być przechowywane przez węzły lub relacje.

Właściwości w Neo4j

Co to jest traversal?

Używam angielskiej nazwy, bo jej polski odpowiednik – przejście – brzmi trochę śmiesznie. Grafowe bazy danych korzystają z path traversal, aby wykonać zapytanie, o których dane potrzebujemy. Traversing (przechodzenie) grafu oznacza odwiedzanie poszczególnych węzłów, podążając zdefiniowanymi krawędziami. Algorytm przechodzi po krawędziach zgodnie z regułami ustawionymi w zapytaniu.

Co to jest index?

Index – podobnie jak w innych bazach danych – pozwala nam zwiększyć wydajność pobierania danych. Baza danych tworzy kopię danych i zapisuje je w możliwie najbardziej efektywny sposób. Powoduje to większe zużycie pamięci i nieco wolniejsze zapisy danych.

Co to jest constraint?

W bazach danych programiści mogą tworzyć pewne ograniczenia zabezpieczające system przed wprowadzaniem niepoprawnych danych. O to właśnie dbają konstrukcje zwane constraintami. Programista definiuje reguły a baza danych przed zatwierdzeniem danych, sprawdza ich poprawność.

Język zapytań w Neo4j – Cypher

Cypher jest językiem zapytań używanym w Neo4j. Dla osób, które miały okazję korzystać z SQL ten język będzie wyglądał znajomo. Cypher nieco przypomina mi streamy w Javie, ponieważ pisząc zapytania, przypominają one coś w rodzaju strumienia. Czytanie zapytania od lewej do prawej przypomina czytanie zdania w języku naturalnym.

Ten język zapytań używa ASCII-Art do tworzenia wzorców, które czynią Cypher bardziej czytelnym. Po spojrzeniu na kod, od razu wiemy, co jest węzłem, co relacją oraz jak zamierzamy użyć tych informacji.

Podstawowe zapytania

Pobieranie danych

MATCH (actor { name: 'Charlie Sheen' })-[:ACTED_IN]->(movie)<-[:DIRECTED]-(director)
RETURN movie.title, director.name

Powyższy kod “mówi” bazie danych, aby zwróciła tytuły filmów oraz imiona reżyserów, w których jednym z aktorów był Charlie Sheen.

Zauważ, że używamy relacji jednokierunkowej poprzez wpisanie strzałki (-[:RELATION_TYPE]->). Ta strzałka precyzyjnie wyjaśnia związek między węzłami.

Tworzenie węzła

CREATE (a:Artist { Name : "Strapping Young Lad" })

Możemy również tworzyć wiele węzłów naraz – używając jednej komendy – oddzielając je przecinkami

CREATE (a:Album { Name: "Killers"}), (b:Album { Name: "Fear of the Dark"}) 
RETURN a,b

lub przez użycie oddzielnych instrukcji CREATE

CREATE (a:Album { Name: "Piece of Mind"}) 
CREATE (b:Album { Name: "Somewhere in Time"}) 
RETURN a,b

Tworzenie relacji

MATCH (a:Actor),(b:Movie)
WHERE a.Name = "John Tree" AND b.Name = "The neo4j movie"
CREATE (a)-[r:ACTED_IN]->(b)
RETURN r

Jak widać powyżej, do tworzenia związków między węzłami używane jest to samo słowo kluczowe. Jedyne co trzeba dodać to informacje o węzłach, które mają być ze sobą powiązane.

Rożnica w porównaniu do SQL

Jeśli znasz SQL, prawdopodobnie zobaczysz wiele podobieństw między tymi językami zapytań. Klauzule takie jak WHERE, UNION, ORDER BY oraz CREATE istnieją w obydwu językach. Główną różnicą jest brak instrukcji JOIN dzięki temu, że Neo4j jest zbudowana w zupełnie inny sposób niż klasyczne relacyjne bazy danych.

Transakcje w Neo4j

Neo4j wspiera ACID, aby w pełni wspierać integralność danych oraz zapewnić dobre zachowanie transakcji.

Wszystkie operacje na danych takie jak dostęp do grafu, indexów czy schematu powinniśmy wykonywać w transakcji.

Ważne do zapamiętania:

  • Dane pobrane podczas przeglądania grafu nie są z żaden sposób chronione przed modyfikacją przez inną transakcję,
  • Mogę wystąpić niepowtarzalne odczyty (non-repeatable) – podczas transakcji zakładane są tylko blokady zapisu,
  • Istnieje możliwość manualnego założenia blokad na węzły oraz relacje, aby osiągnąć wyższy poziom izolacji,
  • Wykrywanie zakleszczeń (deadlock) jest mechanizmem wbudowanym w systemie zarządzania transakcjami.

Aby przeczytać więcej o transakcjach w Neo4j, odwiedź tę stronę.

Poziom Izolacji

Transakcje w Neo4j używają poziomu READ_COMMITED. To oznacza, że transakcje nie widzą żadnych niezatwierdzonych zmian z innych transakcji. Dodatkowo Java API udostępnia możliwość doprecyzowania blokad na węzłach oraz relacjach. Blokady dają możliwość symulowania wyższych poziomów izolacji poprzez zakładanie i zdejmowanie blokad.

Zapisz się na newsletter, aby otrzymywać informacje o nowych artykułach oraz inne dodatki.

Badanie zapytań w Neo4j

EXPLAIN

Komenda EXPLAIN umożliwia nam sprawdzenie planu wykonania zapytanie bez potrzeby uruchamiania kodu. Aby wykonać plan zapytania, wystarczy poprzedzić nasze zapytanie słowem kluczowym EXPLAIN. Taka konstrukcja zwróci nam pusty wynik i nie spowoduje wprowadzenia żadnych zmian na bazie danych.

Poniżej umieściłem wynik następujacego zapytania

EXPLAIN MATCH p=()-[r:ACTED_IN]->() RETURN p LIMIT 25
Plan zapytania
Plan zapytania w Neo4j

PROFILE

Aby sprawdzić, co w naszym zapytaniu wykonuje większość pracy, możemy użyć komendy PROFILE na początku zapytania. Ta komenda uruchamia zapytanie i śledzi ile wierszy wyników przeszło rzez poszczególne operatory. Dodatkowo sprawdzany jest czas, jaki operator potrzebował na interakcję z bazą danych, aby otrzymać dane.

Przykład:

PROFILE MATCH p=()-[r:ACTED_IN]->() RETURN p LIMIT 25
Plan Profilu

Nazewnictwo w Neo4j

Etykieta węzła

Do nazywania etykiet węzłów używamy CamelCase

Poprawna nazwaNiepoprawna nazwa
VehicleOwnervehicle_owner
NetworkNodenetworkNode

Nazwa relacji/związku

Do nazywania relacji używamy wielkich liter, gdzie słowa oddzielone są od siebie znakiem “podłogi” (underscore)

Poprawna nazwaNiepoprawna nazwa
ACTED_INacted_in
OWNED_BYownedBy

Nazwa właściwości

Do nazywania właściwości używamy loweCamelCase.

Poprawna nazwaNiepoprawna nazwa
firstNamefirst_name
amountOfStudentsAMOUNT_OF_STUDENTS

Porównanie do relacyjnej bazy danych

Zakładając hipotetyczną sytuację, że chcemy przemigrować dane z bazy relacyjnej do Neo4j, musielibyśmy myśleć o poszczególnych wierszach jak o węzłach. Mając tę analogię, nazwa tabeli byłaby etykietą węzła. Właściwości w węźle byłby po prostu danymi z poszczególnych wierszy. Nazwa każdej kolumny z kluczem obcym może być wzięta pod uwagę podczas budowania związków między węzłami.

Protokół komunikacyjny w Neo4j – Bolt

Bolt jest nieustandaryzowanym protokołem open-source stworzonym na potrzeby baz danych. Protokół ten jest zorientowany na komunikaty (znów dziwne tłumaczenieni z statement-oriented). Mówiąc prościej, oznacza to, że klient może wysłać komunikaty zawierające ciągi znaków wraz ze zbiorem parametrów. Serwer będzie odpowiadał wiadomościami oraz opcjonalnym strumieniem danych. Neo4j używa tego protokołu a domyślny port to 7687.

Neo4j Bloom

Bloom jest aplikacją dostępną w Graph Platform, która umożliwia użytkownikowi nawiązać wizualną interakcję z danymi w postaci grafu. W prostych słowach jest to aplikacja internetowa, która wizualnie przedstawia graf, z którym pracujemy.

Bloom

Aby zobaczyć więcej o Neo4j Bloom, zachęcam do obejrzenia poniższego wideo.

Licencja

Są dwa typy licencji. Community jest w pełni działającą bazą danych, która może być używana do projektów open-source, projektów wewnątrz organizacji lub do aplikacji uruchamianych na prywatnych urządzeniach. Enterprise udostępnia większą dostępność oraz skalowalność do komercyjnego użycia.

Baza Neo4j wspiera startupy. Aby otrzymać licencję Enterprise dla startupu, wystarczy dołączyć do programu dla startupów oraz spełnić opisane tam wymagania. Zobaz więcej tutaj.

Podsumowanie

Z mojej perspektywy grafowe bazy danych są idealnym wyborem gdy musimy zamodelować prawdziwe zależności lub jakiekolwiek bardziej złożone związki między obiektami. Wyszukiwanie w grafach jest niesamowicie szybkie, co jest obecnie wielką zaletą. Struktura danych w grafie jest o wiele łatwiejsza do wyobrażenia niż w jakiejkolwiek dokumentowej czy tabelarycznej bazie danych.

W Neo4j bardzo lubię sposób operowania na danych. Cypher jest intuicyjnym językiem, który dokładnie pokazuje, co chcemy zrobić. Cypher swoją czytelność zawdzięcza użyciu ASCII-Art. Ten język jest czysty, a napisany w nim kod możemy czytać jak zwykłe zdanie. Dodatkowo zachwycony jestem narzędziem Bloom. Dzięki idealnej wizualizacji grafów oraz dobremu interfejsowi praca z nim jest intuicyjna i przyjemna.

Neo4j nie jest najtańszym narzędziem, ale w przypadku gdy szybkość pobierania danych ma znaczenie, może okazać się idealnym wyborem. Dzięki temu, że bazy grafowe są elastyczne i łatwe w utrzymaniu, dają nieproporcjonalnie dużo zalet w niektórych typach projektów.

Aby podsumować, chciałbym wszystkim polecić Neo4j jako bazę danych do wszystkich projektów, które mogłyby skorzystać z dobrodziejstwa path traversal i innych algorytmów grafowych, jak również z elastycznych relacji i modelowania danych.

Artykuł napisany na podstawie mojego bloga EagerToIt oraz własnych doświadczeń. Zapraszam do kontaktu i dyskusji.