Neo4j中转递关系实现:移除Middleware节点后服务直连客户端与服务器
问题解答
思路是否正确?
完全合理。如果你的Middleware节点仅作为服务转发的中间层,没有需要保留的业务属性或独立逻辑,将其移除并让服务直接关联客户端与后端服务,能简化架构视图,更清晰地体现业务服务的直接依赖关系,这种操作在图建模里属于节点扁平化/路径折叠,是常见的优化手段。
实现方式
方式一:修改已存在的图数据(用Cypher语句)
如果已经创建了包含Middleware的图,直接执行以下Cypher脚本就能完成转换:
// 匹配Client_A到Middleware的关系,以及Middleware到对应Server的同类型关系 MATCH (client:system {name: 'Client_A'})-[r1]->(middleware:system {name: 'Middleware'}) MATCH (middleware)-[r2]->(server:system) WHERE type(r1) = type(r2) // 确保前后关系的服务类型一致 // 创建Client_A到Server的同类型关系,保留原关系属性 CREATE (client)-[newRel:`type(r1)` {type: r1.type}]->(server) // 删除原有的中间关系和Middleware节点 DELETE r1, r2, middleware
执行后,Client_A会通过Service_A直接连接Server_X,Service_B连接Server_Y,Service_C连接Server_Z,Middleware节点和旧关系会被移除。
方式二:修改Python代码,直接创建直连的图
如果是重新构建图,直接跳过Middleware节点的创建,让服务直接关联Client和Server即可。同时修复原代码中Cypher字符串拼接易导致注入的问题,改用参数化查询:
from neo4j import GraphDatabase import logging from neo4j.exceptions import ServiceUnavailable class App: def __init__(self, uri, user, password): self.driver = GraphDatabase.driver(uri, auth=(user, password)) def close(self): self.driver.close() def drop_everything(self): with self.driver.session(database="neo4j") as session: session.execute_write(self._drop_everything) print("Graph DB dropped") @staticmethod def _drop_everything(tx): query = "MATCH (n) DETACH DELETE (n)" try: tx.run(query) except ServiceUnavailable as exception: logging.error(f"{query} raised an error: \n {exception}") raise def create_system(self, system_name): with self.driver.session(database="neo4j") as session: result = session.execute_write(self._create_system, system_name) print(f"Created system: {result}") @staticmethod def _create_system(tx, system_name): query = """ CREATE (n:system{name:$system_name}) RETURN n.name """ result = tx.run(query, system_name=system_name) try: return result.single()[0] except ServiceUnavailable as exception: logging.error(f"{query} raised an error: \n {exception}") raise def create_flow(self, service_name, system1_name, system2_name): with self.driver.session(database="neo4j") as session: result = session.execute_write(self._create_flow, service_name, system1_name, system2_name) print(f"Created flow: {result}") @staticmethod def _create_flow(tx, service_name, system1_name, system2_name): # 改用参数化查询,避免字符串拼接注入风险 query = """ MATCH (p1:system {name: $system1_name}), (p2:system {name: $system2_name}) CREATE (p1)-[r:$service_name {type:'call'}]->(p2) RETURN $service_name """ result = tx.run(query, service_name=service_name, system1_name=system1_name, system2_name=system2_name) try: return result.single()[0] except ServiceUnavailable as exception: logging.error(f"{query} raised an error: \n {exception}") raise def find_system(self, system_name): with self.driver.session(database="neo4j") as session: result = session.execute_read(self._find_system, system_name) for row in result: print(f"Found system: {row}") @staticmethod def _find_system(tx, system_name): query = """ MATCH (p:system) WHERE p.name = $system_name RETURN p.name AS name """ result = tx.run(query, system_name=system_name) return [{row["name"]} for row in result] if __name__ == "__main__": uri = "neo4j+s://8a9f54ab.databases.neo4j.io" user = "neo4j" password = "Xxx" app = App(uri, user, password) app.drop_everything() # 只创建需要的节点,去掉Middleware app.create_system("Client_A") app.create_system("Server_X") app.create_system("Server_Y") app.create_system("Server_Z") # 直接创建Client到Server的服务连接 app.create_flow("Service_A", "Client_A", "Server_X") app.create_flow("Service_B", "Client_A", "Server_Y") app.create_flow("Service_C", "Client_A", "Server_Z") app.close()
注意事项
- 如果Middleware节点有需要保留的属性或其他关联关系,建议不要直接删除,而是考虑用查询时跳过Middleware节点(比如
MATCH (client)-*->(server) WHERE NOT (client)-[:*]->(:system {name:'Middleware'})->(:*)->(server)),避免丢失信息。 - 原代码中的Cypher字符串拼接存在安全风险(注入攻击),修改后的代码用参数化查询解决了这个问题,这是Neo4j开发的最佳实践。
内容的提问来源于stack exchange,提问作者Mario Stefanutti
相关产品推荐
相关产品推荐

