Как вы выполняете рекурсивный запрос в шифровании, где есть условие в отношении пути?

Я пытаюсь настроить новую базу данных графов, чтобы она содержала записи о продуктах и ​​их взаимосвязях с версионными компонентами друг друга. Для каждого продукта может быть много компонентов, и каждый компонент состоит из нескольких версий. Каждая версия может не зависеть ни от одной версии или от многих версий любых других компонентов. Я хочу иметь возможность запрашивать эту базу данных, чтобы выбрать любую версию компонента и определить, от каких других версий компонентов он зависит или что от него зависит.

Структура данных, которую я попытался использовать в своих примерах, еще не определена, поэтому, если более подходящей будет совершенно другая структура, я готов изменить ее. Изначально я рассматривал возможность установкиотношения непосредственно между участниками. Однако, поскольку со временем будут добавляться новые элементы, если новый элемент будет добавлен и попадет в диапазон version_min и version_max существующего диапазона зависимостей записей, мне тогда нужно будет вернуться и идентифицировать все затронутые записи и обновить их все, что не похоже, что это будет масштабироваться со временем. Именно это привело к идее, что элемент зависит от компонента, а ограничения версии определяются в параметрах отношения.

Я собрал очень простой пример из 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_12 разными способами).

      ...

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"│
└───────┘
Другие вопросы по тегам