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

Neo4j - Filter nodes by relationship attribute lists

假设我有 collections (Collection) 附有东西 (Thing)。这些 collection 可以有包含 parent collection 子集的子 collection。 parent 和包含属性列表的子 collection 之间存在 [:FILTERS] 关系,Thing 应根据该属性列表进行过滤。属性键与 Thing 上的键相同,关系上键的值是 Thing 上接受的所有值。如果关系没有任何属性,所有 Things 应该 "shared" 在 collections.

之间

我很难解决的问题是如何根据关系上的属性过滤事物。

图表可能如下所示: Graph view

这里是创建所述图的 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);

假设我想得到应该在 C1_user_img 中的 Things。那将是类型为 image 且用户为 john 的所有 Thing

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_imgcollection.

希望这能解释我的问题。

我刚开始使用 Neo4j,一切对我来说还是很陌生。我已经尝试了很多不同的方法来解决这个问题,但还没有找到有效的方法。

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

例如,如果我尝试:

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']},列表中的所有值都应该被接受。在这种情况下,Things 与用户 '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

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

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

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.

上的属性

更新

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

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 的所有谓词中进行评估。