"""执行 cypher 节点
输入: state['cypher_query']: List[str] 要执行的 cypher 语句
输出: state['cypher_results']: List[List[Dict]] 每条语句一份结果
用 neo4j_manager 的 run_cypher 逐条执行,不用 run_multiple_cypher:后者把整批放进一个
写事务,任意一条报错整批回滚,这里是读查询,一条查不出来不该拖掉其他几条的结果。
结果和 cypher_query 下标一一对应,即使某条报错也占一个空列表的位置 —— 下游要靠下标
把结果对回是哪条语句问出来的。
"""
from typing import Any, Dict, List
from _004_langgraph_more_nodes.agent_state import AgentState
from common.neo4j_manager import neo4j_client
def run_cypher_node(state: AgentState) -> AgentState:
print("cypher执行节点执行开始了")
cypher_query_list = state.get("cypher_query") or []
cypher_results: List[List[Dict[str, Any]]] = []
for cypher_query in cypher_query_list:
try:
rows = neo4j_client.run_cypher(cypher_query)
except Exception as e:
# 查询本身出错不该让整个图挂掉,记下来给这条占位空结果
print(f"cypher 执行失败: {cypher_query[:80]}\n {e}")
rows = []
print(f" {len(rows)} 行 <- {cypher_query[:70]}")
cypher_results.append(rows)
state["cypher_results"] = cypher_results
print(f"完成执行cypher语句: {len(cypher_results)} 条")
return state
if __name__ == "__main__":
state = run_cypher_node(
{
"cypher_query": [
"MATCH (h:Herb)-[:HAS_EFFECT]->(e:Effect) WHERE h.name IN ['人参'] "
"RETURN h.name AS herb, e.name AS effect LIMIT 10",
"MATCH (f:Formula)-[:HAS_INGREDIENT]->(h:Herb) WHERE f.name = '四君子汤' "
"RETURN h.name AS herb LIMIT 10",
"MATCH (s:Source)<-[:FROM_SOURCE]-(f:Formula) WHERE s.name = '《伤寒论》' "
"RETURN f.name AS formula LIMIT 5",
# 图里没有的实体:语句合法,预期 0 行
"MATCH (h:Herb) WHERE h.name = '阿司匹林' RETURN h.name AS herb",
# 语法错误:预期打印失败并占一个空列表
"MATC1H (h:Herb) RETURN h.name",
]
}
)
for i, rows in enumerate(state["cypher_results"], 1):
print(f"\n第 {i} 条 -> {len(rows)} 行")
for row in rows[:5]:
print(f" {row}")
print("======================")
print(state)