Gremlin запрос (SQL самостоятельное соединение) - PullRequest
1 голос
/ 27 марта 2020

Я довольно новичок в Gremlin, я немного потренировался с этим руководством , но когда дело доходит до написания более сложных запросов, я, очевидно, еще не освоил его. Чтобы поместить вас в контекст, я пытаюсь ответить на вопрос, который в SQL может быть легко взломан с помощью самостоятельного соединения.

Представьте себе следующий упрощенный график:

image

Как видите, на графике есть два типа объектов: Маршруты и Ноги. Маршрут состоит из 1+ Ног, следующих за определенным порядком (указанным на ребре), и Нога может быть в нескольких Маршрутах.

Я хочу ответить на вопрос: , какие маршруты следуют от одного страна в другую, а затем обратно в предыдущую страну?

В случае приведенного выше графика маршрут 1 переходит от ES к FR в первом этапе и от FR к ES в третьем этапе. Таким образом, результат запроса будет выглядеть следующим образом:

=> Route id: 1
=> Leg1 order: 1
=> Leg1 id: 1
=> Leg2 order: 3
=> Leg2 id: 3

Если бы у меня была следующая реляционная таблица:

route_id   leg_id   order  source_country   destination_country
1          1        1      ES               FR
1          2        2      FR               FR
1          3        3      FR               ES

Я мог бы получить желаемый результат с помощью следующего запроса:

SELECT
  a.route_id
  ,a.leg_id
  ,a.order
  ,b.leg_id
  ,b.order
FROM Routes a
JOIN Routes b
  ON a.route_id = b.route_id
  AND a.source_country = b.destination_country
  AND a.destination_country = b.source_country
WHERE a.source_country <> a.destination_country;

Когда дело доходит до написания этого в Gremlin, я действительно не совсем уверен, с чего начать. Моя неопытность заставляет меня хотеть выполнять самостоятельное объединение, но даже тогда я не очень далеко ушел:

g.V().hasLabel('Route').as('a').V().hasLabel('Route').as('b').where('a', eq('b')).and(join 'a' edges&legs with 'b' edges&legs)...

И это все, потому что я не знаю, как ссылаться a снова как объект, который можно пройти, чтобы найти края и ноги, связанные с маршрутами.

Буду признателен за любую помощь / руководство, вполне возможно, что эта проблема может быть решена Проще говоря:)

Спасибо, Бентор

Ответы [ 2 ]

4 голосов
/ 27 марта 2020

С графиками вы должны попытаться придумать термины «навигация по связанным вещам», а не «объединение разнородных вещей», потому что с графом вещи уже явно объединены. Это также помогает мыслить с точки зрения ленивых вычислений (т. Е. Объектов, идущих от одного шага Гремлина к следующему).

Прежде всего, картина хороша, но всегда полезнее предоставить пример данные в виде сценария Гремлин, как это:

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 еще проще, чем я предлагал ранее, потому что теперь нам просто нужно искать уникальные маршруты, что означает, что если мы удовлетворяем одну из этих двух ситуаций, то мы нашли маршрут, который нас интересует:

  1. Одна нога начинается и заканчивается в одной и той же стране
  2. Есть несколько этапов, и если количество раз, когда эта страна появляется на маршруте, превышает 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).

3 голосов
/ 27 марта 2020

На тот случай, если это будет полезно, приведу аналогичный пример, с которым я играл до того, как увидел, что Стивен уже ответил. При этом используется набор данных о воздушных маршрутах из учебника. Первый пример начинается именно с LHR. Второй смотрит на все аэропорты. Я предположил, что константа 2 сегмента. Вы можете изменить это, изменив запрос, и, как упомянул Стивен, есть много способов подойти к этому. номер заказа намного приятнее. Набор данных о воздушных маршрутах не имеет понятия сегмента, но подумал, что это может представлять некоторый интерес, когда вы начнете больше изучать Gremlin.

...