寻求 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;
找到所有从a
到c
的路径,然后然后消除那些有边的路径不是 special
。由于从 a
到 c
有数百万条路径,这是完全不可行的。
如果我改为在 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"目标节点的速度变慢了很多。
我们有一个 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;
找到所有从a
到c
的路径,然后然后消除那些有边的路径不是 special
。由于从 a
到 c
有数百万条路径,这是完全不可行的。
如果我改为在 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"目标节点的速度变慢了很多。