Мне поручили написать запрос для внешнего приложения, визуализирующего базу данных Neptune Graph. Допустим, первая вершина - это элементы, а вторая вершина - пользователь. Пользователь может создать элемент. Существуют отношения между элементами для отображения элементов, полученных из другого элемента, например, в случае медиаклипов, вырезанных из исходного медиаклипа. Первый набор созданных элементов должен быть создан в вершине, такой как SERVER
, по которой они сгруппированы в пользовательском интерфейсе.
Следующее требование:
Filtering on a vertex shows the full graph for that vertex:
(1) Follow all ITEM - ITEM relationships
(2) Show any ITEM - USER relationships up to 1 hop (i.e. further ITEMs linked to a USER not shown already by (1) should not be displayed)
Вот визуальное представление графика.
https://drive.google.com/file/d/1YNzh4wbzcdC0JeloMgD2C0oS6MYvfI4q/view?usp=sharing
Ниже приведен пример кода для воспроизведения этого графика. Этот график может стать еще глубже. Это просто простой пример. Пожалуйста, смотрите схему:
g.addV('SERVER').property(id, 'server1')
g.addV('SERVER').property(id, 'server2')
g.addV('ITEM').property(id, 'item1')
g.addV('ITEM').property(id, 'item2')
g.addV('ITEM').property(id, 'item3')
g.addV('ITEM').property(id, 'item4')
g.addV('USER').property(id, 'user1')
g.V('item1').addE('STORED IN').to(g.V('server1'))
g.V('item2').addE('STORED IN').to(g.V('server2'))
g.V('item2').addE('RELATED TO').to(g.V('item1'))
g.V('item3').addE('DERIVED FROM').to(g.V('item2') )
g.V('item3').addE('CREATED BY').to(g.V('user1'))
Результат должен быть в форме ниже, если это возможно:
[
[
{
"V1": {},
"E": {},
"V2": {}
}
]
]
У нас есть API с конечной точкой, которая позволяет выполнять открытые запросы гремлина. Мы вызываем эту конечную точку в нашем клиентском приложении, чтобы получать данные, отображаемые визуально. Я написал запрос, который обеспечивает только один переход между элементами, но не проходит по всей базе данных графа.
g.V('${id}').as('V1').bothE().dedup().as('E')
.otherV().hasLabel(within('USER','ITEM')).as('V2').path().limit(500).select('V1', 'E', 'V2').fold()
Я был бы признателен, если бы я мог получить один запрос, который будет извлекать этот набор данных, если это возможно. Если вершина для предоставленного идентификатора вершины не связана ни с чем, я хотел бы получить ее и отобразить только в пользовательском интерфейсе.
@daniel-kuppitz, пожалуйста, помогите с этим. Спасибо большое.
Я не совсем понимаю описание проблемы. Не могли бы вы предоставить небольшой образец графика?
@daniel-kuppitz, большое спасибо. Я отредактировал свой пост и добавил простое изображение графика. Вы можете увидеть это здесь drive.google.com/file/d/1YNzh4wbzcdC0JeloMgD2C0oS6MYvfI4q/…. Я буду рад, если вы можете помочь с запросом, чтобы пройти весь граф для всех ребер элемента -> элемента, показывая вершины, ребра и вершины назначения, а также свойства вершин таким образом, чтобы я мог получить данные для визуализировать график в интерфейсе. Спасибо большое.
Диаграммы — это хорошо, но код для создания примера графика был бы еще лучше. Например. как показано здесь: stackoverflow.com/questions/56922227/gremlin-find-highest-match Это определенно увеличивает время ответа, если люди, которые хотят ответить, не должны придумывать свой собственный образец графика, который соответствует вашей проблемной области. Кроме того, вы не указываете, какой формат вывода вы бы предпочли. Если это означает, что вы можете брать вершины и ребра в любом произвольном порядке, хорошо, в противном случае вы должны быть более конкретными.
@DanielKuppitz, теперь я добавил пример запроса и примеры результатов. Большое вам спасибо за вашу помощь. Мне очень жаль, что я не придумал код раньше.
@стефен-маллетт
Если я правильно понял все правила, то следующий запрос должен выдать желаемый результат:
g.V('${id}').aggregate('v1').
repeat(__.as('V1').bothE().dedup().as('E').otherV().hasLabel('USER','ITEM').as('V2').
aggregate('x').by(select('V1', 'E', 'V2'))).
until(hasLabel('USER')).
as('V1').bothE().dedup().as('E').otherV().hasLabel('ITEM').as('V2').
aggregate('x').
by(select('V1', 'E', 'V2')).
cap('v1','x','v1').
coalesce(select('x').unfold(),
select('v1').unfold().project('V1'))
Выполненный на вашем примере графика, он дает:
gremlin> g.V('server1').aggregate('v1').
......1> repeat(__.as('V1').bothE().dedup().as('E').otherV().hasLabel('USER','ITEM').as('V2').
......2> aggregate('x').by(select('V1', 'E', 'V2'))).
......3> until(hasLabel('USER')).
......4> as('V1').bothE().dedup().as('E').otherV().hasLabel('ITEM').as('V2').
......5> aggregate('x').
......6> by(select('V1', 'E', 'V2')).
......7> cap('v1','x','v1').
......8> coalesce(select('x').unfold(),
......9> select('v1').unfold().project('V1'))
==>[V1:v[server1],E:e[0][item1-STORED IN->server1],V2:v[item1]]
==>[V1:v[item1],E:e[2][item2-RELATED TO->item1],V2:v[item2]]
==>[V1:v[item2],E:e[3][item3-DERIVED FROM->item2],V2:v[item3]]
==>[V1:v[item3],E:e[4][item3-CREATED BY->user1],V2:v[user1]]
==>[V1:v[user1],E:e[4][item3-CREATED BY->user1],V2:v[item3]]
Спасибо большое. Я попробую это, но выполняет ли until(hasLabel('USER'))
требование одного прыжка, в то время как остальная часть обхода продолжает следовать всем отношениям ITEM -> ITEM до конца графа?
Я где-то наткнулся на это условие, которое помогает пройти весь график - .until(outE().count().is(0))
Остальная часть обхода — это не repeat()
, это просто еще один прыжок.
Это выглядит здорово. Я просто хочу быстро интегрировать его с интерфейсом и посмотреть, как это выглядит. Но он показывает полный граф для этой вершины, верно? Как насчет обработки дубликатов?
Я только что видел шаг dedup(), который вы добавили к краям. Большое спасибо. Это выглядит великолепно. Я отмечу это как ответ через некоторое время. Позвольте мне сделать несколько тестов.
Я заметил, что у последней записи есть дубликат, но если я заменю фильтр until(hasLabel('USER'))
на until(bothE().count().is(0))
, все будет уникальным. Знаете, почему это так? Однако сейчас я отмечу это как ответ. Мне может понадобиться ваша помощь для еще одного запроса, если у меня возникнут какие-либо проблемы, но это будет еще один вопрос SO. Большое спасибо за Вашу помощь.
Последний прыжок не контролируется repeat()
и, следовательно, не имеет dedup())
. Вы можете отфильтровать последние ребра по тому, что уже собрано в x
. То, что until(bothE().count().is(0)
заставляет это работать, больше похоже на совпадение.
Спасибо, Даниэль. Последний вопрос. Если я заменю until(hasLabel('USER'))
на until(out().count().is(0))
для второго требования к одному прыжку, приведет ли это к обходу всего графа в этом случае?
Что ж, только вы можете знать, допустимо ли это условие разрыва. Это, вероятно, будет работать в вашем образце графика, не уверен насчет полного графика. Кроме того, это условие будет очень хрупким, то есть ваш запрос может легко сломаться при изменении схемы.
Спасибо Даниэль. Вы случайно не видели вопрос, который я разместил на stackoverflow.com/questions/57245636/…? Если невозможно ограничить количество узлов, я не возражаю против ограничения количества записей. Спасибо.
Теперь я придумал решение, и мне нужен ваш обзор этого решения, прежде чем я отмечу его как ответ. Спасибо.
@stephen-mallette, я знаю, что ты эксперт в этом. Не могли бы вы помочь мне с этим, пожалуйста? Спасибо большое.