Neo4j Cypher查询查找未连接太慢的节点

时间:2016-12-23 11:23:14

标签: graph neo4j cypher

鉴于我们有以下Neo4j架构(简化但它显示了重要的一点)。有两种类型的节点NODEVERSIONVERSION已通过NODE关系与VERSION_OF s连接。 VERSION个节点有两个属性fromuntil表示有效时间跨度 - 其中一个或两个都可以NULL(在Neo4j术语中不存在)表示无限< / em>的。 NODE可以通过HAS_CHILD关系进行连接。同样,这些关系有两个属性fromuntil表示有效时间跨度 - 其中一个或两个都可以NULL(在Neo4j术语中不存在)来表示无限

编辑VERSION个节点和HAS_CHILD关系的有效日期是独立的(即使示例巧合地显示它们已对齐)。

enter image description here

该示例显示了两个NODE s A B A 有两个VERSION s AV1 ,直到2017年6月30日, AV2 从7/1/17开始,而 B 只有一个版本 BV1 无限制。 B 通过HAS_CHILD关系与 A 相关联,直到17/17/17。

现在的挑战是在一个特定时刻查询不是(即根节点)的所有节点的图表。根据上面的示例,如果查询日期是例如,则查询应仅返回 B 。 17/1/17,但如果查询日期为例如,则应返回 B A 8/1/17(因为 A 不再是 B 的孩子,从7/1/17开始)。

今天的当前查询大致类似于:

MATCH (n1:NODE)
OPTIONAL MATCH (n1)<-[c]-(n2:NODE), (n2)<-[:VERSION_OF]-(nv2:ITEM_VERSION)
WHERE (c.from <= {date} <= c.until)
AND (nv2.from <= {date} <= nv2.until)
WITH n1 WHERE c IS NULL 
MATCH (n1)<-[:VERSION_OF]-(nv1:ITEM_VERSION)
WHERE nv1.from <= {date} <= nv1.until
RETURN n1, nv1 
ORDER BY toLower(nv1.title) ASC 
SKIP 0 LIMIT 15

这个查询通常工作得相对较好,但是当它用在大型数据集上时(与真实的生产数据集相比),它开始变慢。使用20-30k NODE s(大约是VERSION s的两倍)(实际)查询在Mac OS X上运行的小型docker容器上大约需要500-700 ms),这是可以接受的。但是使用1.5M NODE s(大约是VERSION s的两倍),(真实)查询在裸机服务器上运行时间超过1分钟(除了Neo4j之外别无其他)。这是不可接受的。

我们可以选择调整此查询吗?是否有更好的方法来处理NODE的版本控制(我怀疑这里是性能问题)还是关系的有效性?我知道关系属性无法编入索引,因此可能有更好的模式来处理这些关系的有效性。

非常感谢任何帮助甚至是最轻微的暗示。

answer from Michael Hunger之后编辑:

  1. 根节点的百分比:

    使用当前示例数据集(1.5M节点),结果集包含大约2k行。那不到1%。

  2. 第一个ITEM_VERSION中的
  3. MATCH节点:

    我们正在使用ITEM_VERSION nv2将结果集过滤到在给定日期没有连接其他ITEM个节点的ITEM个节点。这意味着要么不存在对于给定日期有效的关系,要么连接的项目不能具有对于给定日期有效的ITEM_VERSION。我试图说明这一点:

    // date 6/1/17
    
    // n1 returned because relationship not valid
    (nv1 ...)->(n1)-[X_HAS_CHILD ...6/30/17]->(n2)<-(nv2 ...)
    
    // n1 not returned because relationship and connected item n2 valid
    (nv1 ...)->(n1)-[X_HAS_CHILD ...]->(n2)<-(nv2 ...)
    
    // n1 returned because connected item n2 not valid even though relationship is valid
    (nv1 ...)->(n1)-[X_HAS_CHILD ...]->(n2)<-(nv2 ...6/30/17)
    
  4. 不使用关系类型:

    这里的问题是该软件具有用户定义的模式,ITEM节点通过自定义关系类型连接。由于我们在关系中不能有多种类型/标签,因此这类关系的唯一共同特征是它们都以X_开头。这里没有简化的例子。是否会使用谓词type(r) STARTS WITH 'X_'进行搜索?

2 个答案:

答案 0 :(得分:2)

你在使用什么Neo4j版本。

在您的示例日期,您的1.5M节点中有多少百分比将作为根发现,如果您没有限制,那么有多少数据会回来?也许这个问题不在匹配中,而不是在最后的排序中?

我不确定为什么你的第一部分中有VERSION节点,至少你没有把它们描述为确定根节点的相关性。

您没有使用关系类型。

MATCH (n1:NODE) // matches 1.5M nodes
// has to do 1.5M * degree optional matches
OPTIONAL MATCH (n1)<-[c:HAS_CHILD]-(n2) WHERE (c.from <= {date} <= c.until)
WITH n1 WHERE c IS NULL
// how many root nodes are left?
// # root nodes * version degree (1..2)
MATCH (n1)<-[:VERSION_OF]-(nv1:ITEM_VERSION)
WHERE nv1.from <= {date} <= nv1.until
// has to sort all those
WITH n1, nv1, toLower(nv1.title) as title
RETURN n1, nv1
ORDER BY title ASC 
SKIP 0 LIMIT 15

答案 1 :(得分:1)

我认为改进的良好开端是使用索引匹配节点,这样您就可以快速获得较小的相关节点子集进行搜索。你现在的方法必须每次检查你的所有:NODE以及它们之间的所有关系和模式,正如你所发现的那样,它们不会随着你的数据而扩展。

现在,图中唯一具有日期/时间属性的节点是您的:ITEM_VERSION节点,所以让我们从这些节点开始。您需要一个索引:ITEM_VERSION来自和直到快速查找的属性。

对于您的查找,空值将会出现问题,因为任何针对空值的不等式都会返回null,并且使用空值的大多数变通方法(使用COALESCE()或多个AND / OR用于空案例)似乎会阻止使用索引查找,这是我特别建议的一点。

我建议您使用min和max值替换from和until中的空值,这样可以让您利用索引查找来查找节点:

MATCH (version:ITEM_VERSION)
WHERE version.from <= {date} <= version.until
MATCH (version)<-[:VERSION_OF]-(node:NODE)
...

至少应该在开始时快速访问较小的节点子集以继续查询。