Как получить подграф, состоящий из всех вершин, удовлетворяющих определенному условию

Документ и Редакция - это два объекта, которые находятся на уровне, специфичном для нашей логики предметной области.

Документ представляет собой абстракцию вокруг любого материального листа бумаги, о котором вы можете подумать. То есть каждый контракт, счет или чертеж можно назвать документом.

С другой стороны, материальным представлением Документа является Редакция: список документов, который инженер-строитель получает на месте, представляет Редакцию документа, созданного дизайнером. Если что-то в чертеже необходимо изменить из-за ошибки или изменения требований, то на сайте появится новая версия - Версия №2 того же Документа.

Редакция может содержать ссылки на другие Документы; таким образом, мы могли бы описать отношения между автомобилем, его дверьми, двигателем, колесами и так далее, а также возможность каждого элемента развиваться независимо, оставаясь привязанным к другим элементам.

Отображается типичный DAG:

Элементы автомобиля - Документы и исправления

Мне удалось вставить все вершины и ребра в CosmosDB с помощью C # Graph API. Мне удалось пройтись по графику и выполнить простые запросы, чтобы узнать, сколько ревизий у автомобиля и есть ли у двигателя турбокомпрессор при его первоначальном создании. Однако мне трудно составить сложный запрос, который возвращает только самые последние версии каждой детали или автомобиля, или запрос, который возвращает состояние автомобиля до 10 августа 2016 года.

Состояние автомобиля до 03.01.2017:  Готовая машина

Состояние автомобиля до 10.08.2016:  Двигатель автомобиля еще не имеет турбонагнетателя

Когда обход посещает потомков вершины (ее "out ()"), я не мог найти способ получить последнее созданное и продолжить обход, не копаясь в других. Буду признателен, если вы предложите мне выражение, которое возвращает только цветные вершины с картинок.


person A.Kostadinov    schedule 03.07.2017    source источник


Ответы (1)


Хотя изображения полезны, задавая вопросы о Gremlin, всегда полезно предоставить скрипт Gremlin, который может сгенерировать образец вашего графика. Например, для ваших вопросов:

graph = TinkerGraph.open()
g = graph.traversal()
g.addV('car').property('name','car').as('car').
  addV('rev').property('name','car revision 1').property('date', 1470787200L).as('carV1').
  addV('rev').property('name','car revision 2').property('date', 1472688000L).as('carV2').
  addV('frontLeftDoor').property('name','front left door').as('frontLeftDoor').
  addV('frontRightDoor').property('name','front right door').as('frontRightDoor').
  addV('engine').property('name','engine').as('engine').
  addV('turbocharger').property('name','turbocharger').as('turbocharger').
  addV('rev').property('name','front left door revision 1').property('date',1470787200L).as('frontLeftDoorV1').
  addV('rev').property('name','front left door revision 2').property('date',1472688000L).as('frontLeftDoorV2').
  addV('rev').property('name','front right door revision 1').property('date',1470787200L).as('frontRightDoorV1').
  addV('rev').property('name','engine revision 1').property('date',1470787200L).as('engineV1').
  addV('rev').property('name','engine revision 2').property('date',1472688000L).as('engineV2'). 
  addV('rev').property('name','engine revision 3').property('date',1483401600L).as('engineV3').
  addV('rev').property('name','turbocharger revision 1').property('date',1470787200L).as('turbochargerV1'). 
  addV('rev').property('name','turbocharger revision 2').property('date',1472688000L).as('turbochargerV2'). 
  addE('relates').from('car').to('carV1').
  addE('relates').from('car').to('carV2').
  addE('relates').from('carV1').to('frontLeftDoor').
  addE('relates').from('carV1').to('frontRightDoor').
  addE('relates').from('carV1').to('engine').
  addE('relates').from('carV2').to('frontLeftDoor').
  addE('relates').from('carV2').to('frontRightDoor').
  addE('relates').from('carV2').to('engine').
  addE('relates').from('frontLeftDoor').to('frontLeftDoorV1').
  addE('relates').from('frontLeftDoor').to('frontLeftDoorV2').
  addE('relates').from('frontRightDoor').to('frontRightDoorV1').
  addE('relates').from('engine').to('engineV1').
  addE('relates').from('engine').to('engineV2').
  addE('relates').from('engine').to('engineV3').
  addE('relates').from('engineV2').to('turbocharger').
  addE('relates').from('engineV3').to('turbocharger').
  addE('relates').from('turbocharger').to('turbochargerV1').
  addE('relates').from('turbocharger').to('turbochargerV2').iterate()

Часто человеку, отвечающему на вопрос, требуется больше времени, чтобы создать образец диаграммы для вопроса, чем разработать Гремлин, дающий ответ.

В любом случае, вот один из способов сделать это, используя «10.08.2016» в качестве «даты начала»:

gremlin> g.V().has('name','car').
......1>   repeat(local(out().has('date',lte(1470787200L)).
......2>                order().
......3>                  by('date',decr).limit(1)).
......4>          out()).
......5>     emit().
......6>   local(out().has('date',lte(1470787200L)).
......7>         order().
......8>           by('date',decr).limit(1)).
......9>   tree().by('name')
==>[car:[car revision 1:[front right door:[front right door revision 1:[]],engine:[engine revision 1:[]],front left door:[front left door revision 1:[]]]]]

Вот тот же ход с другой датой - «01.01.2017»:

gremlin> g.V().has('name','car').
......1>   repeat(local(out().has('date',lte(1483228800L)).
......2>                order().
......3>                  by('date',decr).limit(1)).
......4>          out()).
......5>     emit().
......6>   local(out().has('date',lte(1483228800L)).
......7>         order().
......8>           by('date',decr).limit(1)).
......9>   tree().by('name')
==>[car:[car revision 2:[front right door:[front right door revision 1:[]],engine:[engine revision 2:[turbocharger:[turbocharger revision 2:[]]]],front left door:[front left door revision 2:[]]]]]

В этом случае обратите внимание на то, что "ревизия движка 3" исключена, поскольку это единственная вершина после "01.01.2017" - остальная часть дерева присутствует.

Несколько примечаний:

  1. Я преобразовал ваши даты в длинные для облегчения сравнения. Я не уверен, что CosmosDB имеет удобную обработку дат относительно предиката lte для has(), но если это так, вы, вероятно, предпочтете пойти по этому пути.
  2. Шаг repeat() допускает произвольный обход глубины в дереве, но обратите внимание на дублированную логику, которую он содержит сразу после emit() - это захватывает последние "листья дерева", так как внутри repeat() цикл заканчивается, потому что больше нет outE() для траверс.
  3. Логика в repeat() выглядит немного сложной, но в основном это просто говорит о том, что текущий «документ» проходит по всем «ревизиям», отсортирует дату в порядке убывания и захватит первую. Как только он получит самую последнюю версию, которая контролируется датой, которая вам нужна, перейдите к любым другим документам, к которым он подключен.
  4. В этом случае я использовал шаг tree(), поскольку CosmosDB, похоже, его поддерживает. Не похоже, что они еще поддерживают subgraph(). Технически этот шаг даже не поддерживается в Apache TinkerPop C # Gremlin Language Variant - там есть некоторые проблемы, которые, к сожалению, оставляют только возможности Java. К счастью, форма ваших данных древовидная, поэтому достаточно tree() шагов.

В Groovy вы можете предоставить повторяющуюся логику в виде замыкания, чтобы сделать вещи более пригодными для повторного использования:

gremlin> traverseAndFilter = { out().has('date',lte(1470787200L)).
......1>                       order().
......2>                         by('date',decr).limit(1) }
==>groovysh_evaluate$_run_closure1@1d12e953
gremlin> g.V().has('name','car').
......1>   repeat(local(traverseAndFilter()).out()).
......2>     emit().
......3>   local(local(traverseAndFilter())).
......4>   tree().by('name')
==>[car:[car revision 1:[front right door:[front right door revision 1:[]],engine:[engine revision 1:[]],front left door:[front left door revision 1:[]]]]]

или сохраните сам обход traverseAndFilter и clone() его:

gremlin> traverseAndFilter = out().has('date',lte(1470787200L)).
......1>                       order().
......2>                         by('date',decr).limit(1);[] 
gremlin> g.V().has('name','car').
......1>   repeat(local(traverseAndFilter.clone()).out()).
......2>     emit().
......3>   local(local(traverseAndFilter.clone())).
......4>   tree().by('name')
==>[car:[car revision 1:[front right door:[front right door revision 1:[]],engine:[engine revision 1:[]],front left door:[front left door revision 1:[]]]]]
person stephen mallette    schedule 03.07.2017
comment
Прошу прощения за то, что не опубликовал сценарий вставки! Я обязательно сделаю это в следующий раз! - person A.Kostadinov; 03.07.2017
comment
Запрос должен найти самую последнюю Редакцию до указанной даты среди всех редакций Документа. Должно получиться что-то вроде: Получить все версии текущего документа - ›OrderByDate-› GetClosestAndNotGreatedThan Date - ›Продолжить с документом, на который указывает найденная редакция. Если есть ревизии старше 10 августа 2016 года, они тоже будут приняты lte (1470787200L) - person A.Kostadinov; 03.07.2017
comment
В приведенном выше примере даты первых ревизий совпадают случайно. Абсолютно возможно, что каждая ревизия будет иметь разную дату, а указанная дата (дата разрешения) является случайной. Разрешение дерева может произойти в любое время. - person A.Kostadinov; 03.07.2017
comment
Думаю, я скопировал / вставил в свой ответ старую версию своего кода. У меня даже были плохие данные в моем образце графика. В любом случае, я исправил пару вещей, которые, как мне кажется, решают вашу проблему. Я даже добавил второй обход, используя 01.01.2017, чтобы показать, что он не привязан к какой-либо конкретной дате. - person stephen mallette; 03.07.2017
comment
Спасибо за то, что помогли мне! Я очень ценю ваше время! Однако я чувствую, что все еще не могу уточнить желаемое поведение запроса. Ожидаемый результат на 01.01.2017 должен быть следующим: [Автомобиль], [Автомобиль - Версия 2], [Передняя левая дверь - Версия 2], [Передняя правая дверь - Версия 1], [Двигатель - Версия 2], [Турбокомпрессор - Версия 2] ». Исправления, более старые, чем последние сделанные, должны быть удалены. Второй запрос возвращает все ревизии, созданные до 01.01.2017, но мне нужна только последняя из них. - person A.Kostadinov; 04.07.2017
comment
извините - я продолжал не понимать, что вам нужно. я думаю, что у меня это есть прямо сейчас - person stephen mallette; 04.07.2017
comment
Да вот оно что! Большое тебе спасибо! Хорошее объяснение шагов гремлина! - person A.Kostadinov; 04.07.2017