Neo4 层级遍历
Neo4 Hierarchy Traversal
我在 neo4j 中存储了一些分层数据,需要查询以查找特定用户的 parent 节点的所有 children。基本场景是:
(Root Task {control: 1})
(Child Task 2 {control: 2})
(Child Task 3 {control: 3})
(Child Task 4 {control: 4})
child 任务与 parent 有 :CHILD_OF
关系。所以这一切都很好,我能够得到 parent 的 children。使用以下查询,我将得到 Child Task 4 returned.
MATCH (rootTask:Task {control: 3}), (user:User {control: 60})
,(childTask:Task)-[:CHILD_OF*]->(rootTask)
WHERE (user)-[:LINKED_TO]->(childTask)
RETURN childTask
问题在于用户需要 re-arrange 结构,但仅限于他自己。所以我引入了一个新的关系,其中包含对用户的引用。 CHILD_OF_<usercontrol>
被添加,如果它存在,它应该优先于 CHILD_OF
关系。
因此,如果用户 60 决定(Child 任务 4)任务应属于(根任务)而不是(Child 任务 3),则有两个 link创建:
MERGE (Level 4 task)-[:CHILD_OF]->(Child Task 3)
MERGE (Level 4 task)-[:CHILD_OF_60]->(Root Task)
他现在的观点基本是:
(Root Task {control: 1})
(Child Task 2 {control: 2})
(Child Task 3 {control: 3})
(Child Task 4 {control: 4})
所以现在当我要求 children of (Child Task 3) 时,对于用户 60 我不想要 (Child Task 4) return编辑
使用之前的查询并添加特定于用户的关系和附加约束以不return一个child任务,如果它有一个特定于用户的link到一个未绑定的任务几乎可以工作,预计它会 return children of (Child Task 4) 因为他们只有 CHILD_OF 关系 linked 到。排除具有用户特定 link 的 child 任务的逻辑也有缺陷,因为它实际上可能指向相同的 parent.
MATCH (rootTask:Task {control: 3}), (user:User {control: 60})
,(childTask:Task)-[:CHILD_OF|CHILD_OF_60*]->(rootTask)
WHERE (user)-[:LINKED_TO]->(childTask)
AND NOT (childTask)-[:CHILD_OF_60]-(:Task)
RETURN childTask
我需要的逻辑的本质是如果任务存在关系 CHILD_OF_60 则遵循该关系并忽略默认的 CHILD_OF 关系。
测试数据
MERGE (ruan :User {control: 50, fullname: 'Ruan'})
MERGE (amber :User {control: 60, fullname: 'Amber'})
MERGE (task1 :Task {control: 1, subject: 'Root Task:'})
MERGE (task2 :Task {control: 2, subject: 'Child of Root:'})
MERGE (task3 :Task {control: 3, subject: 'User properties'})
MERGE (task4 :Task {control: 4, subject: 'User parent links'})
MERGE (task5 :Task {control: 5, subject: 'Hierarchy Traversal'})
MERGE (task6 :Task {control: 6, subject: 'Parent'})
MERGE (task7 :Task {control: 7, subject: 'Child'})
MERGE (task8 :Task {control: 8, subject: 'Query1'})
MERGE (task2)-[:CHILD_OF]->(task1)
MERGE (task3)-[:CHILD_OF]->(task2)
MERGE (task4)-[:CHILD_OF]->(task2)
MERGE (task5)-[:CHILD_OF]->(task2)
MERGE (task6)-[:CHILD_OF]->(task5)
MERGE (task7)-[:CHILD_OF]->(task5)
MERGE (task8)-[:CHILD_OF]->(task7)
MERGE (ruan)-[:LINKED_TO]->(task1)
MERGE (ruan)-[:LINKED_TO]->(task2)
MERGE (ruan)-[:LINKED_TO]->(task3)
MERGE (ruan)-[:LINKED_TO]->(task4)
MERGE (ruan)-[:LINKED_TO]->(task5)
MERGE (ruan)-[:LINKED_TO]->(task6)
MERGE (ruan)-[:LINKED_TO]->(task7)
MERGE (ruan)-[:LINKED_TO]->(task8)
MERGE (amber)-[:LINKED_TO]->(task1)
MERGE (amber)-[:LINKED_TO]->(task2)
MERGE (amber)-[:LINKED_TO]->(task3)
MERGE (amber)-[:LINKED_TO]->(task4)
MERGE (amber)-[:LINKED_TO]->(task5)
MERGE (amber)-[:LINKED_TO]->(task6)
MERGE (amber)-[:LINKED_TO]->(task7)
MERGE (amber)-[:LINKED_TO]->(task8)
MERGE (task2)-[:CHILD_OF]->(task1)
MERGE (task3)-[:CHILD_OF]->(task2)
MERGE (task4)-[:CHILD_OF]->(task2)
MERGE (task5)-[:CHILD_OF]->(task2)
MERGE (task6)-[:CHILD_OF]->(task5)
MERGE (task7)-[:CHILD_OF]->(task5)
MERGE (task8)-[:CHILD_OF]->(task7)
MERGE (task5)-[:CHILD_OF_60]->(task1)
MERGE (task3)-[:CHILD_OF_60]->(task1)
这是一项有趣的工作。我在这里制作了一个 GraphGist 来证明我的建议:
http://graphgist.neo4j.com/#!/gists/54d8b5ef8cfb85197aa4
但我也会把解决方案放在这里:
MATCH
(rootTask:Task { control: 1 }),
path=(childTask:Task)-[:CHILD_OF|CHILD_OF_60*1..]->rootTask,
(user:User { control: 60 })-[:LINKED_TO]->childTask
WITH childTask, path AS the_path, path
UNWIND nodes(path) AS node
OPTIONAL MATCH node-[:CHILD_OF_60]->(otherParent:Task)
WITH childTask, the_path, collect(otherParent IS NULL OR otherParent IN nodes(the_path))[0..-1] AS otherParentResults
WHERE ALL(result IN otherParentResults WHERE result)
RETURN DISTINCT childTask
基本上我正在获取路径,通过 CHILD_OF_60
关系检查叶节点是否有另一个父节点,然后返回子节点,如果它没有另一个父节点或者 otherParent 是不在祖先路径中。
如果这个解决方案得到自动化测试的支持,我会觉得更舒服,但请尝试一下! ;)
此外,作为一项规则,我尽量不创建变量关系名称。您可能需要考虑在您的 CHILD_OF
关系中设置一个可选的 user_id
属性。或者你可能有类似 CHILD_OF_FOR_USER
的关系类型,它有 user_id
属性.
编辑:我编辑了上面的查询和 GraphGist 以处理根节点路径中具有移动祖先的子节点
我在 neo4j 中存储了一些分层数据,需要查询以查找特定用户的 parent 节点的所有 children。基本场景是:
(Root Task {control: 1})
(Child Task 2 {control: 2})
(Child Task 3 {control: 3})
(Child Task 4 {control: 4})
child 任务与 parent 有 :CHILD_OF
关系。所以这一切都很好,我能够得到 parent 的 children。使用以下查询,我将得到 Child Task 4 returned.
MATCH (rootTask:Task {control: 3}), (user:User {control: 60})
,(childTask:Task)-[:CHILD_OF*]->(rootTask)
WHERE (user)-[:LINKED_TO]->(childTask)
RETURN childTask
问题在于用户需要 re-arrange 结构,但仅限于他自己。所以我引入了一个新的关系,其中包含对用户的引用。 CHILD_OF_<usercontrol>
被添加,如果它存在,它应该优先于 CHILD_OF
关系。
因此,如果用户 60 决定(Child 任务 4)任务应属于(根任务)而不是(Child 任务 3),则有两个 link创建:
MERGE (Level 4 task)-[:CHILD_OF]->(Child Task 3)
MERGE (Level 4 task)-[:CHILD_OF_60]->(Root Task)
他现在的观点基本是:
(Root Task {control: 1})
(Child Task 2 {control: 2})
(Child Task 3 {control: 3})
(Child Task 4 {control: 4})
所以现在当我要求 children of (Child Task 3) 时,对于用户 60 我不想要 (Child Task 4) return编辑
使用之前的查询并添加特定于用户的关系和附加约束以不return一个child任务,如果它有一个特定于用户的link到一个未绑定的任务几乎可以工作,预计它会 return children of (Child Task 4) 因为他们只有 CHILD_OF 关系 linked 到。排除具有用户特定 link 的 child 任务的逻辑也有缺陷,因为它实际上可能指向相同的 parent.
MATCH (rootTask:Task {control: 3}), (user:User {control: 60})
,(childTask:Task)-[:CHILD_OF|CHILD_OF_60*]->(rootTask)
WHERE (user)-[:LINKED_TO]->(childTask)
AND NOT (childTask)-[:CHILD_OF_60]-(:Task)
RETURN childTask
我需要的逻辑的本质是如果任务存在关系 CHILD_OF_60 则遵循该关系并忽略默认的 CHILD_OF 关系。
测试数据
MERGE (ruan :User {control: 50, fullname: 'Ruan'})
MERGE (amber :User {control: 60, fullname: 'Amber'})
MERGE (task1 :Task {control: 1, subject: 'Root Task:'})
MERGE (task2 :Task {control: 2, subject: 'Child of Root:'})
MERGE (task3 :Task {control: 3, subject: 'User properties'})
MERGE (task4 :Task {control: 4, subject: 'User parent links'})
MERGE (task5 :Task {control: 5, subject: 'Hierarchy Traversal'})
MERGE (task6 :Task {control: 6, subject: 'Parent'})
MERGE (task7 :Task {control: 7, subject: 'Child'})
MERGE (task8 :Task {control: 8, subject: 'Query1'})
MERGE (task2)-[:CHILD_OF]->(task1)
MERGE (task3)-[:CHILD_OF]->(task2)
MERGE (task4)-[:CHILD_OF]->(task2)
MERGE (task5)-[:CHILD_OF]->(task2)
MERGE (task6)-[:CHILD_OF]->(task5)
MERGE (task7)-[:CHILD_OF]->(task5)
MERGE (task8)-[:CHILD_OF]->(task7)
MERGE (ruan)-[:LINKED_TO]->(task1)
MERGE (ruan)-[:LINKED_TO]->(task2)
MERGE (ruan)-[:LINKED_TO]->(task3)
MERGE (ruan)-[:LINKED_TO]->(task4)
MERGE (ruan)-[:LINKED_TO]->(task5)
MERGE (ruan)-[:LINKED_TO]->(task6)
MERGE (ruan)-[:LINKED_TO]->(task7)
MERGE (ruan)-[:LINKED_TO]->(task8)
MERGE (amber)-[:LINKED_TO]->(task1)
MERGE (amber)-[:LINKED_TO]->(task2)
MERGE (amber)-[:LINKED_TO]->(task3)
MERGE (amber)-[:LINKED_TO]->(task4)
MERGE (amber)-[:LINKED_TO]->(task5)
MERGE (amber)-[:LINKED_TO]->(task6)
MERGE (amber)-[:LINKED_TO]->(task7)
MERGE (amber)-[:LINKED_TO]->(task8)
MERGE (task2)-[:CHILD_OF]->(task1)
MERGE (task3)-[:CHILD_OF]->(task2)
MERGE (task4)-[:CHILD_OF]->(task2)
MERGE (task5)-[:CHILD_OF]->(task2)
MERGE (task6)-[:CHILD_OF]->(task5)
MERGE (task7)-[:CHILD_OF]->(task5)
MERGE (task8)-[:CHILD_OF]->(task7)
MERGE (task5)-[:CHILD_OF_60]->(task1)
MERGE (task3)-[:CHILD_OF_60]->(task1)
这是一项有趣的工作。我在这里制作了一个 GraphGist 来证明我的建议:
http://graphgist.neo4j.com/#!/gists/54d8b5ef8cfb85197aa4
但我也会把解决方案放在这里:
MATCH
(rootTask:Task { control: 1 }),
path=(childTask:Task)-[:CHILD_OF|CHILD_OF_60*1..]->rootTask,
(user:User { control: 60 })-[:LINKED_TO]->childTask
WITH childTask, path AS the_path, path
UNWIND nodes(path) AS node
OPTIONAL MATCH node-[:CHILD_OF_60]->(otherParent:Task)
WITH childTask, the_path, collect(otherParent IS NULL OR otherParent IN nodes(the_path))[0..-1] AS otherParentResults
WHERE ALL(result IN otherParentResults WHERE result)
RETURN DISTINCT childTask
基本上我正在获取路径,通过 CHILD_OF_60
关系检查叶节点是否有另一个父节点,然后返回子节点,如果它没有另一个父节点或者 otherParent 是不在祖先路径中。
如果这个解决方案得到自动化测试的支持,我会觉得更舒服,但请尝试一下! ;)
此外,作为一项规则,我尽量不创建变量关系名称。您可能需要考虑在您的 CHILD_OF
关系中设置一个可选的 user_id
属性。或者你可能有类似 CHILD_OF_FOR_USER
的关系类型,它有 user_id
属性.
编辑:我编辑了上面的查询和 GraphGist 以处理根节点路径中具有移动祖先的子节点