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 3

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, чтобы заботиться о дочерних узлах с перемещенными предками в пути к корню узел