优化Cypher查询Neo4j

时间:2015-11-18 02:56:37

标签: neo4j cypher graph-databases

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

查询是:

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

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

为每条边走边

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

图表可能包含周期。

example query

例如,在上图中,顶点按“group”属性分组。查询应该返回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(架构)

我在windows笔记本电脑上运行了来自dockerhub的neo4j 2.3.0 docker image。实际上它在我的笔记本电脑上的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引擎返回所有路径,但我只想要distint edge list。

左一:

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

profile result

3 个答案:

答案 0 :(得分:2)

我认为,如果我了解查询,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浏览特定路径时,它会停止发现它已经回到某个节点(编辑 关系,而不是 > 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

答案 1 :(得分:1)

我不能说这会更快,但这是尝试的另一种方式:

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获取有关查询的哪些部分较慢的报告。

答案 2 :(得分:1)

// 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;