Как вы выполняете рекурсивный запрос в шифровании, где есть условие в отношении пути?
Я пытаюсь настроить новую базу данных графов, чтобы она содержала записи о продуктах и их взаимосвязях с версионными компонентами друг друга. Для каждого продукта может быть много компонентов, и каждый компонент состоит из нескольких версий. Каждая версия может не зависеть ни от одной версии или от многих версий любых других компонентов. Я хочу иметь возможность запрашивать эту базу данных, чтобы выбрать любую версию компонента и определить, от каких других версий компонентов он зависит или что от него зависит.
Структура данных, которую я попытался использовать в своих примерах, еще не определена, поэтому, если более подходящей будет совершенно другая структура, я готов изменить ее. Изначально я рассматривал возможность установки
Я собрал очень простой пример из 3 продуктов (пример данных в конце), с одним типом компонента и 1 версией каждого во всех случаях, кроме одного. Затем я добавил в этот пример только две зависимости: «a» зависит от диапазона версий «b», а одна из версий «b» зависит от версии «c».
Я хотел бы иметь возможность выполнить запрос, чтобы сказать: «дайте мне всех нижестоящих членов, от которых зависит член». Точно так же я хотел бы сделать это и в обратном порядке, что, как я полагаю, достигается путем простого изменения некоторых параметров отношений.
До сих пор я добился этого для одного прыжка (список версий b, от которых зависит a), показанный здесь:
MATCH
p=(a:member{name:'prod_a_comp_1_v_1'})-[d:DEPENDS_ON]->(c:component)<-[v:VERSION_OF]-(b:member) WHERE b.version >= d.version_min AND b.version <= d.version_max
RETURN p
Но я не знаю, как заставить его рекурсивно выполнять этот запрос по результатам этого первого совпадения. Я исследовал переменную длину/глубину, но поскольку в отношении переменной глубины есть условный параметр (DEPENDS_ON), мне не удалось заставить это работать.
Из примера данных при запросе всех нижестоящих зависимостей
В настоящее время я думаю использовать приведенный выше запрос и выполнить повторный вызов базы данных на основе результатов с клиентской стороны (захват циклических циклов и т. д.), но это кажется очень нежелательным.
Образец данных:
CREATE
(prod_a:product {name:'prod_a'}),
(prod_a_comp_1:component {name: 'prod_a_comp_1', type:'comp_1'}),
(prod_a_comp_1)-[:COMPONENT_OF {type:'comp_1'}]->(prod_a),
(prod_a_comp_1_v_1:member {name:'prod_a_comp_1_v_1', type:'comp_1', version:1}),
(prod_a_comp_1_v_1)-[:VERSION_OF {version:1}]->(prod_a_comp_1)
CREATE
(prod_b:product {name:'prod_b'}),
(prod_b_comp_1:component {name: 'prod_b_comp_1', type:'comp_1'}),
(prod_b_comp_1)-[:COMPONENT_OF {type:'comp_1'}]->(prod_b),
(prod_b_comp_1_v_1:member {name:'prod_b_comp_1_v_1', type:'comp_1', version:1}),
(prod_b_comp_1_v_2:member {name:'prod_b_comp_1_v_2', type:'comp_1', version:2}),
(prod_b_comp_1_v_3:member {name:'prod_b_comp_1_v_3', type:'comp_1', version:3}),
(prod_b_comp_1_v_1)-[:VERSION_OF {version:1}]->(prod_b_comp_1),
(prod_b_comp_1_v_2)-[:VERSION_OF {version:2}]->(prod_b_comp_1),
(prod_b_comp_1_v_3)-[:VERSION_OF {version:3}]->(prod_b_comp_1)
CREATE
(prod_c:product {name:'prod_c'}),
(prod_c_comp_1:component {name: 'prod_c_comp_1', type:'comp_1'}),
(prod_c_comp_1)-[:COMPONENT_OF {type:'comp_1'}]->(prod_c),
(prod_c_comp_1_v_1:member {name:'prod_c_comp_1_v_1', type:'comp_1', version:1}),
(prod_c_comp_1_v_1)-[:VERSION_OF {version:1}]->(prod_c_comp_1)
CREATE
(prod_a_comp_1_v_1)-[:DEPENDS_ON {version_min:2, version_max:3}]->(prod_b_comp_1),
(prod_b_comp_1_v_3)-[:DEPENDS_ON {version_min:1, version_max:100}]->(prod_c_comp_1)
2 ответа
Извините, если я неправильно понял ваш вопрос, но я считаю, что это возможно с помощью функции APOC Expand Paths: https://neo4j.com/docs/apoc/current/graph-querying/expand-paths/
Пример Cypher для вашего графика:
MATCH (a:member{name:'prod_a_comp_1_v_1'})
CALL apoc.path.expand(a, ">DEPENDS_ON|<VERSION_OF", null, 1, -1)
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
Пример результатов:
Во-первых, позвольте мне сначала настроить метки ваших узлов, чтобы использовать верблюжий регистр, чтобы следовать предложенному соглашению об именах neo4j. тоже буду переименовывать
member
к гораздо более интуитивной этикетке.
Вы можете изменить свою модель данных, чтобы тип отношения напрямую соединял узлы. Хотя это может привести к гораздо большему количеству взаимосвязей, это также позволит вам пропустить номера версий в пределах минимального/максимального диапазона (например, если некоторые номера версий в диапазоне ненадежны/дорогие/не того цвета и т. д.). Но главная причина сделать это в том, что это сделает ваш вариант использования чрезвычайно простым.
Вот как создать обновленную версию вашего образца данных с предложенными мной корректировками (и с менее избыточной информацией):
CREATE
(a:Product {id: 'a'}),
(a_1:Component {id: 'a_1', type: 'comp_1'})-[:COMPONENT_OF]->(a),
(a_1_1:Version {id: 'a_1_1', version: 1})-[:VERSION_OF]->(a_1)
CREATE
(b:Product {id: 'b'}),
(b_1:Component {id: 'b_1', type: 'comp_1'})-[:COMPONENT_OF]->(b),
(b_1_1:Version {id: 'b_1_1', version:1})-[:VERSION_OF]->(b_1),
(b_1_2:Version {id: 'b_1_2', version:2})-[:VERSION_OF]->(b_1),
(b_1_3:Version {id: 'b_1_3', version:3})-[:VERSION_OF]->(b_1)
CREATE
(c:Product {id:'c'}),
(c_1:Component {id: 'c_1', type: 'comp_1'})-[:COMPONENT_OF]->(c),
(c_1_1:Version {id: 'c_1_1', version: 1})-[:VERSION_OF]->(c_1)
CREATE
(a_1_1)-[:DEPENDS_ON]->(b_1_2),
(a_1_1)-[:DEPENDS_ON]->(b_1_3),
(b_1_3)-[:DEPENDS_ON]->(c_1_1)
Вот как получить версии, от которых зависит версия:
MATCH (v:Version)-[:DEPENDS_ON*]->(x:Version)
WHERE v.id = 'a_1_1'
RETURN x.id
И вот результат:
╒═══════╕
│"x.id" │
╞═══════╡
│"b_1_3"│
├───────┤
│"c_1_1"│
├───────┤
│"b_1_2"│
└───────┘
[ОБНОВЛЯТЬ]
Чтобы уменьшить общее количество отношений, мы можем использовать необязательные узлы, представляющие общие наборы версий. A может использоваться как конечная точка aDEPENDS_ON
отношения, как узлы. И их даже можно связать вместе.
Мы можем изменить приведенный выше пример кода Cypher, заменив его последнийCREATE
пункт со следующим. В этом новом примере VersionSet представляет зависимости, общие для версий.a_1_1
и . Обратите внимание, как наборыs2
иs1
являются частью цепи. Также обратите внимание, как версия зависит отVersion
а такжеVersionSet
. Существует большая гибкость, и мы даже можем обрабатывать цепочку наборов версий с перекрывающимися версиями (зависит отc_1_1
2 разными способами).
...
CREATE
(d:Product {id:'d'}),
(d_1:Component {id: 'd_1', type: 'comp_2'})-[:COMPONENT_OF]->(d),
(d_1_1:Version {id: 'd_1_1', version: 1})-[:VERSION_OF]->(d_1)
CREATE
(s1:VersionSet),
(s1)-[:DEPENDS_ON]->(b_1_2),
(s1)-[:DEPENDS_ON]->(b_1_3),
(s2:VersionSet),
(s2)-[:DEPENDS_ON]->(s1), // chaining 2 sets
(s2)-[:DEPENDS_ON]->(b_1_1)
CREATE
(a_1_1)-[:DEPENDS_ON]->(s1),
(b_1_3)-[:DEPENDS_ON]->(c_1_1),
(d_1_1)-[:DEPENDS_ON]->(s2),
(d_1_1)-[:DEPENDS_ON]->(c_1_1) // redundant dependency
Этот обновленный запрос возвращает различные зависимости версии дляd_1_1
:
MATCH (v:Version)-[:DEPENDS_ON*]->(x:Version)
WHERE v.id = 'd_1_1'
RETURN DISTINCT x.id
Результат:
╒═══════╕
│"x.id" │
╞═══════╡
│"c_1_1"│
├───────┤
│"b_1_1"│
├───────┤
│"b_1_3"│
├───────┤
│"b_1_2"│
└───────┘