В отличии от традиционной RDMS (Relational Database Management System – реляционная база данных), у которой в первую очередь играют роли данные, в Graph Database связи между данными имеют большее значение, и выделены в отдельные объекты, что даёт лучшую производительность, особенно при больших объёмах данных с множеством взаимосвязанных объектов, и делает управление такой базой более гибким.
Одной из первых таких систем является Neo4j, которую мы сегодня и потрагаем за приятные места.
Для запросов в Neo4j используется язык запросов Cypher (CQL), для которого существует cypher-shell
, а для доступа к Neo4j через браузер имеется встроенный UI, плюс к этмоу полная поддержка REST API.
Neo4j распространяется по платной модели, при этом имеется бесплатная Community Edition со множеством ограничений (отсутствие кластеризации, скейлинга и прочее), и SaaS Aura. См. сравнение тут>>>.
В этом посте запустим один инстанс Neo4j Community Edition в Docker, кратко посмотрим на его язык запросов и бекап-восстановление данных.
Запуск Neo4j в Docker
Запустим на рабочей машине в Docker, посмотрим на основные компоненты и как происходит доступ к данным на диске. Документация тут>>>.
docker run --rm --name neo4j -p 7474:7474 -p 7687:7687 neo4j:latest
...
Directories in use:
home: /var/lib/neo4j
config: /var/lib/neo4j/conf
logs: /logs
plugins: /var/lib/neo4j/plugins
import: /var/lib/neo4j/import
data: /var/lib/neo4j/data
certificates: /var/lib/neo4j/certificates
run: /var/lib/neo4j/run
Starting Neo4j.
...
2020-07-27 10:11:30.394+0000 INFO Bolt enabled on 0.0.0.0:7687.
2020-07-27 10:11:31.640+0000 INFO Remote interface available at http://localhost:7474/
2020-07-27 10:11:31.640+0000 INFO Started
Проверяем – открываем в браузере http://localhost:7474, и логинимся с дефолтными логином-паролем neo4j:neo4j:


Admin password
Что бы переопределить логин:пароль при запуске – используем --env NEO4J_AUTH
:
docker run --rm --name neo4j --env NEO4J_AUTH=neo4j/pass -p 7474:7474 -p 7687:7687 neo4j:latest
Changed password for user 'neo4j'.
...
cypher-shell
Для работы с данными можно использовать REST API, либо локальную консоль – cypher-shell
.
И подключаемся к shell:
docker exec -ti neo4j cypher-shell -u neo4j -p pass
Connected to Neo4j 4.1.0 at neo4j://localhost:7687 as user neo4j.
Type :help for a list of available commands or :exit to exit the shell.
Note that Cypher queries must end with a semicolon.
Файл настроек Neo4j
В контейнере файл располагается по пути $NEO4J_HOME/conf/neo4j.conf
, т.е. /var/lib/neo4j/conf/neo4j.conf
:
*****************************************************************
Neo4j configuration
For more details and a complete list of settings, please see
https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/
*****************************************************************
The name of the default database
dbms.default_database=neo4j
Что бы переопределить настройки – монтируем их в каталог /conf
контейнера.
Все параметры для файла neo4j.conf
можно найти тут>>>.
Получить текущие настройки прямо из консоли можно с помощью dbms.listConfig()
:
YIELD name, value
WHERE name STARTS WITH 'dbms.default'
RETURN name, value
ORDER BY name
LIMIT 3;
+-------------------------------------------------+
| name | value |
+-------------------------------------------------+
| "dbms.default_advertised_address" | "localhost" |
| "dbms.default_database" | "neo4j" |
| "dbms.default_listen_address" | "0.0.0.0" |
+-------------------------------------------------+
3 rows available after 216 ms, consumed after another 13 ms
cypher-shell
&& CQL
CREATE
Попробуем немного поработать с данными.
По типам данных есть хороший курс на Tutorialspoint вот тут>>>.
Создадим ноду:
0 rows available after 56 ms, consumed after another 0 ms
Added 1 nodes
DELETE
Удалим её:
0 rows available after 32 ms, consumed after another 0 ms
Deleted 1 nodes
Для удаления всех записей – используем (n)
:
Labels
Создать ноду с лейблой label1, и добавим ей Properties с ключами key1 и key2:
0 rows available after 47 ms, consumed after another 0 ms
Added 1 nodes, Set 2 properties, Added 1 labels
Проверяем:
+--------------------------------------------+
| node1 |
+--------------------------------------------+
| (:label1 {key1: "value1", key2: "value2"}) |
+--------------------------------------------+
Либо используя RETURN
– получим ноду сразу после создания:
[email protected]> CREATE (node2:label2 {key1: "value1", key2: "value2"} ) RETURN node2;
+--------------------------------------------+
| node2 |
+--------------------------------------------+
| (:label2 {key1: "value1", key2: "value2"}) |
+--------------------------------------------+
Проверяем в браузере, используя match(n) return n
, что бы вывести все записи в базе:

Relations
Связи можно создать между новыми нодами, так и между уже существующими.
Для создания связи между новыми нодами – добавляем -[r:Имя]->
:
[email protected]> create (node3:label3 {key1: "value1", key2: "value2"}) -[r:RelationName]-> (node4:label4{key1: "value1", key2: "value2"}) RETURN node3, node4;
+-----------------------------------------------------------------------------------------+
| node3 | node4 |
+-----------------------------------------------------------------------------------------+
| (:label3 {key1: "value1", key2: "value2"}) | (:label4 {key1: "value1", key2: "value2"}) |
+-----------------------------------------------------------------------------------------+
1 row available after 88 ms, consumed after another 8 ms
Added 2 nodes, Created 1 relationships, Set 4 properties, Added 2 labels
Проверяем:

Создать связь между двумя существующими нодами можно с помощью MATCH
:
[email protected]> MATCH (node3:label3), (node4:label4) CREATE (node3) -[r:RelationName2]-> (node4) RETURN node3, node4;
+-----------------------------------------------------------------------------------------+
| node3 | node4 |
+-----------------------------------------------------------------------------------------+
| (:label3 {key1: "value1", key2: "value2"}) | (:label4 {key1: "value1", key2: "value2"}) |
+-----------------------------------------------------------------------------------------+
1 row available after 124 ms, consumed after another 9 ms
Created 1 relationships
Backup && Restore
Данные хранятся в каталоге $NEO4J_HOME/data
, который является симлинком на /data
, см. тут>>>.
Проверяем каталоги:
lrwxrwxrwx 1 root root 5 Jul 23 09:01 /var/lib/neo4j/data -> /data
total 12
drwxrwxrwx 4 neo4j neo4j 4096 Jul 27 11:19 databases
drwxr-xr-x 2 neo4j neo4j 4096 Jul 27 11:19 dbms
drwxrwxrwx 4 neo4j neo4j 4096 Jul 27 11:19 transactions
Файлы баз хранятся в папке databases
, где по умолчанию созданы две базы – system, и neo4j, которые можно увидеть с помощью show databases
:
+------------------------------------------------------------------------------------------------+
| name | address | role | requestedStatus | currentStatus | error | default |
+------------------------------------------------------------------------------------------------+
| "neo4j" | "localhost:7687" | "standalone" | "online" | "online" | "" | TRUE |
| "system" | "localhost:7687" | "standalone" | "online" | "online" | "" | FALSE |
+------------------------------------------------------------------------------------------------+
База system хранит системные настройки сервера, а nedo4j – дефолтная пользовательская база.
Neo4j dump
На рабочей машине создаём каталоги:
mkdir -p /tmp/neo4/{data,logs}
Перезапустим Neo4j, смонтируем локальные каталоги в контейнер:
docker run --rm --name neo4j --env NEO4J_AUTH=neo4j/pass -p 7474:7474 -p 7687:7687 -v /tmp/neo4/data/:/data -v /tmp/neo4/logs/:/logs neo4j:latest
Changed password for user 'neo4j'.
Directories in use:
home: /var/lib/neo4j
config: /var/lib/neo4j/conf
logs: /logs
plugins: /var/lib/neo4j/plugins
import: /var/lib/neo4j/import
data: /var/lib/neo4j/data
certificates: /var/lib/neo4j/certificates
run: /var/lib/neo4j/run
...
На хосте проверяем:
ll /tmp/neo4/data/databases/
total 0
drwxr-xr-x 2 7474 7474 720 Jul 27 16:07 neo4j
-rw-r--r-- 1 7474 7474 0 Jul 27 16:07 store_lock
drwxr-xr-x 3 7474 7474 740 Jul 27 16:07 system
Подключаемся, создаём тестовую запись:
docker exec -ti neo4j cypher-shell -u neo4j -p pass
0 rows available after 131 ms, consumed after another 0 ms
Added 1 nodes
Для создания дампа необходимо остановить Neo4j:
[email protected]:/var/lib/neo4j# neo4j-admin dump --database=neo4j --to=/data/backups/
The database is in use. Stop database 'neo4j' and try again.
Выходим из контейнера, и останавливаем его:
Запускаем его ещё раз, но команду передаём bash
, что бы не запустился сам Neo4j:
docker run -ti --rm --name neo4j --env NEO4J_AUTH=neo4j/pass -p 7474:7474 -p 7687:7687 -v /tmp/neo4/data/:/data -v /tmp/neo4/logs/:/logs neo4j:latest bash
Создаём дамп:
Done: 34 files, 250.8MiB processed.
Проверяем:
total 12
-rw-r--r-- 1 neo4j neo4j 9971 Jul 27 13:46 neo4j.dump
Restore
На хосте создаём каталоги для второго инстанса:
mkdir -p /tmp/neo4-2/{data,logs}
Копируем каталог с бекапом из папки старого контейнера в новый:
sudo cp -r /tmp/neo4/data/backup/ /tmp/neo4-2/data/
Запускаем сервер, пока как обычно, монтируем ему /tmp/neo4-2
, меняем порты и имя:
docker run --rm --name neo4j-2 --env NEO4J_AUTH=neo4j/pass -p 7475:7474 -p 7688:7687 -v /tmp/neo4-2/data/:/data -v /tmp/neo4-2/logs/:/logs neo4j:latest
Подключаемся, проверяем данные:
docker exec -ti neo4j-2 cypher-shell -u neo4j -p pass
Connected to Neo4j 4.1.0 at neo4j://localhost:7687 as user neo4j.
Type :help for a list of available commands or :exit to exit the shell.
Note that Cypher queries must end with a semicolon.
+---+
| n |
+---+
+---+
Окей – наших записей нет, т.к. база новая.
Выходим, останавливаем контейнер, и запускаем его с bash
:
docker run -ti --rm --name neo4j-2 --env NEO4J_AUTH=neo4j/pass -p 7475:7474 -p 7688:7687 -v /tmp/neo4-2/data/:/data -v /tmp/neo4-2/logs/:/logs neo4j:latest bash
Загружаем базу, добавляем --force
, т.к. база с таким именем уже есть:
[email protected]:~$ neo4j-admin load --from=/data/backup/neo4j.dump --database=neo4j --force
Done: 34 files, 250.8MiB processed.
Выходим, перезапускаем контейнер, как обычно, что бы запустился сервис:
docker run -ti --rm --name neo4j-2 --env NEO4J_AUTH=neo4j/pass -p 7475:7474 -p 7688:7687 -v /tmp/neo4-2/data/:/data -v /tmp/neo4-2/logs/:/logs neo4j:latest
Подключаемся, проверяем:
+-------------+
| n |
+-------------+
| (:tobackup) |
+-------------+
Всё на месте.