对路径列表进行过滤会影响列表变量的内容

时间:2018-10-24 16:17:58

标签: filter neo4j cypher collect

我正在Windows上使用neo4j Community Edition,版本3.4.0。

我有一个简单的用例,我希望收集许多路径结果,将它们组合成一个列表,然后处理该列表的内容。 我希望过滤特定节点属性的公共列表,并根据过滤器的类别处理那些节点。 然后,我希望将更多过滤器应用于公用列表,并以类似方式处理结果节点。 某些节点可能会被多个过滤器选中,因此重要的是,一个过滤器不要从公共列表中删除任何节点。

我遇到的问题是,在第一个过滤器之后,公共列表的内容将减少为仅包含与该过滤器匹配的节点的那些路径。 看来过滤器正在影响它正在解析的列表的内容,而不仅仅是返回符合过滤条件的新节点列表。

以下查询是人为设计的,但它们说明了我面临的问题:

创建测试数据:

CREATE (b:B)-[:FollowedBy]->(c:C)-[:FollowedBy]->(d:D)
RETURN b, c, d;

查询:

// Establish two related paths
MATCH p1 = (:B)-[:FollowedBy]->(c)
MATCH p2 = (c)-[:FollowedBy]->()

// Join the two paths to create a single list
WITH collect(p1) + collect(p2) AS pList

// Unwind the common list so that it can be filtered for specific categories
UNWIND pList AS path 

// Filter for nodes in the 'D' category
WITH filter(n1 IN nodes(path) WHERE 'D' IN labels(n1)) AS dNodes, pList, path 

// Unwind the filtered set of 'D' nodes so that they can be processed
UNWIND dNodes AS dNode 
// ... do some dNode stuff

// Filter for nodes in the 'B' category
WITH filter(n2 IN nodes(path) WHERE 'B' IN labels(n2)) AS bNodes, pList, path 

// Unwind the filtered set of 'B' nodes so that they can be processed
UNWIND bNodes AS bNode 
// ... do some bNode stuff

RETURN path, pList;

如果我运行此查询,将返回0行。

实际发生的是:

1)收集并连接了两个路径后,公用列表“ pList”看起来像预期的那样。它返回具有两个路径元素的单个集合。

+------------------------------------------------------+
| pList                                                |
+------------------------------------------------------+
| [(:B)-[:FollowedBy]->(:C), (:C)-[:FollowedBy]->(:D)] |
+------------------------------------------------------+

2)将pList展开到路径后,pList现在包含两个相同的记录,每个路径值对应一个记录-请有人解释为什么是这种情况,即为什么“将pList展开到路径”会影响pList本身?:

+---------------------------------------------------------------------------------+
| pList                                                | path                     |
+---------------------------------------------------------------------------------+
| [(:B)-[:FollowedBy]->(:C), (:C)-[:FollowedBy]->(:D)] | (:B)-[:FollowedBy]->(:C) |
| [(:B)-[:FollowedBy]->(:C), (:C)-[:FollowedBy]->(:D)] | (:C)-[:FollowedBy]->(:D) |
+---------------------------------------------------------------------------------+

3)在过滤了“ D”类中的节点并展开结果列表“ dNodes”之后,pList的内容是单个记录,并且路径现在仅包含与过滤后的节点相对应的路径?

+---------------------------------------------------------------------------------+
| pList                                                | path                     |
+---------------------------------------------------------------------------------+
| [(:B)-[:FollowedBy]->(:C), (:C)-[:FollowedBy]->(:D)] | (:C)-[:FollowedBy]->(:D) |
+---------------------------------------------------------------------------------+

4)在过滤了“ B”类中的节点并展开结果列表“ bNodes”之后,返回pList或path导致零行。这意味着不可能处理“ B”节点?

我想我对Cypher如何处理变量和过滤器有一个基本的误解,如果有人能解释我上面描述的行为,我将不胜感激。

另外,考虑到我的要求,我应该怎么做?我可以执行多个查询,但是看来我的要求很简单,因此我应该能够一次完成整个过程。

谢谢。

2 个答案:

答案 0 :(得分:1)

在步骤2中,pList尚未更改(进入2条相同的记录)。

在那一步,您只有pListpath作为变量。 Neo4j会将每种可能的变量值组合表示为单独的数据行,并处理每一行。由于只有一个pList值和2个path值,因此将导致两行数据,这正是您在#2的表中显示的。

此外,您没有显示完整的Cypher代码,因此未知为什么整个查询什么都不返回。可能有一个未显示的MATCH子句不匹配,这将中止查询的其余部分。

答案 1 :(得分:1)

cybersam回答了您的#2问题。

对于#3和#4,重要的是要了解UNWIND将为列表的每个元素提供一行,并且在处理空列表时,它将擦除该行(没有元素,因此没有行)。这是您取消过滤器的结果时发生的事情,因为一个路径没有:D节点(因此该行被删除了),而其余路径没有:B节点(并且被删除了)。 / p>

我们有一个entry describing this in the documentation,以及一个变通方法,以防您希望在列表为空的情况下使行保持null结果。

对于您而言,最好使用FOREACH处理过滤的节点列表(前提是您仅使用SET,CREATE,MERGE,REMOVE或DELETE):

MATCH p1 = (:B)-[:FollowedBy]->(c)
MATCH p2 = (c)-[:FollowedBy]->()

WITH collect(p1) + collect(p2) AS pList

UNWIND pList AS path 

FOREACH(dNode in [n in nodes(path) WHERE n:D] | 
// ... do some dNode stuff
)

FOREACH(bNode in [n in nodes(path) WHERE n:B] | 
// ... do some bNode stuff
)

RETURN path, pList;

否则,如果要处理dNode和bNode的事情比较复杂,可以在链接文档中使用技巧,在过滤列表为空时使用UNWIND [null]CASE