Запрос Gremlin работает или нет в зависимости от контекста

В запросе (кстати, написанном Стивеном Маллеттом в this question) проблема в том, что она работает в gremlify, но когда я вставляю ее в свой проект, выдает неверный результат.

Итак, я открыл gremlify, чтобы написать запрос на создание данных, а затем вставить его в консоль gremlin, чтобы я мог протестировать его там, и я заметил, что он не работает в gremlify, если он выполняется после части запроса создания данных, и он должен работать насколько я понимаю.

Запрос такой:

g.V().as('a').
  repeat(both().simplePath()).
    times(2).
  where(both().as('a')).
  path().
  map(unfold().limit(3).order().by(id).dedup().fold())
  dedup().
  group('m').
    by(limit(local,2)).
  group('m').
    by(tail(local,2)).
  group('m').
    by(union(limit(local,1),tail(local,1)).fold()).     
  cap('m').
  unfold().
  map(select(values).unfold().unfold().order().by(id).dedup().fold()).
  dedup().
  map(unfold().values('name').fold())

Вот это работает, вывод правильный: https://gremlify.com/psiygozr559

Здесь он дает неверный результат: https://gremlify.com/mqw6ut0y1z (тот же график, но созданный с помощью запроса )

Здесь он вообще не дает никаких результатов: https://gremlify.com/fzgmzdq1omq (то же, что и раньше с изменение в строке 1)

В моем проекте также выдает неверный результат, и я не выполняю ничего странного перед запросом, как в проектах gremlify выше.

Есть еще один запрос, который делает то же самое, я написал его сам, менее эффективен, но отлично работает во всех тех же ситуациях и в моем проекте, см.:

https://gremlify.com/zihygx0w8e

https://gremlify.com/xsc6q8dranj

В моем проекте я подключаюсь к серверу gremlin локально с нетронутой конфигурацией по умолчанию, используя Node.js.

Здесь что-то происходит, чего я не понимаю.


person fermmm    schedule 25.07.2020    source источник


Ответы (1)


Расширяя обход, вы расширили Path. Размещение addV('j') в начале обхода добавляет кое-что, что мой исходный алгоритм не учел:

gremlin> g.addV("j").sideEffect(V().drop()).sideEffect(
......1>     addV("user").property("name", "luana").as("luana")
......2>     .addV("user").property("name", "luisa").as("luisa")
......3>     .addV("user").property("name", "sabrina").as("sabrina")
......4>     .addV("user").property("name", "marcello").as("marcello")
......5>     .addV("user").property("name", "mario").as("mario")
......6>     .addV("user").property("name", "lidia").as("lidia")
......7>     
......7>     .addE("friend").from("luana").to("luisa")
......8>     .addE("friend").from("luana").to("sabrina")
......9>     .addE("friend").from("luana").to("marcello")
.....10>     .addE("friend").from("luana").to("mario")
.....11>     .addE("friend").from("luana").to("lidia")
.....12>     
.....12>     .addE("friend").from("sabrina").to("luisa")
.....13>     .addE("friend").from("sabrina").to("marcello")
.....14>     .addE("friend").from("sabrina").to("mario")
.....15>     
.....15>     .addE("friend").from("mario").to("luisa")
.....16>     .addE("friend").from("mario").to("marcello")
.....17>     ).V().as('a').
.....18>   repeat(both().simplePath()).
.....19>     times(2).
.....20>   where(both().as('a')).
.....21>   path().by(label)
==>[j,user,user,user]
==>[j,user,user,user]
==>[j,user,user,user]
==>[j,user,user,user]
...
==>[j,user,user,user]

Вы можете учесть это, назвав путь, который вам важен, или иным образом ограничив или отфильтровав этот начальный элемент пути:

gremlin> g.addV("j").sideEffect(V().drop()).sideEffect(
......1>     addV("user").property("name", "luana").as("luana")
......2>     .addV("user").property("name", "luisa").as("luisa")
......3>     .addV("user").property("name", "sabrina").as("sabrina")
......4>     .addV("user").property("name", "marcello").as("marcello")
......5>     .addV("user").property("name", "mario").as("mario")
......6>     .addV("user").property("name", "lidia").as("lidia")
......7>     
......7>     .addE("friend").from("luana").to("luisa")
......8>     .addE("friend").from("luana").to("sabrina")
......9>     .addE("friend").from("luana").to("marcello")
.....10>     .addE("friend").from("luana").to("mario")
.....11>     .addE("friend").from("luana").to("lidia")
.....12>     
.....12>     .addE("friend").from("sabrina").to("luisa")
.....13>     .addE("friend").from("sabrina").to("marcello")
.....14>     .addE("friend").from("sabrina").to("mario")
.....15>     
.....15>     .addE("friend").from("mario").to("luisa")
.....16>     .addE("friend").from("mario").to("marcello")
.....17>     ).V().as('a').
.....18>   repeat(both().simplePath()).
.....19>     times(2).
.....20>   where(both().as('a')).
.....21>   path().from('a').
.....22>   map(unfold().limit(3).order().by(id).dedup().fold()).
.....23>   dedup().
.....24>   group('m').
.....25>     by(limit(local,2)).
.....26>   group('m').
.....27>     by(tail(local,2)).
.....28>   group('m').
.....29>     by(union(limit(local,1),tail(local,1)).fold()).     
.....30>   cap('m').
.....31>   unfold().
.....32>   map(select(values).unfold().unfold().order().by(id).dedup().fold()).
.....33>   dedup().
.....34>   map(unfold().values('name').fold())
==>[luana,luisa,sabrina,mario]
==>[luana,sabrina,marcello,mario]
==>[luana,luisa,sabrina,marcello,mario]

Обратите внимание на строку 21 выше, где мы просто добавляем path().from('a'), в котором говорится, начинайте путь с метки шага a, а затем запрос снова начинает работать.

Что касается вашего другого примера, в котором не используется sideEffect() для добавления данных образца графика, обратите внимание на результат path(), когда он следует за repeat():

gremlin> g.addV("j").sideEffect(V().drop()).
......1>   addV("user").property("name", "luana").as("luana").
......2>   addV("user").property("name", "luisa").as("luisa").
......3>   addV("user").property("name", "sabrina").as("sabrina").
......4>   addV("user").property("name", "marcello").as("marcello").
......5>   addV("user").property("name", "mario").as("mario").
......6>   addV("user").property("name", "lidia").as("lidia").
......7>     
......7>   addE("friend").from("luana").to("luisa").
......8>   addE("friend").from("luana").to("sabrina").
......9>   addE("friend").from("luana").to("marcello").
.....10>   addE("friend").from("luana").to("mario").
.....11>   addE("friend").from("luana").to("lidia").
.....12>     
.....12>   addE("friend").from("sabrina").to("luisa").
.....13>   addE("friend").from("sabrina").to("marcello").
.....14>   addE("friend").from("sabrina").to("mario").
.....15>     
.....15>   addE("friend").from("mario").to("luisa").
.....16>   addE("friend").from("mario").to("marcello").
.....17>   V().as('a').both().path()
==>[v[712],v[713],v[715],v[717],v[719],v[721],v[723],e[725][713-friend->715],e[726][713-friend->717],e[727][713-friend->719],e[728][713-friend->721],e[729][713-friend->723],e[730][717-friend->715],e[731][717-friend->719],e[732][717-friend->721],e[733][721-friend->715],e[734][721-friend->719],v[721],v[715]]
==>[v[712],v[713],v[715],v[717],v[719],v[721],v[723],e[725][713-friend->715],e[726][713-friend->717],e[727][713-friend->719],e[728][713-friend->721],e[729][713-friend->723],e[730][717-friend->715],e[731][717-friend->719],e[732][717-friend->721],e[733][721-friend->715],e[734][721-friend->719],v[721],v[719]]
...
==>[v[712],v[713],v[715],v[717],v[719],v[721],v[723],e[725][713-friend->715],e[726][713-friend->717],e[727][713-friend->719],e[728][713-friend->721],e[729][713-friend->723],e[730][717-friend->715],e[731][717-friend->719],e[732][717-friend->721],e[733][721-friend->715],e[734][721-friend->719],v[719],v[721]]

Поскольку вы добавляли вершины / ребра за пределами sideEffect(), они включаются в этот вывод. Поэтому simplePath() немедленно отфильтровывает их, как только вы пытаетесь пройти V().as('a')!

==>[v[712],v[713],v[715],v[717],v[719],v[721],v[723],e[725][713-friend->715],e[726][713-friend->717],e[727][713-friend->719],e[728][713-friend->721],e[729][713-friend->723],e[730][717-friend->715],e[731][717-friend->719],e[732][717-friend->721],e[733][721-friend->715],e[734][721-friend->719],v[721],v[715]]

Посмотрите, как v[721] появляется дважды - один раз для addV() и один раз для V(). simplePath() видит, что вы прошли эту вершину и вернулись к ней.

Мой подход к отладке этого (поскольку ответ не был сразу ясен) заключался в том, чтобы сначала profile() два обхода и сравнить количество одинаковых разделов. Я отметил, где они начали отличаться, что поставило меня в точку, в которой возникла проблема. Оттуда я начал выполнять запрос до этих шагов параллельно, пока не заметил разницу в выводе около Path. Подробнее о том, как разбирать и отлаживать запросы Gremlin, можно здесь.

person stephen mallette    schedule 25.07.2020
comment
По-прежнему есть одна из опубликованных мною проблем, которая не может быть исправлена ​​добавлением из ('a'), вот эта: gremlify .com / uoqttm3rg5p Я исправил это, удалив simplePath (), потому что заметил, что после simplePath () ничего не возвращается. Я не знаю, почему возникает такая проблема. Может, вы дадите мне немного информации об этом. - person fermmm; 26.07.2020
comment
благодаря отсутствию побочных эффектов в этих вызовах addV() и addE() вы разместили на пути больше объектов. эти вновь добавленные вершины сразу покажут цикл на первых both() и simplePath() отфильтруют их. Вы можете увидеть эффект, если добавите следующее: V().as('a').both().path() после последнего addE(). - person stephen mallette; 26.07.2020
comment
Извините, я все еще не понимаю. Насколько я понимаю, выбор всех вершин графа с помощью V () аналогичен выделению этих вершин, потому что я создал их с помощью addV (). Почему simplePath () ведет себя по-разному в зависимости от того, как были выбраны вершины? - person fermmm; 26.07.2020
comment
Я обновил свой ответ, добавив некоторые пояснения относительно того, что я просил вас попробовать с V().as('a').both().path() - надеюсь, теперь это имеет смысл. Path - это больше, чем просто то, что вы прошли через V() - это вся линия элементов, по которым прошел Гремлин, включая мутации, преобразования и т. д. - person stephen mallette; 26.07.2020
comment
Вы правильно об этом думали. simplePath() фильтрует результаты шага. результат path() в моем примере включает все вершины, созданные addV() шагами, поэтому, когда вы затем проходите все вершины с V(), вы немедленно создаете цикл и simplePath() фильтрует этот конкретный путь. Он будет делать это для ВСЕХ путей, которые вы пройдете, потому что всегда будет полный список каждой вершины в графе, созданном addV(), а затем одна из тех вершин, которые пройдены от V(). Еще раз просмотрите пример и убедитесь, что каждый Path объект видит одну вершину более одного раза. - person stephen mallette; 26.07.2020
comment
Хорошо, я понял, спасибо за терпение. Я был сбит с толку, потому что думал, что g.V().V().simplePath() не должен возвращать результатов, но он вернется, если вершин больше 1. Но я заметил, что g.V().V().V().simplePath() не возвращает результатов, поэтому теперь я могу создать мысленный образ того, что происходит. - person fermmm; 26.07.2020