一、准备工作

1. 安装 Neo4j Python 驱动

pip install neo4j

✅ 推荐使用虚拟环境(如 venvconda)隔离依赖。

2. 启动 Neo4j 数据库

确保你本地或远程的 Neo4j 实例正在运行(默认端口为 7687)。

默认用户名/密码为 neo4j / password(首次登录需修改密码)。


二、基础操作

1. 连接数据库

使用 GraphDatabase.driver() 创建驱动实例:

from neo4j import GraphDatabase

URI = "bolt://localhost:7687"
AUTH = ("neo4j", "your_password")  # 替换为你的密码

driver = GraphDatabase.driver(URI, auth=AUTH)

🔒 安全提示:不要硬编码密码!建议使用环境变量或配置文件。

import os
AUTH = ("neo4j", os.getenv("NEO4J_PASSWORD"))

2. 执行 Cypher 查询

使用 session.run() 执行查询:

with driver.session() as session:
    result = session.run("MATCH (n) RETURN count(n) AS nodeCount")
    record = result.single()
    print("Total nodes:", record["nodeCount"])
  • session 是轻量级会话对象,自动管理连接池中的连接。
  • 使用 with 语句确保会话正确关闭。

3. 参数化查询(防止注入)

永远不要拼接用户输入到 Cypher 字符串中!

✅ 正确做法:使用参数占位符 $param

def get_person_by_name(tx, name):
    query = "MATCH (p:Person {name: $name}) RETURN p.age"
    result = tx.run(query, name=name)
    return [record["p.age"] for record in result]

with driver.session() as session:
    ages = session.read_transaction(get_person_by_name, "Alice")
    print(ages)

💡 参数通过字典传入,驱动会自动转义,防止 Cypher 注入攻击。

4. 事务管理

Neo4j 驱动支持显式事务和隐式事务。

方式一:隐式事务(推荐)

使用 session.execute_write()session.execute_read()

def create_person(tx, name, age):
    query = "CREATE (p:Person {name: $name, age: $age})"
    tx.run(query, name=name, age=age)

with driver.session() as session:
    session.execute_write(create_person, "Bob", 30)

方式二:显式事务

with driver.session() as session:
    tx = session.begin_transaction()
    try:
        tx.run("CREATE (p:Person {name: 'Charlie'})")
        tx.commit()
    except Exception as e:
        tx.rollback()
        raise e

⚠️ 显式事务需手动 commit()rollback(),否则可能挂起。

5. 结果处理(Record 与 Value)

session.run() 返回 Result 对象,可迭代获取 Record

result = session.run("MATCH (p:Person) RETURN p.name AS name, p.age AS age")

for record in result:
    print(record["name"], record["age"])          # 通过 key 访问
    print(record[0], record[1])                   # 通过索引访问
    print(dict(record))                           # 转为字典
  • Record 类似命名元组,支持键和索引访问。
  • 节点/关系对象可通过 .get() 或直接访问属性。

6. 错误处理

常见异常来自 neo4j.exceptions 模块:

from neo4j import ServiceUnavailable, TransientError, AuthError

try:
    with driver.session() as session:
        session.run("MATCH (n) RETURN n LIMIT 1")
except ServiceUnavailable:
    print("数据库不可用,请检查连接")
except AuthError:
    print("认证失败,请检查用户名/密码")
except TransientError as e:
    print(f"临时错误(如死锁): {e}")
except Exception as e:
    print(f"其他错误: {e}")
  • ServiceUnavailable:网络或服务宕机。
  • TransientError:可重试的错误(如并发冲突)。
  • 建议对 TransientError 实现重试逻辑。

三、最佳实践

1. 使用连接池

GraphDatabase.driver() 默认启用连接池,无需额外配置。但可调整参数:

driver = GraphDatabase.driver(
    URI,
    auth=AUTH,
    max_connection_pool_size=50,
    connection_timeout=30.0,
    max_connection_lifetime=3600  # 连接最大存活时间(秒)
)

📌 生产环境中根据负载调整池大小。

2. 封装为 DAO 类(数据访问对象)

将数据库操作封装为类,提高可维护性:

from neo4j import GraphDatabase
import logging

class PersonDAO:
    def __init__(self, uri, auth):
        self.driver = GraphDatabase.driver(uri, auth=auth)
        logging.basicConfig(level=logging.INFO)

    def close(self):
        self.driver.close()

    def _log_query(self, query, params):
        logging.info(f"Executing: {query} | Params: {params}")

    def create_person(self, name, age):
        def _create(tx, name, age):
            query = "CREATE (p:Person {name: $name, age: $age})"
            self._log_query(query, {"name": name, "age": age})
            tx.run(query, name=name, age=age)
        with self.driver.session() as session:
            session.execute_write(_create, name, age)

    def find_person_by_name(self, name):
        def _find(tx, name):
            query = "MATCH (p:Person {name: $name}) RETURN p.name AS name, p.age AS age"
            self._log_query(query, {"name": name})
            result = tx.run(query, name=name)
            return [dict(record) for record in result]
        with self.driver.session() as session:
            return session.execute_read(_find, name)

使用示例:

dao = PersonDAO("bolt://localhost:7687", ("neo4j", "password"))
dao.create_person("David", 25)
people = dao.find_person_by_name("David")
print(people)
dao.close()

3. 日志记录查询语句(便于调试)

如上例所示,在 DAO 中添加 _log_query() 方法,配合 logging 模块输出查询语句和参数。

🔍 开发阶段开启 INFO 日志,生产环境可关闭或降级为 WARNING。


四、完整示例:用户管理系统

# neo4j_dao.py
from neo4j import GraphDatabase
import logging
import os

logging.basicConfig(level=logging.DEBUG)

class UserDAO:
    def __init__(self):
        uri = os.getenv("NEO4J_URI", "bolt://localhost:7687")
        user = os.getenv("NEO4J_USER", "neo4j")
        password = os.getenv("NEO4J_PASSWORD")
        if not password:
            raise ValueError("NEO4J_PASSWORD 环境变量未设置")
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self.driver.close()

    def _run_query(self, tx, query, **params):
        logging.debug(f"Query: {query} | Params: {params}")
        return tx.run(query, **params)

    def create_user(self, username, email):
        def _create(tx):
            query = """
            CREATE (u:User {username: $username, email: $email, created_at: timestamp()})
            RETURN u
            """
            return self._run_query(tx, query, username=username, email=email).single()
        with self.driver.session() as session:
            return session.execute_write(_create)

    def get_user_by_email(self, email):
        def _get(tx):
            query = "MATCH (u:User {email: $email}) RETURN u"
            return self._run_query(tx, query, email=email).single()
        with self.driver.session() as session:
            return session.execute_read(_get)

# 使用
if __name__ == "__main__":
    dao = UserDAO()
    dao.create_user("alice", "alice@example.com")
    user = dao.get_user_by_email("alice@example.com")
    print(user["u"]["username"] if user else "Not found")
    dao.close()

 


五、总结

主题关键点
连接 使用 GraphDatabase.driver(),避免硬编码凭证
查询 始终使用参数化查询 $param
事务 优先使用 execute_read/write,避免手动事务
错误处理 捕获 ServiceUnavailableTransientError
最佳实践 封装 DAO、启用日志、利用连接池

通过以上方法,你可以安全、高效地在 Python 应用中集成 Neo4j 图数据库。

posted on 2025-12-11 17:44  程序员丁先生  阅读(4)  评论(0)    收藏  举报