Neo4 Иерархия Обход
У меня есть некоторые иерархические данные, хранящиеся в neo4j, и мне нужен запрос, чтобы найти все дочерние элементы родительского узла для конкретного пользователя. Основной сценарий таков:
(Root Task {control: 1})
(Child Task 2 {control: 2})
(Child Task 3 {control: 3})
(Child Task 4 {control: 4})
Дочерние задачи имеют отношение: CHILD_OF к родителю. Так что все это прекрасно, и я могу получить детей от родителей. Используя следующий запрос, я бы вернул дочернюю Задачу 4.
MATCH (rootTask:Task {control: 3}), (user:User {control: 60})
,(childTask:Task)-[:CHILD_OF*]->(rootTask)
WHERE (user)-[:LINKED_TO]->(childTask)
RETURN childTask
Проблема заключается в том, что пользователь должен перестроить структуру, но только для себя. Так что я ввел новая связь, содержащая ссылку на пользователя. CHILD_OF_ добавляется,и если он существует, то он должен иметь преимущество над отношением CHILD_OF.
Таким образом, если пользователь 60 решает, что задача (дочерняя Задача 4) должна подпадать под (корневую задачу), а не (дочернюю Задачу 3), создаются две ссылки:
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})
Так что теперь, когда я прошу детей (дочерняя Задача 3), для пользователя 60 я не хочу, чтобы (дочерняя Задача 4) возвращалась.
Используя предыдущий запрос и добавление пользовательского отношения и дополнительного ограничения не возвращать дочернюю задачу, если у нее есть пользовательская ссылка на несвязанную задачу, почти работает, ожидайте, что она вернет дочерние элементы (дочерняя Задача 4), потому что у них есть только отношение CHILD_OF, которое связывается. Логика исключения дочерних задач, которые имеют пользовательскую ссылку, также ошибочна, потому что она может фактически указывать на одного и того же родителя.
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)
1 ответ:
Над этим было весело работать. Я составил графист здесь, чтобы продемонстрировать мое предложение:
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
, а затем возвращаю ребенка, если у него нет другого родителя или если другой родитель не находится в пути предков.Я бы чувствовал больше удобно с этим решением, если оно было подкреплено автоматическими тестами,но попробуйте! ;)
Также, как правило, я стараюсь не делать имена переменных отношений. Возможно, вы захотите рассмотреть возможность наличия дополнительного свойства
user_id
в ваших отношенияхCHILD_OF
. В качестве альтернативы вы можете иметь что-то вроде типа отношенийCHILD_OF_FOR_USER
, который имеет свойствоuser_id
.EDIT: я отредактировал запрос выше и GraphGist, чтобы заботиться о дочерних узлах с перемещенными предками в пути к корню узел