寻求 Neo4J Cypher 查询以获取长但(几乎)唯一的路径

Seeking Neo4J Cypher query for long but (nearly) unique paths

我们有一个 Neo4J 数据库,代表一个具有大约 10 万个节点和 20 万个关系的进化过程。节点是世代相传的个体,边代表 parent-child 关系。主要目标是能够在最后一代中获取一个或多个感兴趣的节点,并探索它们的进化历史(大致 "how did we get here?")。

查找所有祖先的 "obvious" 第一个查询不起作用,因为可能的祖先和路径太多 space:

match (a)-[:PARENT_OF*]->(c {is_interesting: true}) 
return distinct a;

所以我们有 pre-processed 数据,所以一些边被标记为 "special" 这样 almost 每个节点最多有一个 "special" parent 边,尽管有时两个 parent 边都标记为 "special"。那么,我希望这个查询能够(有效地)生成沿 "special" 边的(几乎)唯一路径:

match (a)-[r:PARENT_OF* {special: true}]->(c {is_interesting: true}) 
return distinct a;

然而,这仍然慢得无法运行。

这很令人沮丧,因为 "as a human",逻辑很简单:从少量 "interesting" 个节点开始(通常是 1 个,从不超过几十个),然后沿着几乎总是唯一的 "special" 边。假设具有两个 "special" parent 的节点数量非常少,这应该类似于 O(N),其中 N 是过去的代数。

然而,在 Neo4J 中,从一个唯一的 "interesting" 节点返回 25 步,其中 每个 步骤都是唯一的,然而,需要 30 秒,一旦有 单个 分叉(其中两个 parent 都是 "special")随着步骤的变化,它变得更糟。 28 步(让我们到达第一个分叉点)需要 2 分钟,30 步(仍然只有一个分叉点)需要 6 分钟,我什至没有想过尝试完整的 100 步到模拟的开始。

去年的一些类似工作似乎表现更好,但我们使用了各种边缘标签(例如,(a)-[:SPECIAL_PARENT_OF*]->(c) 以及 (a)-[:PARENT_OF*]->(c)),而不是在边缘使用数据字段。查询关系字段值不是一个好主意吗?我们有很多不同的值附加到这个模型中的关系(一些布尔值,一些数字),我们 hoping/assuming 我们可以使用它们来有效地限制搜索,但也许事实并非如此。

非常感谢有关如何调整我们的模型或查询的建议。

更新 我应该提一下,这都是 Neo4J 2.1.7。我打算按照 Brian Underwood 的建议试一试 2.2,然后再报告。

我很幸运地指定了路径长度的限制。因此,如果您知道它永远不会超过 30 跳,您可以尝试:

MATCH (c {is_interesting: true})
WITH c
MATCH (a)-[:PARENT_OF*1..30]->c 
RETURN DISTINCT a

另外,is_interesting 属性 上有索引吗?当然,这也可能导致运行缓慢。

您使用的是哪个版本的 Neo4j?如果您正在使用或升级到 2.2.0,则可以使用新的查询分析工具:

http://neo4j.com/docs/2.2.0/how-do-i-profile-a-query.html

此外,如果您在 Web 控制台中使用它们,您会得到一个漂亮的图形树状事物(技术术语)来显示每个步骤。

在使用 Neo4J 2.2 中的分析工具探索之后(感谢 Brian Underwood 的提示)很明显(目前)Neo4J 没有对边缘属性进行任何预过滤,这会导致令人讨厌的具有长路径的组合爆炸。

例如原查询:

match (a)-[r:PARENT_OF* {special: true}]->(c {is_interesting: true}) 
return distinct a;

找到所有ac的路径,然后然后消除那些有边的路径不是 special。由于从 ac 有数百万条路径,这是完全不可行的。

如果我改为在 PARENT_OF 边有 {special: true} 的地方添加 IS_SPECIAL 边,那么查询会变得非常快,允许我在不到一秒。

此查询创建所有新边:

match (a)-[r:PARENT_OF {special: true}]->(b) 
create (a)-[:IS_SPECIAL]->(b);

不到一秒钟就可以在我们的图表中添加 91K 个关系。

然后

match (c {is_interesting: true}) 
with c 
match (a)-[:IS_SPECIAL*]->(c) 
return distinct a;

用不到一秒钟的时间找到从唯一目标节点 c 返回的 "special" 路径上的 112 个节点。首先匹配 c 并使用 with c 限制节点集似乎也很重要,因为 Neo4J 似乎也没有对节点属性进行预过滤,如果有多个 "interesting"目标节点的速度变慢了很多。