我们有一个Neo4J数据库,代表一个具有大约100K节点和200K关系的进化过程。节点是代中的个体,边缘代表父子关系。主要目标是能够在最后一代中获取一个或多个感兴趣的节点,并探索它们的进化历史(大致上,"我们是如何到达这里的?")。
"显而易见"第一次查询找到他们的祖先并不起作用,因为在这个空间里有太多可能的祖先和路径:
match (a)-[:PARENT_OF*]->(c {is_interesting: true})
return distinct a;
因此我们对数据进行了预处理,以便将某些边缘标记为"特殊"这样几乎每个节点最多只有一个"特殊的"父边缘,虽然偶尔两个父边缘都标记为"特殊"。那么,我希望这个查询能够(有效地)生成(特别)"特殊的"边缘:
match (a)-[r:PARENT_OF* {special: true}]->(c {is_interesting: true})
return distinct a;
然而,这仍然是非常缓慢的。
这令人沮丧,因为"作为人类",逻辑很简单:从少数"有趣的"开始。节点(通常为1,从不超过几十个),并沿着几乎总是独特的特殊"追逐#34;边缘。假设节点数量非常少,有两个"特殊"父母,这应该是O(N),其中N是时代的世代数。
然而,在Neo4J中,从一个独特的"有趣的"回到25步。 每个步骤唯一的节点,需要30秒,并且一旦单个分叉(其中父母都是"特殊")作为步骤的功能,它变得更快。 28步(让我们到达第一个分叉处)需要2分钟,30分钟(那里仍然只有一个分叉)需要6分钟,我甚至没想过尝试完整的100步到达模拟的开始。
去年的一些类似工作似乎表现更好,但我们使用了各种边缘标签(例如(a)-[:SPECIAL_PARENT_OF*]->(c)
以及(a)-[:PARENT_OF*]->(c)
),而不是使用边缘上的数据字段。查询关系字段值是不是一个好主意?我们在这个模型中有一些不同的值附加到一个关系(一些布尔值,一些数字),我们希望/假设我们可以使用它们来有效地限制搜索,但也许情况并非如此。
非常感谢有关如何调整模型或查询的建议。
更新我应该提到,这都是Neo4J 2.1.7。我将根据Brian Underwood的建议尝试2.2尝试并报告。
答案 0 :(得分: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控制台中使用它们,您将获得一个很好的图形树(技术术语),显示每个步骤。
答案 1 :(得分:2)
在使用Neo4J 2.2中的分析工具进行探索之后(感谢Brian Underwood的提示),很明显(目前)Neo4J没有对边缘属性进行任何预过滤,这导致了长路径的令人讨厌的组合爆炸。
例如原始查询:
match (a)-[r:PARENT_OF* {special: true}]->(c {is_interesting: true})
return distinct a;
找到所有从a
到c
的路径,然后消除那些边缘不是{{1}的路径}}。由于从special
到a
有数百万条路径,因此这是完全不可行的。
如果我在c
边缘有IS_SPECIAL
的地方添加PARENT_OF
边缘,那么查询会变得非常快,让我可以在一个下方推回100代左右第二
此查询创建所有新边:
{special: true}
并在一秒钟内在我们的图表中添加91K关系。
然后
match (a)-[r:PARENT_OF {special: true}]->(b)
create (a)-[:IS_SPECIAL]->(b);
需要一秒钟才能找到沿着"特殊"的112个节点。从唯一目标节点match (c {is_interesting: true})
with c
match (a)-[:IS_SPECIAL*]->(c)
return distinct a;
返回的路径。首先匹配c
并使用c
限制节点集似乎也很重要,因为Neo4J似乎也没有对节点属性进行预过滤,并且如果有多个&#34 ;有趣"目标节点事情变得慢得多。