优化Cypher查询Neo4j
我想用Cypher编写一个查询并在Neo4j上运行它 查询是: 给定一些起始顶点,遍历边并找到所有连接到任一起始顶点的顶点优化Cypher查询Neo4j,neo4j,cypher,graph-databases,Neo4j,Cypher,Graph Databases,我想用Cypher编写一个查询并在Neo4j上运行它 查询是: 给定一些起始顶点,遍历边并找到所有连接到任一起始顶点的顶点 (start)-[*]->(v) 每走一条路,我都会走 if startVertex(E).someproperty != endVertex(E).someproperty, output E. 该图可能包含循环 例如,在上图中,顶点按“group”属性分组。查询应返回7行,表示图中的7条橙色边 如果我自己编写算法,它将是一个简单的深度/广度优先搜索,如果过滤
(start)-[*]->(v)
每走一条路,我都会走
if startVertex(E).someproperty != endVertex(E).someproperty, output E.
该图可能包含循环
例如,在上图中,顶点按“group”属性分组。查询应返回7行,表示图中的7条橙色边
如果我自己编写算法,它将是一个简单的深度/广度优先搜索,如果过滤条件为真,则对访问的每条边输出此边。复杂性为O(V+E)
但是我不能用密码来表达这个算法,因为它是一种非常不同的语言
然后我写了这个查询:
查找所有可到达的顶点
(start)-[*]->(v), reachable = start + v.
从任何可到达的边开始查找所有边。如果边以任何可到达顶点结束并通过过滤器,则输出它
match (reachable)-[]->(n) where n in reachable and reachable.someprop != n.someprop
所以密码是这样的:
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
MATCH(n:Col{schema:${DWMDATA}),表:“CHK_P_T80_ASSET_act_AMT_DD”})
与n匹配(n:Col)-[*]->(m:Col)
使用collect(不同的n)+collect(不同的m)作为c1
将c1作为rn展开
匹配(rn:Col)-[]->(xn:Col),其中rn.schemaxn.schema和c1中的xn
返回rn,xn
这个查询的性能不如我想象的那么好。在:列(架构)上有索引
我正在windows笔记本电脑上从dockerhub运行neo4j 2.3.0 docker映像。实际上它在我笔记本电脑上的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;
VertexQueue1=(起始顶点);
VisitedVertexSet=(空);
EdgeSet1=(空);
While(VertexSet1不是空的)
{
Vertex0=VertexQueue1.pop();
VisitedVertexSet.add(Vertex0);
foreach(从顶点0开始的边0)
{
顶点1=结束顶点(边0);
if(Vertex1.schema Vertex0.schema)
{
EdgeSet1.put(Edge0);
}
if(VisitedVertexSet.notContains(Vertex1)
和VertexQueue1.notContains(Vertex1))
{
VertexQueue1.push(Vertex1);
}
}
}
返回边1;
编辑:
轮廓结果表明,扩展所有路径的成本很高。查看行号,似乎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(开始:Col{table:“F_XXY_DSMK_ITRPNL_IDX_statw})
,(开始)-[*0..]->(上一个:列)-->(节点:列)
其中prev.schemaAnode.schema
返回不同的上一个节点
正确的一点:
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
MATCH(n:Col{schema:${DWMDATA}),表:“CHK_P_T80_ASSET_act_AMT_DD”})
与n匹配(n:Col)-[*]->(m:Col)
使用collect(不同的n)+collect(不同的m)作为c1
将c1作为rn展开
匹配(rn:Col)-[]->(xn:Col),其中rn.schemaxn.schema和c1中的xn
返回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
不过,这种开放式的可变长度关系规范可能很慢
还要注意的是,当Cypher浏览一个特定路径时,它会停止浏览,并发现它被循环回到迄今为止匹配的路径中的某个节点(编辑关系,而不是节点),因此循环不是真正的问题
另外,您传入的DWMDATA
值是插值的吗?如果是这样,您应该考虑使用安全/性能参数:
编辑:
根据你的评论,我有一些想法。首先限制为不同的路径
不会有帮助,因为它找到的每个路径都是不同的。我认为,您需要的是一组不同的对,我认为可以通过向查询中添加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
匹配
(开始:Col{schema:“${DWMDATA}”,表:“CHK_P_T80_ASSET_ACCT_AMT_DD”})
(开始)-[*0..]->(上一个:列)-->(节点:列)
其中prev.schema node.schema
返回DISTINT prev,节点
下面是另一种方法,可能更有效,也可能不更有效,但至少可以让你了解如何以不同的方式行事:
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
匹配
path=(开始:Col{schema:${DWMDATA}),表:“CHK_P_T80_ASSET_ACCT_AMT_DD”}-->(节点:Col)
使用rels(路径)作为rels
将rel作为rel展开
具有明显的相关性
startNode(rel)作为开始节点,endNode(rel)作为结束节点
其中start_node.schema end_node.schema
返回开始节点,结束节点
我不能说这会更快,但这里有另一种尝试方法:
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;
谢谢你的回答:)我需要开放式可变长度关系。我运行了您答案中的第二段代码,它返回了start、prev和node的所有可能路径。在我的示例数据(v=0.1M)上,等待300秒后,查询返回了9M行。我认为对于我的数据,期望的输出应该是大约300行(代表300条不同的边)。我添加了
(prev)->[path]->(node)返回不同的路径
,但它不影响EXPLAIN输出的执行计划。有没有办法告诉neo4j“您只需要访问每个边缘和节点一次”并加快查询速度?通过添加DISTINCT,查询返回了DISTINCT对集,但性能没有提高。似乎执行引擎计算了所有可能的路径,我不需要所有不同的路径,只需要不同的边集就可以了。我添加了一个代码块
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;