neo4j-driver-python-skill
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen to Use
适用场景
- Writing Python code that connects to Neo4j
- Setting up driver, sessions, transactions, or async patterns
- Debugging result handling, serialization, or UNWIND batching
- Reviewing Neo4j driver usage in Python code
- 编写连接Neo4j的Python代码
- 设置驱动、会话、事务或异步模式
- 调试结果处理、序列化或UNWIND批量处理
- 审查Python代码中Neo4j驱动的使用
When NOT to Use
不适用场景
- Writing/optimizing Cypher →
neo4j-cypher-skill - Driver version upgrades →
neo4j-migration-skill - GraphRAG pipelines (package) →
neo4j-graphragneo4j-graphrag-skill
- 编写/优化Cypher查询 → 使用
neo4j-cypher-skill - 驱动版本升级 → 使用
neo4j-migration-skill - GraphRAG流水线(包)→ 使用
neo4j-graphragneo4j-graphrag-skill
Installation
安装
bash
pip install neo4j # package name is `neo4j`, NOT `neo4j-driver` (deprecated since v6)
pip install neo4j-rust-ext # optional: 3–10× faster serialization, same APIPython >=3.10 required for v6.x.
bash
pip install neo4j # 包名称为`neo4j`,而非`neo4j-driver`(自v6起已废弃)
pip install neo4j-rust-ext # 可选:序列化速度提升3–10倍,API保持一致v6.x版本要求Python ≥3.10。
Environment Variables
环境变量
Load connection config from environment — never hardcode credentials.
python
import os
from dotenv import load_dotenv # pip install python-dotenv
load_dotenv(".env") # reads NEO4J_URI / NEO4J_USERNAME / NEO4J_PASSWORD / NEO4J_DATABASE
URI = os.getenv("NEO4J_URI", "neo4j://localhost:7687")
USER = os.getenv("NEO4J_USERNAME", "neo4j")
PASSWORD = os.getenv("NEO4J_PASSWORD", "")
DATABASE = os.getenv("NEO4J_DATABASE", "neo4j").envNEO4J_URI=neo4j+s://xxx.databases.neo4j.io
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=secret
NEO4J_DATABASE=neo4jAdd to . Without , use in shell or directly.
.env.gitignorepython-dotenvexportos.getenv从环境中加载连接配置——切勿硬编码凭证。
python
import os
from dotenv import load_dotenv # 需安装python-dotenv:pip install python-dotenv
load_dotenv(".env") # 读取NEO4J_URI / NEO4J_USERNAME / NEO4J_PASSWORD / NEO4J_DATABASE
URI = os.getenv("NEO4J_URI", "neo4j://localhost:7687")
USER = os.getenv("NEO4J_USERNAME", "neo4j")
PASSWORD = os.getenv("NEO4J_PASSWORD", "")
DATABASE = os.getenv("NEO4J_DATABASE", "neo4j").envNEO4J_URI=neo4j+s://xxx.databases.neo4j.io
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=secret
NEO4J_DATABASE=neo4j将添加到中。如果不使用,可在shell中使用命令,或直接使用。
.env.gitignorepython-dotenvexportos.getenvDriver Lifecycle
驱动生命周期
Create one Driver per application. Thread-safe, expensive to create. Never create per-request.
python
from neo4j import GraphDatabase
URI = "neo4j+s://xxx.databases.neo4j.io" # Aura
AUTH = ("neo4j", "password")每个应用只创建一个Driver实例。Driver是线程安全的,创建成本较高。切勿为每个请求创建Driver。
python
from neo4j import GraphDatabase
URI = "neo4j+s://xxx.databases.neo4j.io" # Aura环境
AUTH = ("neo4j", "password")Context manager — preferred for scripts
上下文管理器——脚本中首选方式
with GraphDatabase.driver(URI, auth=AUTH) as driver:
driver.verify_connectivity()
# ... work ...
with GraphDatabase.driver(URI, auth=AUTH) as driver:
driver.verify_connectivity()
# ... 执行操作 ...
Long-lived singleton (service / web app)
长期存在的单例(服务/Web应用)
driver = GraphDatabase.driver(URI, auth=AUTH)
driver.verify_connectivity()
driver = GraphDatabase.driver(URI, auth=AUTH)
driver.verify_connectivity()
on shutdown:
关闭时:
driver.close()
URI schemes:
| Scheme | Use |
|---|---|
| `neo4j+s://` | TLS + cluster routing — **Aura default** |
| `neo4j://` | Unencrypted + cluster routing |
| `bolt+s://` | TLS, single instance |
| `bolt://` | Unencrypted, single instance |
Auth options: `("user", "pass")` tuple, `basic_auth()`, `bearer_auth("jwt")`, `kerberos_auth("b64")`.
---driver.close()
URI协议:
| 协议 | 用途 |
|---|---|
| `neo4j+s://` | TLS加密 + 集群路由 —— **Aura默认协议** |
| `neo4j://` | 未加密 + 集群路由 |
| `bolt+s://` | TLS加密,单实例 |
| `bolt://` | 未加密,单实例 |
认证选项:`("user", "pass")`元组、`basic_auth()`、`bearer_auth("jwt")`、`kerberos_auth("b64")`。
---Choosing the Right API
选择合适的API
| API | Use when | Auto-retry | Streaming |
|---|---|---|---|
| Most queries — simple, safe default | ✅ | ❌ eager |
| Large results / multiple queries in one tx | ✅ | ✅ |
| | ❌ | ✅ |
| asyncio applications | ✅ | ✅ |
| API | 适用场景 | 自动重试 | 流式处理 |
|---|---|---|---|
| 大多数查询——简单、安全的默认选项 | ✅ | ❌ 立即加载 |
| 大结果集 / 单个事务中执行多个查询 | ✅ | ✅ |
| | ❌ | ✅ |
| asyncio应用 | ✅ | ✅ |
execute_query
— Default API
execute_queryexecute_query
—— 默认API
execute_querypython
from neo4j import GraphDatabase, RoutingControlpython
from neo4j import GraphDatabase, RoutingControlTuple unpacking — most common
元组解包——最常用方式
records, summary, keys = driver.execute_query(
"MATCH (p:Person {name: $name})-[:KNOWS]->(f) RETURN f.name AS name",
name="Alice",
routing_=RoutingControl.READ, # route reads to replicas
database_="neo4j", # always specify — saves a round-trip
)
for record in records:
print(record["name"])
print(summary.result_available_after, "ms")
records, summary, keys = driver.execute_query(
"MATCH (p:Person {name: $name})-[:KNOWS]->(f) RETURN f.name AS name",
name="Alice",
routing_=RoutingControl.READ, # 将读请求路由到副本节点
database_="neo4j", # 始终指定——减少一次往返请求
)
for record in records:
print(record["name"])
print(summary.result_available_after, "毫秒")
Write — check counters
写入操作——检查计数器
summary = driver.execute_query(
"CREATE (p:Person {name: $name, age: $age})",
name="Bob", age=30,
database_="neo4j",
).summary
print(summary.counters.nodes_created)
**Trailing-underscore convention** — config kwargs end with `_` (`database_`, `routing_`, `auth_`, `result_transformer_`, `bookmark_manager_`). No query parameter name may end with `_`; pass those via `parameters_={"key_": val}`.
**Never f-string or format Cypher.** Always `$param` — prevents injection and enables plan caching.
`result_transformer_` — reshape before return:
```python
import neo4j
df = driver.execute_query("MATCH (p:Person) RETURN p.name, p.age", database_="neo4j",
result_transformer_=neo4j.Result.to_df)
record = driver.execute_query("MATCH (p:Person {name:$n}) RETURN p", n="Alice", database_="neo4j",
result_transformer_=neo4j.Result.single) # raises if 0 or 2+ resultsResult.single()ResultNotSingleErrorsingle(strict=False)summary = driver.execute_query(
"CREATE (p:Person {name: $name, age: $age})",
name="Bob", age=30,
database_="neo4j",
).summary
print(summary.counters.nodes_created)
**下划线后缀约定**——配置参数以`_`结尾(`database_`、`routing_`、`auth_`、`result_transformer_`、`bookmark_manager_`)。查询参数名称不得以`_`结尾;如需传递此类参数,请使用`parameters_={"key_": val}`。
**切勿使用f-string或格式化字符串拼接Cypher查询**。始终使用`$param`参数化查询——防止注入攻击并启用查询计划缓存。
`result_transformer_`——返回前重塑结果:
```python
import neo4j
df = driver.execute_query("MATCH (p:Person) RETURN p.name, p.age", database_="neo4j",
result_transformer_=neo4j.Result.to_df)
record = driver.execute_query("MATCH (p:Person {name:$n}) RETURN p", n="Alice", database_="neo4j",
result_transformer_=neo4j.Result.single) # 结果为0或2+时抛出异常Result.single()ResultNotSingleErrorsingle(strict=False)Managed Transactions (execute_read
/ execute_write
)
execute_readexecute_write托管事务(execute_read
/ execute_write
)
execute_readexecute_writeUse for large results or multiple queries in one transaction.
python
with driver.session(database="neo4j") as session:
def get_people(tx):
result = tx.run("MATCH (p:Person) WHERE p.name STARTS WITH $pfx RETURN p.name AS name",
pfx="Al")
return [r["name"] for r in result] # consume INSIDE callback — Result invalid after tx closes
names = session.execute_read(get_people)
def create_person(tx):
tx.run("CREATE (p:Person {name: $name})", name="Carol")
session.execute_write(create_person)Result lifetime — is a lazy cursor backed by the open transaction. Returning it unconsumed raises . Always collect to inside the callback.
ResultResultConsumedErrorlistCallback may retry on transient failures — keep callbacks idempotent; move side effects (HTTP calls, emails) outside the callback.
Timeout/metadata via (named functions only — cannot decorate lambdas):
@unit_of_workpython
from neo4j import unit_of_work
@unit_of_work(timeout=5.0, metadata={"app": "svc", "user": user_id})
def get_people(tx):
return [r["name"] for r in tx.run("MATCH (p:Person) RETURN p.name AS name")]
session.execute_read(get_people)适用于大结果集或单个事务中执行多个查询的场景。
python
with driver.session(database="neo4j") as session:
def get_people(tx):
result = tx.run("MATCH (p:Person) WHERE p.name STARTS WITH $pfx RETURN p.name AS name",
pfx="Al")
return [r["name"] for r in result] # 在回调内部消费结果——事务关闭后Result失效
names = session.execute_read(get_people)
def create_person(tx):
tx.run("CREATE (p:Person {name: $name})", name="Carol")
session.execute_write(create_person)Result生命周期——是基于打开事务的惰性游标。未消费就返回会抛出。请始终在回调内部将结果收集为。
ResultResultConsumedErrorlist回调可能会重试——针对临时故障,回调函数需保持幂等;将副作用(HTTP调用、邮件发送等)移到回调外部。
通过设置超时/元数据(仅支持命名函数——无法装饰lambda):
@unit_of_workpython
from neo4j import unit_of_work
@unit_of_work(timeout=5.0, metadata={"app": "svc", "user": user_id})
def get_people(tx):
return [r["name"] for r in tx.run("MATCH (p:Person) RETURN p.name AS name")]
session.execute_read(get_people)Implicit Transactions (session.run
)
session.run隐式事务(session.run
)
session.runNot auto-retried. Use only for , , or quick scripts.
LOAD CSVCALL {} IN TRANSACTIONSpython
with driver.session(database="neo4j") as session:
result = session.run("CREATE (p:Person {name: $name})", name="Alice")
summary = result.consume() # call consume() to guarantee commit before proceeding
print(summary.counters.nodes_created)不会自动重试。仅适用于、或快速脚本。
LOAD CSVCALL {} IN TRANSACTIONSpython
with driver.session(database="neo4j") as session:
result = session.run("CREATE (p:Person {name: $name})", name="Alice")
summary = result.consume() # 调用consume()以确保在继续操作前提交事务
print(summary.counters.nodes_created)Async API
异步API
Mirror of sync API — replace with , every call.
GraphDatabaseAsyncGraphDatabaseawaitpython
from neo4j import AsyncGraphDatabase
import asyncio与同步API镜像——将替换为,并为每个调用添加。
GraphDatabaseAsyncGraphDatabaseawaitpython
from neo4j import AsyncGraphDatabase
import asyncioSingleton — same rule as sync: never create per-request
单例——与同步规则相同:切勿为每个请求创建
driver = AsyncGraphDatabase.driver(URI, auth=AUTH)
async def main():
records, , _ = await driver.execute_query(
"MATCH (p:Person) RETURN p.name AS name",
database="neo4j", routing_=RoutingControl.READ,
)
print([r["name"] for r in records])
await driver.close()
asyncio.run(main())
FastAPI lifespan pattern:
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
_driver = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global _driver
_driver = AsyncGraphDatabase.driver(URI, auth=AUTH)
await _driver.verify_connectivity()
yield
await _driver.close()
app = FastAPI(lifespan=lifespan)Parallel queries with :
asyncio.gatherpython
results = await asyncio.gather(
driver.execute_query("MATCH (a:Artist) RETURN a.name AS name", database_="neo4j"),
driver.execute_query("MATCH (v:Venue) RETURN v.name AS name", database_="neo4j"),
)Never use sync in asyncio — blocks the event loop.
GraphDatabaseFull async patterns → references/async.md
driver = AsyncGraphDatabase.driver(URI, auth=AUTH)
async def main():
records, , _ = await driver.execute_query(
"MATCH (p:Person) RETURN p.name AS name",
database="neo4j", routing_=RoutingControl.READ,
)
print([r["name"] for r in records])
await driver.close()
asyncio.run(main())
FastAPI生命周期模式:
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
_driver = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global _driver
_driver = AsyncGraphDatabase.driver(URI, auth=AUTH)
await _driver.verify_connectivity()
yield
await _driver.close()
app = FastAPI(lifespan=lifespan)使用执行并行查询:
asyncio.gatherpython
results = await asyncio.gather(
driver.execute_query("MATCH (a:Artist) RETURN a.name AS name", database_="neo4j"),
driver.execute_query("MATCH (v:Venue) RETURN v.name AS name", database_="neo4j"),
)切勿在asyncio中使用同步——会阻塞事件循环。
GraphDatabase完整异步模式请查看 → references/async.md
Error Handling
错误处理
python
from neo4j.exceptions import (
Neo4jError, ServiceUnavailable, TransientError,
AuthError, ConstraintError,
)
try:
driver.execute_query("...", database_="neo4j")
except AuthError:
... # bad credentials
except ServiceUnavailable:
... # no servers reachable
except ConstraintError as e:
# unique/existence constraint violation — catch BEFORE Neo4jError (it's a subclass)
print(e.code, e.message)
except TransientError as e:
# raised only after retries exhausted (execute_query retries automatically)
print(e.code)
except Neo4jError as e:
print(e.code, e.message, e.gql_status)Catch before — it is a subclass and will be swallowed otherwise.
ConstraintErrorNeo4jErrorpython
from neo4j.exceptions import (
Neo4jError, ServiceUnavailable, TransientError,
AuthError, ConstraintError,
)
try:
driver.execute_query("...", database_="neo4j")
except AuthError:
... # 凭证错误
except ServiceUnavailable:
... # 无法连接到服务器
except ConstraintError as e:
# 唯一/存在约束违反——需在Neo4jError之前捕获(它是Neo4jError的子类)
print(e.code, e.message)
except TransientError as e:
# 仅在自动重试耗尽后抛出(execute_query会自动重试)
print(e.code)
except Neo4jError as e:
print(e.code, e.message, e.gql_status)需先捕获再捕获——因为它是子类,否则会被后者覆盖。
ConstraintErrorNeo4jErrorResult Access & Null Safety
结果访问与空值安全
python
record = records[0]
record["name"] # by key — KeyError if absent
record[0] # by index
record.get("name") # None for absent key OR graph null
record.get("name", "Unknown")
d = record.data() # dict — values still driver objects for Node/Rel/temporal typesrecord.data()NodeRelationshipPathneo4j.time.*python
undefinedpython
record = records[0]
record["name"] # 通过键访问——键不存在时抛出KeyError
record[0] # 通过索引访问
record.get("name") # 键不存在或图中值为null时返回None
record.get("name", "Unknown")
d = record.data() # 转换为字典——Node/Rel/时间类型仍为驱动对象如果结果包含、、或类型的值,无法直接序列化为JSON。请在Cypher查询中投影标量字段,而非返回整个节点。
NodeRelationshipPathneo4j.time.*record.data()python
undefined❌ raises TypeError on json.dumps
❌ 使用json.dumps时会抛出TypeError
records, , _ = driver.execute_query("MATCH (p:Person) RETURN p", database="neo4j")
json.dumps(records[0].data())
records, , _ = driver.execute_query("MATCH (p:Person) RETURN p", database="neo4j")
json.dumps(records[0].data())
✅ project scalars
✅ 投影标量字段
records, , _ = driver.execute_query(
"MATCH (p:Person) RETURN p.name AS name, p.age AS age", database="neo4j")
json.dumps(records[0].data()) # safe
Node/Relationship/temporal access:
```python
node = record["p"] # neo4j.graph.Node
node.element_id # stable within this transaction only
node.labels # frozenset({'Person'})
dict(node) # all properties as plain dict
rel = record["r"] # neo4j.graph.Relationship
rel.type # 'KNOWS'
dt = record["created_at"] # neo4j.time.DateTime
dt.to_native() # datetime.datetime (loses sub-µs precision)Full type mapping table → references/data-types.md
records, , _ = driver.execute_query(
"MATCH (p:Person) RETURN p.name AS name, p.age AS age", database="neo4j")
json.dumps(records[0].data()) # 安全可序列化
节点/关系/时间类型访问:
```python
node = record["p"] # neo4j.graph.Node对象
node.element_id # 仅在当前事务内稳定
node.labels # 冻结集合({'Person'})
dict(node) # 将所有属性转换为普通字典
rel = record["r"] # neo4j.graph.Relationship对象
rel.type # 关系类型,如'KNOWS'
dt = record["created_at"] # neo4j.time.DateTime对象
dt.to_native() # 转换为datetime.datetime(会丢失亚微秒精度)完整类型映射表请查看 → references/data-types.md
Batch Writes with UNWIND
使用UNWIND进行批量写入
Pass — only shape the driver serializes correctly for .
list[dict]UNWINDpython
people = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
driver.execute_query(
"UNWIND $rows AS row MERGE (p:Person {name: row.name}) SET p.age = row.age",
rows=people,
database_="neo4j",
)Custom objects and dataclasses must be converted to before passing as parameters.
dict传递类型数据——这是驱动为序列化的正确格式。
list[dict]UNWINDpython
people = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
driver.execute_query(
"UNWIND $rows AS row MERGE (p:Person {name: row.name}) SET p.age = row.age",
rows=people,
database_="neo4j",
)自定义对象和数据类必须先转换为,再作为参数传递。
dictPerformance
性能优化
- Always set /
database_— omitting triggers a home-database round-trip per call.database= - routes to replicas automatically; use
execute_readwithrouting_=RoutingControl.READ.execute_query - Batch writes: one callback for the whole list > one tx per item.
execute_write - Large results: stream lazily inside callback;
execute_readis always eager.execute_query
Connection pool tuning:
python
driver = GraphDatabase.driver(URI, auth=AUTH,
max_connection_pool_size=50, # default 100
connection_acquisition_timeout=30, # seconds to wait for free connection
max_connection_lifetime=3600, # seconds; recycles stale connections
connection_timeout=15,
keep_alive=True,
)Session exhaustion: each open session holds a connection. Always use .
with driver.session(...) as sessionFull performance patterns → references/performance.md
- 始终设置/
database_——省略该参数会导致每次调用触发一次主数据库往返请求。database= - 会自动将请求路由到副本节点;使用
execute_read时可配合execute_query。routing_=RoutingControl.READ - 批量写入:对整个列表使用一个回调 > 为每个项创建一个事务。
execute_write - 大结果集:在回调内部惰性流式处理;
execute_read始终是立即加载。execute_query
连接池调优:
python
driver = GraphDatabase.driver(URI, auth=AUTH,
max_connection_pool_size=50, # 默认值100
connection_acquisition_timeout=30, # 等待空闲连接的超时时间(秒)
max_connection_lifetime=3600, # 连接最大存活时间(秒);回收过期连接
connection_timeout=15,
keep_alive=True,
)会话耗尽:每个打开的会话会占用一个连接。请始终使用。
with driver.session(...) as session完整性能优化模式请查看 → references/performance.md
Common Errors
常见错误
| Mistake | Fix |
|---|---|
f-string / | Use |
Param name ending with | Pass via |
Omitting | Always set — saves a round-trip every call |
Returning | Consume to |
Side effects in | Move outside — callback may retry |
| Passing dataclass/Pydantic as param | Convert to |
| |
| |
No | Commit timing undefined; call |
| Sync driver inside asyncio | Use |
| Async driver created per request | Singleton — create once at startup |
| Leaked sessions | |
| Project scalars in Cypher or convert explicitly |
| Index |
| It raises — use |
| Use named function |
| Catch |
| Package is |
| 错误操作 | 修复方法 |
|---|---|
使用f-string / | 始终使用 |
参数名称以 | 通过 |
省略 | 始终设置该参数——每次调用可减少一次往返请求 |
从事务回调中返回 | 在回调内部将结果消费为 |
在 | 将副作用移到回调外部——回调可能会重试 |
| 将数据类/Pydantic对象作为参数传递 | 先转换为 |
| 仅使用 |
使用 | 使用 |
| 提交时间不确定;请调用 |
| 在asyncio中使用同步驱动 | 使用 |
| 为每个请求创建异步驱动 | 使用单例——在应用启动时创建一次 |
| 会话泄漏 | 始终使用 |
对包含节点/时间类型的 | 在Cypher中投影标量字段或显式转换 |
在 | 使用 |
| 它会抛出异常——使用 |
在lambda上使用 | 使用命名函数 |
先捕获 | 先捕获 |
使用 | 自v6起包名称为 |
References
参考资料
Load on demand:
- references/async.md — full async patterns: managed transactions, result methods, concurrency
- references/data-types.md — complete Python↔Cypher type mapping, temporal conversion, graph object API, spatial types (CartesianPoint/WGS84Point)
- references/performance.md — connection pool, lazy streaming, threading vs asyncio, bookmarks/causal consistency
- references/transactions.md — explicit transactions, rollback, commit uncertainty, details
unit_of_work
Docs:
按需加载:
- references/async.md —— 完整异步模式:托管事务、结果方法、并发
- references/data-types.md —— 完整Python↔Cypher类型映射、时间转换、图对象API、空间类型(CartesianPoint/WGS84Point)
- references/performance.md —— 连接池、惰性流式处理、线程vs asyncio、书签/因果一致性
- references/transactions.md —— 显式事务、回滚、提交不确定性、细节
unit_of_work
官方文档:
Checklist
检查清单
- Package installed as (not
neo4j)neo4j-driver - One Driver instance created at startup; shared everywhere
- called at startup
verify_connectivity() - /
database_set on every calldatabase= - placeholders used — no f-strings or
$param.format() - Result consumed inside tx callback (not returned raw)
- Sessions used as context managers ()
with driver.session(...) as session - caught before
ConstraintErrorNeo4jError - used in asyncio code (not sync driver)
AsyncGraphDatabase - Async driver created once at app startup (not per request)
- Side effects outside callbacks
execute_read/write - UNWIND batches use
list[dict]
- 安装的包为(而非
neo4j)neo4j-driver - 在应用启动时创建一个Driver实例,并在各处共享
- 在启动时调用
verify_connectivity() - 每次调用都设置/
database_database= - 使用占位符——不使用f-strings或
$param.format() - 在事务回调内部消费结果(不直接返回原始Result)
- 使用上下文管理器管理会话()
with driver.session(...) as session - 先捕获再捕获
ConstraintErrorNeo4jError - 在asyncio代码中使用(而非同步驱动)
AsyncGraphDatabase - 在应用启动时创建一次异步驱动(不为每个请求创建)
- 将副作用移到回调外部
execute_read/write - UNWIND批量处理使用类型
list[dict]