Как получить путь по заданной цепочке крошек в Cypher (Neo4j)? - PullRequest
0 голосов
/ 02 ноября 2019

Исходная ситуация

  • Корневое дерево, представляющее структуру в виде каталога
    • корневой узел
    • без циклов
    • без двоичного кода
    • имена каталогов не уникальны
    • каталоги смоделированы, но нет никаких «файлов»
    • граф подключен
    • граф направлен от корня к листьям
  • Модель
    • (:Root)-[:CONTAINS]->(:Directory)-[:CONTAINS*]->(:Directory)
  • Neo4j 3.5.11
  • около 20 уровней в глубину

graph visualization

CREATE
    (root:Root {name:'Root'}),
    (dirA:Directory {name:'dir A'}),
    (dirB:Directory {name:'dir B'}),
    (dirC:Directory {name:'dir C'}),
    (dirD:Directory {name:'dir D'}),
    (dirE:Directory {name:'dir E'}),
    (dirF:Directory {name:'dir F'}),
    (dirG:Directory {name:'dir G'}),
    (root)-[:CONTAINS]->(dirA),
    (root)-[:CONTAINS]->(dirB),
    (dirA)-[:CONTAINS]->(dirC),
    (dirA)-[:CONTAINS]->(dirD),
    (dirD)-[:CONTAINS]->(dirE),
    (dirD)-[:CONTAINS]->(dirF),
    (dirD)-[:CONTAINS]->(dirG);
  • уровень свободы
    • :Root метка также может быть смоделирована как (:Directory name:’Root’), еслиобязательно
    • библиотека apoc приветствуется

Заданный входной параметр

  • список строк непосредственно связанных имен каталогов
    • различное количество каталогов
    • смежные каталоги строки хлебных крошек также напрямую связаны в дереве / графике

пример:

WITH 'dir A/dir D/dir G' as inputString
WITH split(inputString, '/') AS directories
UNWIND
    directories AS directory
RETURN
    directory;

╒═══════════╕
│"directory"│
╞═══════════╡
│"dir A"    │
├───────────┤
│"dir D"    │
├───────────┤
│"dir G"    │
└───────────┘

Задача, которая должна быть решена

Для указанной строки с хлебными крошками ("dir A / dir D / dir G") мне нужен путь представления в Cypher, который будет частью более сложного запроса. Я не могу просто найти в дереве последнюю запись каталога ("dir G") хлебной крошки, потому что имена каталогов не уникальны . Как мой запрос может быть реализован в Cypher?

Ожидаемый результат:

╒═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╕
│"path"                                                                                                         │
╞═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╡
│[{"name":"Root"},{},{"name":"dir A"},{"name":"dir A"},{},{"name":"dir D"},{"name":"dir D"},{},{"name":"dir G"}]│
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

1 Ответ

1 голос
/ 02 ноября 2019

В этом случае я предлагаю, чтобы каждый: узел каталога имел полный путь в качестве свойства, это упростит сопоставление с каталогами и их путями:

MATCH path = (:Root)-[:CONTAINS*]->(d:Directory)
WITH d, [node in tail(nodes(path)) | node.name] as directories
WITH d, apoc.text.join(directories, '/') as pathString
SET d.path = pathString

(вы можете использовать аналогичный запрос дляобновить каталог (и его дочерние элементы) в случаях, когда каталог перемещается в дереве)

С помощью этого набора он позволяет легко сопоставить конечный узел пути, даже если вы не предоставляетечасть пути над интересующим путем (вы не упомянули, всегда ли указанный вами путь простирается от корня или это просто конец пути):

WITH 'dir A/dir D/dir G' as inputString
MATCH (end:Directory)
WHERE end.path ENDS WITH inputString
RETURN end

Так что если :DIRECTORY(path) индексируется, тогда у вас есть быстрый доступ к конечному узлу. Теперь, чтобы найти другие.

Мы можем использовать выражение пути переменной длины, чтобы найти полный путь этих узлов, используя предикат all(), чтобы гарантировать, что каждый узел в пути имеет имя из входных данных разделенияи это проверяется при расширении. Это дает нам пути к нужным нам узлам (тратит впустую только один дополнительный обход к родительскому элементу выше), но это не гарантирует порядок, мы должны фильтровать это после.

Это должно работать с вашимпример графика:

WITH 'dir A/dir D/dir G' as inputString
WITH inputString, split(inputString, '/') as dirNames
MATCH (end:Directory)
WHERE end.path ENDS WITH inputString
MATCH path = (start)-[:CONTAINS*]->(end)
WHERE all(node in nodes(path) WHERE node.name IN dirNames)
WITH path
WHERE length(path) + 1 = size(dirNames) AND [node in nodes(path) | node.name] = dirNames
RETURN path
...