Neo4j Cypher 复杂查询优化
Posted
技术标签:
【中文标题】Neo4j Cypher 复杂查询优化【英文标题】:Neo4j Cypher complex query optimization 【发布时间】:2021-08-22 15:25:42 【问题描述】:现在我有一个包含数百万个节点和数百万个边关系的图表。节点之间存在有向关系。
现在假设节点有两个状态A和B。我想找到路径上所有状态A节点没有状态B。
如下图,有节点A--K,然后E、G、J三个是B类型,其余的都是A类型。 图片链接是https://i.stack.imgur.com/a0yOV.jpg
对于节点E,其上下游遍历如下图,所以节点B、H、K不满足要求。
对于节点G,其上下游遍历如下图,所以节点B、D、K不满足要求。
对于节点J,其上下游遍历如下图,所以节点A、B、C、D、F不满足要求。
所以最后只有节点“I”是满足要求的节点。 图片链接是https://i.stack.imgur.com/A2eqv.jpg
上面例子的case是一个DAG,但实际情况是图中可能存在循环,包括自旋循环(案例1)、AB循环(案例2)、大循环(案例3)、复循环(案例4) 图片链接是https://i.stack.imgur.com/NDpED.jpg 我能写的 Cypher 查询语句
MATCH (n:A)
WHERE NOT exists((n)-[*]->(:B))
AND NOT exists((n)<-[*]-(:B))
RETURN n;
但是这个查询语句在百万节点和百万边的情况下卡住了limit 35
,但最终有3万多个节点满足要求。
显然我的语句占用了太多内存,查询出30+个节点几乎已经占用了所有可用内存,如何写一个更高效的查询?
这是一个例子
CREATE (a:Aid:'a')
CREATE (b:Aid:'b')
CREATE (c:Aid:'c')
CREATE (d:Aid:'d')
CREATE (e:Bid:'e')
CREATE (f:Aid:'f')
CREATE (g:Bid:'g')
CREATE (h:Aid:'h')
CREATE (i:Aid:'i')
CREATE (j:Bid:'j')
CREATE (k:Aid:'k')
MERGE (a)-[:REF]->(c)
MERGE (b)-[:REF]->(c)
MERGE (b)-[:REF]->(d)
MERGE (b)-[:REF]->(e)
MERGE (c)-[:REF]->(f)
MERGE (d)-[:REF]->(g)
MERGE (e)-[:REF]->(g)
MERGE (e)-[:REF]->(h)
MERGE (f)-[:REF]->(i)
MERGE (f)-[:REF]->(j)
MERGE (f)-[:REF]->(k)
MERGE (g)-[:REF]->(k)
MERGE (g)-[:REF]->(j)
使用此代码将得到结果'i'
MATCH (n:A)
WHERE NOT exists((n)-[*]->(:B))
AND NOT exists((n)<-[*]-(:B))
RETURN n;
但是当图中有 800,000 个节点(A 类 400,000 个,B 类 400,000 个)和超过 140 万条边时,此代码无法运行结果
【问题讨论】:
【参考方案1】:一些想法:
我认为这个全局图搜索无法通过单个查询来解决。您将需要某种流程来优化探索并在后续步骤中将结果使用到某个点。 何时可以分配节点标签而不是属性来反映 节点的状态,您可以使用 apoc.path.expandConfig 来 探索路径,直到找到状态为 B 的节点。 在遇到状态 B 的节点之前,您无需重新调查您遍历的状态 A 节点,因为它们不符合要求。考虑到 B 节点的上行或下行路径上的所有节点都无法满足要求,另一种方法可能是这样。仍然假设您使用标签来区分 A 和 B 节点。
MATCH (b:B)
CALL apoc.path.spanningTree(b,
relationshipFilter: "<",
labelFilter:"/B"
) YIELD path
UNWIND nodes(path) AS downStreamNode
WITH b,COLLECT(DISTINCT downStreamNode) AS downStreamNodes
CALL apoc.path.spanningTree(b,
relationshipFilter: ">",
labelFilter:"/B"
) YIELD path
UNWIND nodes(path) AS upStreamNode
WITH b,downStreamNodes+COLLECT(DISTINCT upStreamNode) AS upAndDownStreamNodes
RETURN apoc.coll.toSet(apoc.coll.flatten(COLLECT(upAndDownStreamNodes))) AS allNodesThatDoNotFulfillRequirements
【讨论】:
非常感谢您的回答,我还没有想出如何分步实现这个需求的代码。我目前想用Java实现它的方式是1.将所有环合并到一个节点2.遍历图两次,如果一个节点是A类型,那么它的所有子节点都不满足要求;反转相同 3.统计两次迭代没有标记为不符合的节点个数 这个逻辑能用neo4j实现吗? 我添加了第二种方法,使用生成树。 感谢您的代码!但是当我执行这段代码时,我发现你的代码找到了所有状态为 B 的节点,并没有过滤掉路径上状态为 A 的节点 不会是剩下的那些,不在结果集中的那些 (allNodesThatDoNotFulfillRequirements),是你要找的吗? 我明白你的代码意思是找到所有B类型的节点,但不是所有连接的A类型的节点。“allNodesThatDoNotFulfillRequirements”的意思应该是B类型的所有节点和A类型的所有节点在上游和下游路径上具有类型 B 的节点以上是关于Neo4j Cypher 复杂查询优化的主要内容,如果未能解决你的问题,请参考以下文章