С графиками вы должны попытаться придумать термины «навигация по связанным вещам», а не «объединение разнородных вещей», потому что с графом вещи уже явно объединены. Это также помогает мыслить с точки зрения ленивых вычислений (т. Е. Объектов, идущих от одного шага Гремлина к следующему).
Прежде всего, картина хороша, но всегда полезнее предоставить пример данные в виде сценария Гремлин, как это:
g = TinkerGraph.open().traversal()
g.addV('route').property('rid',1).as('r1').
addV('route').property('rid',2).as('r2').
addV('route').property('rid',3).as('r3').
addV('leg').property('lid',1).property('source','ES').property('dest','FR').as('l1').
addV('leg').property('lid',2).property('source','FR').property('dest','FR').as('l2').
addV('leg').property('lid',3).property('source','FR').property('dest','ES').as('l3').
addV('leg').property('lid',4).property('source','ES').property('dest','FR').as('l4').
addV('leg').property('lid',5).property('source','FR').property('dest','FR').as('l5').
addV('leg').property('lid',6).property('source','FR').property('dest','US').as('l6').
addE('has_leg').from('r1').to('l1').property('order',1).
addE('has_leg').from('r1').to('l2').property('order',2).
addE('has_leg').from('r1').to('l3').property('order',3).
addE('has_leg').from('r3').to('l4').property('order',1).
addE('has_leg').from('r3').to('l5').property('order',2).
addE('has_leg').from('r3').to('l6').property('order',3).
addE('has_leg').from('r2').to('l2').property('order',1).iterate()
Ваш вопрос был:
, какие маршруты следуют из одной страны в другую, а затем обратно в предыдущую страну?
Обратите внимание, что я добавил некоторые дополнительные данные, которые не отвечали требованиям этого вопроса, чтобы убедиться, что мой обход работал правильно. Полагаю, я предполагал, что вы были открыты для получения маршрутов, которые просто оставались в стране, как нога, которая просто перешла от «FR» к FR », как она начиналась в« FR »и заканчивалась в« предыдущей стране ». доработайте это, чтобы сделать это, если вы действительно нуждаетесь во мне, но сейчас я буду придерживаться этого предположения, поскольку вы только учитесь.
После рассмотрения данных и чтения этого вопроса, который я сразу подумал, давайте найдем маршруты, которые вы проделали достаточно хорошо, а затем давайте просто посмотрим, что нужно, чтобы получить начальную часть поездки и конечную часть поездки для этого маршрута:
gremlin> g.V().hasLabel('route').
......1> map(outE('has_leg').
......2> order().by('order').
......3> union(limit(1).inV().values('source'), tail().inV().values('dest')).
......4> fold())
==>[ES,ES]
==>[FR,FR]
==>[ES,US]
Итак, я нахожу «маршрут» вершина с hasLabel('route')
, а затем я конвертирую каждую в List
начальной и конечной страны (т. е. пары, в которой первый элемент является страной «источника», а второй элемент - страной «дест»). Я пересекаю исходящие ребра "has_leg", упорядочиваю их. После того, как приказал, я беру первое ребро в потоке (то есть с limit(1)
) и перехожу к входящей вершине "ноги" и беру ее " исходное значение и сделайте то же самое для последней входящей вершины ребра (то есть с tail()
), но на этот раз захватите его значение dest. Затем мы используем fold()
to pu sh, что два потока предметов из union()
в List
. Опять же, поскольку все это происходит внутри map()
, мы эффективно делаем это для каждой вершины «маршрута», поэтому мы получаем три пары.
С этим выводом нам сейчас нужно сравнить начало / конец значения в парах, чтобы определить, какой маршрут начинается и заканчивается в одной и той же стране.
gremlin> g.V().hasLabel('route').
......1> filter(outE('has_leg').
......2> order().by('order').
......3> fold().
......4> project('start','end').
......5> by(unfold().limit(1).inV().values('source')).
......6> by(unfold().tail().inV().values('dest')).
......7> where('start', eq('end'))).
......8> elementMap()
==>[id:0,label:route,rid:1]
==>[id:2,label:route,rid:2]
В строке 1 обратите внимание, что мы изменили map()
на filter()
. Изначально я использовал map()
, чтобы увидеть результаты того, что я проходил, прежде чем беспокоиться о том, как использовать эти результаты, чтобы избавиться от данных, которые мне не нужны. Это обычная практика с Gremlin, когда вы создаете все больше и больше сложности в своих обходах. Итак, теперь мы готовы применить filter()
к каждой вершине «маршрута». Я полагаю, что есть несколько способов сделать это, но я решил собрать все упорядоченные ребра в List
в строке 3. Затем я project()
перешагнул строку 4 и преобразовал список ребер для обоих «start». клавиши "и" конец "с использованием связанных модуляторов by()
. В обоих случаях я должен unfold()
список ребер к потоку, а затем применить тот же самый тип обхода limit(1)
и tail()
, который был объяснен ранее. В результате получается Map
с клавишами «старт» и «конец», которые можно сравнить с помощью шага where()
. Как видно из результата, третий маршрут, который начинался в «ES» и заканчивался в «US», был отфильтрован.
Я расширю свой ответ на основе вашего комментария - так как все мои предыдущие кажется, что данные совпадают с вашим более общим случаем, когда вы хотите найти какой-либо маршрут, который в какой-либо степени возвращает страну:
g = TinkerGraph.open().traversal()
g.addV('route').property('rid',1).as('r1').
addV('route').property('rid',2).as('r2').
addV('route').property('rid',3).as('r3').
addV('route').property('rid',4).as('r4').
addV('leg').property('lid',1).property('source','ES').property('dest','FR').as('l1').
addV('leg').property('lid',2).property('source','FR').property('dest','FR').as('l2').
addV('leg').property('lid',3).property('source','FR').property('dest','ES').as('l3').
addV('leg').property('lid',4).property('source','ES').property('dest','FR').as('l4').
addV('leg').property('lid',5).property('source','FR').property('dest','FR').as('l5').
addV('leg').property('lid',6).property('source','FR').property('dest','US').as('l6').
addV('leg').property('lid',7).property('source','ES').property('dest','FR').as('l7').
addV('leg').property('lid',8).property('source','FR').property('dest','CA').as('l8').
addV('leg').property('lid',9).property('source','CA').property('dest','US').as('l9').
addE('has_leg').from('r1').to('l1').property('order',1).
addE('has_leg').from('r1').to('l2').property('order',2).
addE('has_leg').from('r1').to('l3').property('order',3).
addE('has_leg').from('r3').to('l4').property('order',1).
addE('has_leg').from('r3').to('l5').property('order',2).
addE('has_leg').from('r3').to('l6').property('order',3).
addE('has_leg').from('r4').to('l7').property('order',1).
addE('has_leg').from('r4').to('l8').property('order',2).
addE('has_leg').from('r4').to('l9').property('order',3).
addE('has_leg').from('r2').to('l2').property('order',1).iterate()
Если у меня есть это право, следует добавить новый добавленный маршрут "rid = 4" поскольку его маршрут никогда не посещает ту же страну. Я думаю, что этот кусочек Gremlin еще проще, чем я предлагал ранее, потому что теперь нам просто нужно искать уникальные маршруты, что означает, что если мы удовлетворяем одну из этих двух ситуаций, то мы нашли маршрут, который нас интересует:
- Одна нога начинается и заканчивается в одной и той же стране
- Есть несколько этапов, и если количество раз, когда эта страна появляется на маршруте, превышает 2 (потому что мы учитываем "source" и "dest")
Вот Гремлин:
gremlin> g.V().hasLabel('route').
......1> filter(out('has_leg').
......2> union(values('source'),
......3> values('dest')).
......4> groupCount().
......5> or(select(values).unfold().is(gt(2)),
......6> count(local).is(1))).
......7> elementMap()
==>[id:0,label:route,rid:1]
==>[id:2,label:route,rid:2]
==>[id:4,label:route,rid:3]
Если вы поняли мои более ранние объяснения кода, то вы, вероятно, выполните все до строки 5, где мы берем Map
, полученное с помощью groupCount()
для названий стран, и применяем два условия фильтрации: I только что описал. В строке 5 мы применяем второе условие, которое извлекает значения из Map
(т. Е. Подсчитывает количество раз, которое появляется в каждой стране) и обнаруживает, если они больше 2. В строке 6 мы подсчитываем записи в Map
, который соответствует первому условию. Обратите внимание, что мы используем local
там, потому что мы не учитываем Map
-объекты в потоке, но записи внутри Map
(т.е. локально для Map
).