优化 Cypher 查询 Neo4j

Optimizing Cypher Query Neo4j

我想在 Cypher 中编写一个查询,运行 它在 Neo4j 中。

查询是:

给定一些起始顶点,遍历边并找到连接到任何起始顶点的所有顶点。

(start)-[*]->(v)

E 走过的每条边

if startVertex(E).someproperty != endVertex(E).someproperty, output E.

该图可能包含循环。

例如,在上图中,顶点按 "group" 属性 分组。查询应该 return 7 行代表图中的 7 条橙色边。

如果我自己写算法就是一个简单的深度/广度优先搜索,如果过滤条件为真,则对于访问的每条边,输出这条边。复杂度为 O(V+E)

但是我无法用 Cypher 表达这个算法,因为它是非常不同的语言。

然后我写了这个查询:

找到所有可达的顶点

(start)-[*]->(v), reachable = start + v. 

找到从任何可达的边开始的所有边。如果边以任何可到达的顶点结束并通过过滤器,则输出它。

match (reachable)-[]->(n) where n in reachable and reachable.someprop != n.someprop

因此 Cypher 代码如下所示:

MATCH (n:Col {schema:"${DWMDATA}",table:"CHK_P_T80_ASSET_ACCT_AMT_DD"})
WITH n MATCH (n:Col)-[*]->(m:Col)
WITH collect(distinct n) + collect(distinct m) AS c1
UNWIND c1 AS rn
MATCH (rn:Col)-[]->(xn:Col) WHERE rn.schema<>xn.schema and xn in c1
RETURN rn,xn

这个查询的性能并没有我想象的那么好。 :Col(schema)

上有索引

我 运行ning neo4j 2.3.0 docker 图片来自 dockerhub 在我的 windows 笔记本电脑上。实际上它 运行 在我笔记本电脑上的 linux 虚拟机上。

我的示例数据是一个包含 0.1M 顶点和 0.5M 边的小型数据集。对于某些起始节点,完成此查询需要 60 秒或更多秒。对优化或重写查询有什么建议吗?谢谢。

下面的代码块就是我想要的逻辑:

 VertexQueue1 = (starting vertexes);
 VisitedVertexSet = (empty);
 EdgeSet1 = (empty);
 While (VertexSet1 is not empty)
 {
     Vertex0 = VertexQueue1.pop();
     VisitedVertexSet.add(Vertex0);
     foreach (Edge0 starting from Vertex0)
     {
           Vertex1 = endingVertex(Edge0);
           if (Vertex1.schema <> Vertex0.schema)
           {
               EdgeSet1.put(Edge0);
           }
           if (VisitedVertexSet.notContains(Vertex1) 
               and VertexQueue1.notContains(Vertex1)) 
           {
               VertexQueue1.push(Vertex1);
           }
     }
 }
 return EdgeSet1;

编辑:

剖析结果表明,展开所有路径成本较高。查看行号,似乎 Cypher exec 引擎 return 是所有路径,但我只想要不同的边缘列表。

剩下一个:

match (start:Col {table:"F_XXY_DSMK_ITRPNL_IDX_STAT_W"})
,(start)-[*0..]->(prev:Col)-->(node:Col)
 where prev.schema<>node.schema 
return distinct prev,node

右一:

MATCH (n:Col {schema:"${DWMDATA}",table:"CHK_P_T80_ASSET_ACCT_AMT_DD"})
WITH n MATCH (n:Col)-[*]->(m:Col)
WITH collect(distinct n) + collect(distinct m) AS c1
UNWIND c1 AS rn
MATCH (rn:Col)-[]->(xn:Col) WHERE rn.schema<>xn.schema and xn in c1
RETURN rn,xn

我认为 Cypher 让这比您预期的要容易得多,如果我理解查询的话。试试这个:

MATCH (start:Col {schema:"${DWMDATA}",table:"CHK_P_T80_ASSET_ACCT_AMT_DD"})-->(node:Col)
WHERE start.schema <> node.schema
RETURN start, node

尽管我不确定您为什么要比较节点上的 schema 属性。起始节点的schema不是由你传入的值固定的吗?

虽然我可能不理解查询。如果您要查找的不仅仅是连接到起始节点的节点,您可以这样做:

MATCH
  (start:Col {schema:"${DWMDATA}",table:"CHK_P_T80_ASSET_ACCT_AMT_DD"})
  (start)-[*0..]->(prev:Col)-->(node:Col)
WHERE prev.schema <> node.schema
RETURN prev, node

不过,开放式可变长度关系规范可能会很慢。

另请注意,当 Cypher 浏览特定路径时,它会停止并发现它已循环回到某个节点(EDIT relationship , 到目前为止匹配的路径中没有 node),所以循环不是真正的问题。

此外,您传入的 DWMDATA 值是否经过插值?如果是这样,您应该考虑使用参数来提高安全性/性能:

http://neo4j.com/docs/stable/cypher-parameters.html

编辑:

根据您的评论,我有几点想法。首先限制为 DISTINCT path 不会有帮助,因为它找到的每条路径都是不同的。我认为,您想要的是一组不同的对,我认为只需将 DISTINCT 添加到查询中即可实现:

MATCH
  (start:Col {schema:"${DWMDATA}",table:"CHK_P_T80_ASSET_ACCT_AMT_DD"})
  (start)-[*0..]->(prev:Col)-->(node:Col)
WHERE prev.schema <> node.schema
RETURN DISTINT prev, node

这是另一种解决方法,可能更有效也可能不更有效,但至少可以让您了解如何以不同的方式处理事情:

MATCH
  path=(start:Col {schema:"${DWMDATA}",table:"CHK_P_T80_ASSET_ACCT_AMT_DD"})-->(node:Col)
WITH rels(path) AS rels
UNWIND rels AS rel
WITH DISTINCT rel
WITH startNode(rel) AS start_node, endNode(rel) AS end_node
WHERE start_node.schema <> end_node.schema
RETURN start_node, end_node

我不能说这样会更快,但可以尝试另一种方法:

MATCH (start:Col)-[*]->(node:Col)
WHERE start.property IN {property_values}

WITH collect(ID(node)) AS node_ids

MATCH (:Col)-[r]->(node:Col)
WHERE ID(node) IN node_ids
WITH DISTINCT r
RETURN startNode(r) AS start_node, endNode(r) AS end_node

我怀疑所有情况下的问题都与开放式可变长度路径有关。实际上,我已经在 Slack 小组上询问过,试图更好地了解它是如何工作的。同时,对于您尝试的所有查询,我建议在它们前面加上 PROFILE 关键字,以便从 Neo4j 获取有关查询的哪些部分速度较慢的报告。

// this is very inefficient!
MATCH (start:Col)-[*]->(node:Col)
WHERE start.property IN {property_values}
WITH distinct node
MATCH (prev)-[r]->(node)
RETURN distinct prev, node;

你可能会更好:

MATCH (start:Col)
WHERE start.property IN {property_values}

MATCH (node:Col)
WHERE shortestPath((start)-[*]->(node)) IS NOT NULL

MATCH (prev)-[r]->(node)
RETURN distinct prev, node;