До сих пор запросы писал человек: в cypher-shell, в Browser, в песочнице. В настоящем проекте запросы шлёт приложение: сайт клуба показывает знакомых, бот записывает оценки. Для этого у Neo4j есть официальные драйверы для Python, JavaScript, Java, Go и .NET. В этом уроке пишем по маленькому скрипту на Python и на JavaScript. Выводы ниже получены на Neo4j 2026.10.1 в Docker из урока про установку с загруженным графом клуба, драйверы neo4j 6.4 для Python и neo4j-driver 6.2 для Node.js.
Как драйвер говорит с базой
Драйвер ходит к серверу по протоколу Bolt на порт 7687, тому же, что cypher-shell. Адрес сервера пишется как neo4j://localhost:7687. Схема neo4j:// умеет работать и с одним сервером, и с кластером, поэтому её берут по умолчанию. Есть и bolt://, она подключается строго к одному серверу.
Главный объект это сам драйвер. Внутри он держит пул соединений, поэтому его создают один раз на всё приложение и закрывают при выходе, а не на каждый запрос.
Python
Ставим драйвер: pip install neo4j. Скрипт находит знакомых Ани:
from neo4j import GraphDatabase
URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "secret-password")
with GraphDatabase.driver(URI, auth=AUTH) as driver:
driver.verify_connectivity()
records, summary, keys = driver.execute_query(
"""
MATCH (p:Person {name: $name})-[:KNOWS]->(friend)
RETURN friend.name AS name, friend.born AS born
ORDER BY name
""",
name="Аня",
database_="neo4j",
)
print(keys)
for record in records:
print(record["name"], record["born"], type(record["born"]).__name__)
$ python friends.py
['name', 'born']
Борис 1990 int
Глеб 1987 int
execute_query сам открывает сессию, выполняет запрос в транзакции и возвращает три вещи: записи, сводку и имена колонок. К полю записи обращаются по имени колонки, как к словарю. Целые числа Cypher стали обычными int в Python.
Параметры вместо склейки строк
В запросе стоит $name, а значение "Аня" передано отдельно. Это параметр. Никогда не вставляй значения в текст запроса через f-строки или +:
# так нельзя
driver.execute_query(f"MATCH (p:Person {{name: '{name}'}}) RETURN p")
Если в name окажется строка с кавычкой и куском Cypher, она станет частью запроса. Это та же инъекция, что и в SQL. С параметром значение всегда остаётся значением. Бонус: сервер кэширует план запроса по его тексту, и запрос с параметром не планируется заново для каждого имени.
Запись и сводка
Запись идёт тем же execute_query. Сводка summary рассказывает, что изменилось, это та же строка итога, что печатает cypher-shell:
from neo4j import GraphDatabase
URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "secret-password")
with GraphDatabase.driver(URI, auth=AUTH) as driver:
summary = driver.execute_query(
"""
MERGE (p:Person {name: $name})
ON CREATE SET p.born = $born
""",
name="Кира",
born=1996,
database_="neo4j",
).summary
print("создано узлов:", summary.counters.nodes_created)
print("записано свойств:", summary.counters.properties_set)
$ python write.py
создано узлов: 1
записано свойств: 2
$ python write.py
создано узлов: 0
записано свойств: 0
Второй запуск ничего не создал: MERGE работает и через драйвер.
JavaScript
Для Node.js драйвер ставится командой npm install neo4j-driver. Тот же запрос:
import neo4j from 'neo4j-driver';
const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'secret-password'));
try {
const { records, keys } = await driver.executeQuery(
`MATCH (p:Person {name: $name})-[:KNOWS]->(friend)
RETURN friend.name AS name, friend.born AS born
ORDER BY name`,
{ name: 'Аня' },
{ database: 'neo4j' },
);
console.log(keys);
for (const record of records) {
console.log(record.get('name'), record.get('born'));
}
console.log(records[0].get('born').toNumber() + 1);
} finally {
await driver.close();
}
$ node friends.mjs
[ 'name', 'born' ]
Борис Integer { low: 1990, high: 0 }
Глеб Integer { low: 1987, high: 0 }
1991
Вот главная ловушка JavaScript. Целые в Neo4j 64-битные, а обычное число JS точно хранит целые только до 2^53. Чтобы не терять точность, драйвер возвращает свой тип Integer. Для вывода и арифметики его превращают через toNumber(), а если все числа заведомо небольшие, можно создать драйвер с настройкой disableLosslessIntegers: true, и он будет отдавать обычные числа.
Несколько запросов в одной транзакции
execute_query выполняет один запрос в одной транзакции. Если нужно несколько запросов, которые должны пройти вместе или не пройти вовсе, открывают сессию и передают функцию с транзакцией: в Python session.execute_write(...), в JS session.executeWrite(...). Драйвер повторит такую функцию сам, если транзакция сорвалась из-за временной ошибки, например потери связи с кластером. Что такое транзакция в Neo4j и чем она защищает, разберём в следующем уроке.
Параметры в песочнице
Параметры есть и в консоли. В cypher-shell и в песочнице их задают командой :param, как будто это значение пришло из приложения:
:param name => 'Аня'
MATCH (p:Person {name: $name})-[:KNOWS]->(friend)
RETURN friend.name AS name, friend.born AS born
ORDER BY name;
neo4j@neo4j> :param name => 'Аня'
neo4j@neo4j> MATCH (p:Person {name: $name})-[:KNOWS]->(friend)
RETURN friend.name AS name, friend.born AS born
ORDER BY name;
+----------------+
| name | born |
+----------------+
| "Борис" | 1990 |
| "Глеб" | 1987 |
+----------------+
2 rows
Ответ тот же, что у скриптов на Python и JS. Команда :param без аргументов покажет все заданные параметры.
Попробуй сам
Песочница с параметрами: поменяй значение $name и выполни снова. Картинка покажет знакомых выбранного участника.
Задай второй параметр :param since => 2019 и добавь условие WHERE k.since >= $since. Выполни :param без аргументов и посмотри список параметров.
Проверь себя
Задание
Дальше
Драйвер выполняет запрос в транзакции, даже если мы об этом не просим. В следующем уроке разберёмся, что это значит в Neo4j: как устроены автоматические и явные транзакции, что происходит при ошибке и как база защищает данные при одновременной записи.
домашка