Neo4j - 按关系属性列表过滤节点

时间:2016-06-09 11:05:02

标签: filter neo4j cypher graph-databases

假设我有集合(Collection),它们附有东西(Thing)。这些集合可以包含子集合,其中包含父集合的子集。父集合和子集合之间存在[:FILTERS]关系,其中包含应过滤Thing的属性列表。属性键与Thing上的键相同,关系上键的值是Thing上接受的所有值。如果关系没有任何属性,则所有Thing应该在集合之间“共享”。

我很难解决的问题是如何通过关系中的属性来过滤事物。

以下是图表的外观: Graph view enter image description here

以下是创建所述图表的Cypher代码:

// Collections
CREATE
(C1:Collection {name: 'C1'})-[:FILTERS {type: ['image']}]->(C1_img:Collection {name:'C1_img'}),
(C1_img)-[:FILTERS {user: ['john']}]->(C1_user_img:Collection {name:'C1_user_img'}),

(C2:Collection {name: 'C2'})-[:FILTERS {type: ['image']}]->(C2_img:Collection {name:'C2_img'}),
(C2_img)-[:FILTERS]->(C1)

// C1 Things
CREATE
(C1I1:Thing {id:1, title:"C1 Thing 1", type:'image', user:'john'})-[:BELONGS_TO]->(C1),
(C1I2:Thing {id:1, title:"C1 Thing 2", type:'image'})-[:BELONGS_TO]->(C1)

// C2 Things
CREATE
(C2I1:Thing {id:1, title:"C2 Thing 1", type:'image'})-[:BELONGS_TO]->(C2),
(C2I2:Thing {id:1, title:"C2 Thing 2", type:'image', user:'john'})-[:BELONGS_TO]->(C2);

让我们说我想得到Thing中的C1_user_img。这将是类型Thing和用户image的所有john

C1_img -> C1_user_img filters on user=john,
C1->C1_img filters on type=image,
C2_img -> C1 has no filter,
C2 -> C2_img filters on type=images

换句话说,Thing的C1I1和C2I2应该在C1_user_img集合中。

希望这能解释我的问题。

我刚刚开始使用Neo4j,对我来说一切都还不错。我已经尝试了很多不同的方法,但还没有找到有效的方法。

我不能只创建一个过滤器列表并按此过滤所有内容,但我需要为每个集合节点过滤内容,因为过滤器可能因集合而异。

例如,如果我尝试:

MATCH (start_node:Collection {name: 'C1_user_img'})<-[thing_filters:FILTERS*]-(collections:Collection)<-[:BELONGS_TO]-(t:Thing)
RETURN thing_filters:FILTERS, t

我找回了一些我可能会合作的东西;每个Thing应该过滤的内容。然后是一个大问题,如何将这些值与Thing节点的值相匹配。我想这里可以使用像all()这样的东西,但由于所有这些都是相当新的,我还没想到它。

可能在Cypher中处理这个问题太复杂了,某些部分需要在代码中完成,但如果只用查询就可以完成它。

修改

由于我给出的示例可能有点过于简单,而且我的解释可能有点缺乏,我正在添加一些更高级的示例。

假设您有一个FILTERS关系中的多个过滤器,则应考虑所有过滤器。此外,由于过滤器是列表,如{user:['john', 'tom']}中所示,因此应接受列表中的所有值。在这种情况下,Thing s与用户'john''tom'。

以下是一些用于测试的其他节点:

MATCH (C2:Collection {name:'C2'}), (C1_img:Collection {name:'C1_img'})
CREATE
(C2I3:Thing {id:12, title:"C2 Thing 3", type:'image', user:'john', extra:'foo'}),
(C1_user_extra_img:Collection {name:'C1_user_extra_img'})
CREATE
(C2I3)-[:BELONGS_TO]->(C2),
(C1_img)-[:FILTERS {user: ['john'], extra: ['foo']}]->(C1_user_extra_img)
return C1_user_extra_img, C2I3

现在获取Thing的所有C1_user_extra_img时,需要同时对userextra进行过滤。还可以添加一个Thing节点和用户'tom',然后在'john'和'tom'上添加关系过滤器,这应该返回所有具有用户'john'或'tom。

1 个答案:

答案 0 :(得分:0)

这是一个有趣的。我找到了一个解决方案 - 也许其他人想出一个更优雅的解决方案:

MATCH (start_node:Collection {name: 'C1_user_img'})<-[thing_filters:FILTERS*]-(collections:Collection)<-[:BELONGS_TO]-(t:Thing)
WITH t, 
reduce(acc=[], x in thing_filters | acc + keys(x)) AS keys,
reduce(acc=[], x in thing_filters | acc + reduce(b=[], y in keys(x) | x[y]))     AS values
WHERE all(x in range(0,size(keys)-1) WHERE t[keys[x]] = values[x])
RETURN t

在匹配路径之后,我们为该路径的关系(keysvalues)构建所有属性键和属性值的集合。

使用all谓词,我们确保keysvalues的所有元素都设置为t上的属性。

更新

如果关系上的属性值是数组,并且条件是事物上的相应属性值需要在该列表中,则需要对现有cypher语句进行小的修改:

MATCH (start_node:Collection {name: 'C1_user_img'})<-[thing_filters:FILTERS*]-(collections:Collection)<-[:BELONGS_TO]-(t:Thing)
WITH t, 
   reduce(acc=[], x in thing_filters | acc + keys(x)) AS keys,
   reduce(acc=[], x in thing_filters | acc + reduce(b=[], y in keys(x) | [x[y]])) AS values
WHERE all(x in range(0,size(keys)-1) WHERE t[keys[x]] in (values[x]) )
RETURN t

通过在reduce中为values引入方括号,我们构建了一个数组数组,这些数组将在WHERE的所有谓词中进行评估。