Как получить эксклюзивный подграф из вершины?

Недавно мне пришлось перейти с Cypher на Gremlin, и я пытаюсь преобразовать запрос, который позволяет пользователю «удалить» узел и все узлы подграфа, на которые это повлияет. Фактически он не удалял узлы, а просто добавлял метку «УДАЛЕНО» к затронутым узлам.

Я могу получить подграф в Gremlin, используя:

g.V(nodeId).repeat(__.inE('memberOf').subgraph('subGraph').outV()).cap('subGraph')

но при этом не принимаются во внимание какие-либо узлы в подграфе, которые могут иметь обратный маршрут мимо первоначально «удаленного» узла и, следовательно, не должны быть осиротевшими.

введите описание изображения здесь

Если взять график выше; B - удаляемый узел. Его подграф будет включать D, E, G и H. Однако, поскольку у E все еще есть обратный путь к A через C, мы не хотим его «удалять». D, G и H останутся без обратного пути к A и, следовательно, также должны быть удалены.

Мой запрос Cypher работал следующим образом (с использованием Neo4jClient.Cypher в C #):

// Find the node to be deleted i.e. B
.Match("(b {Id: 'B'})")  
// Set a DELETED label to B   
.Set("b:DELETED")     
.With("b")
// Find the central node i.e A
.Match("(a {Id: 'A'})") 
// Find the subgraph of B ignoring previously deleted nodes
.Call("apoc.path.subgraphAll(b, { relationshipFilter: '<memberOf', labelFilter: '-DELETED'})")     
.Yield("nodes AS subgraph1")
// Get each node in subgraph1 as sg1n
.Unwind("subgraph1", "sg1n") 
// Check if each sg1n node has a route back to A ignoring DELETED routes    
.Call("apoc.path.expandConfig(sg1n, { optional: true, relationshipFilter: 'memberOf>', labelFilter: '-DELETED', blacklistNodes:[b],terminatorNodes:[a]})")     
.Yield("path")
// If there is a path then store the nodes as n
.Unwind("CASE WHEN path IS NULL THEN [null] ELSE nodes(path) END", "n")     
// Remove the nodes in n from the original subgraph (This should leave the nodes without a route back)
.With("apoc.coll.subtract(subgraph1, collect(n)) AS subgraph2") 
// Set the DELETED label on the remaining nodes     
.ForEach("(n IN(subgraph2) | SET n:DELETED)")  

Есть ли способ получить аналогичную функциональность в Gremlin?

ОБНОВИТЬ

Благодаря помощи sel-fish в этом вопросе и в этот, теперь у меня это работает, используя:

g.V(itemId)                                            // Find the item to delete.
  .union(                                              // Start a union to return
    g.V(itemId),                                       // both the item 
    g.V(itemId)                                        // and its descendants.
      .repeat(__.inE('memberOf').outV().store('x'))    // Find all of its descendants.
      .cap('x').unfold()                               // Unfold them.
      .where(repeat(out('memberOf')                    // Check each descendant
        .where(hasId(neq(itemId))).simplePath())       // to see if it has a path back that doesn't go through the original vertex
        .until(hasId(centralId)))                      // that ends at the central vertex .
      .aggregate('exception')                          // Aggregate these together.
      .cap('x').unfold()                               // Get all the descendants again.
      .where(without('exception')))                    // Remove the exceptions.
  .property('deleted', true)                           // Set the deleted property.
  .valueMap(true)                                      // Return the results.

person ghertyish    schedule 20.03.2019    source источник


Ответы (1)


Сначала сохраните вершины в подграфе как candidates:

candidates = g.V().has('Id', 'B').repeat(__.inE('memberOf').subgraph('subGraph').outV()).cap('subGraph').next().traversal().V().toList()

Затем отфильтруйте candidates, останутся те, которые не получают путь к вершине ('A'), не включая вершину ('B'):

g.V(candidates).where(repeat(out('memberOf').where(has('Id', neq('B'))).simplePath()).until(has('Id','A'))).has('Id', neq('B')).aggregate('expection').V(candidates).where(without('expection'))
person sel-fish    schedule 21.03.2019
comment
Потрясающие! Спасибо за ответ. Меня беспокоит только этот подход, так это то, что «А» может вырасти и иметь тысячи потомков, и поэтому сначала найти их всех, а затем фильтровать по ним может быть проблемой. Могу ли я сначала посмотреть на подграф B и вместо этого отработать его потомков? - person ghertyish; 21.03.2019
comment
Я принял это как правильное, потому что это правильный подход, и я почти уверен, что он будет работать с базой данных, полностью поддерживающей Gremlin. К сожалению, я использую CosmosDB, который не поддерживает шаги next (). Traversal () и toList (). Gremlin.NET также, похоже, не поддерживает назначение запросов переменным, как если бы вы назначили первый запрос «кандидатам». Это может быть просто невозможно, но можете ли вы придумать способ сделать это с такими ограничениями? - person ghertyish; 22.03.2019
comment
Итак, я достиг точки, когда я могу вызвать функцию where для результатов первого запроса, используя: g.V().has('Id', 'B').repeat(__.inE('memberOf').subgraph('subGraph').outV().store('x')).cap('x').unfold().unfold().where(has('Id', 'G')) (я не уверен, почему мне нужно развернуть его дважды, но это единственное, что я обнаружил, что кажется Работа). Это работает и просто возвращает G, однако, когда я вставляю бит повторения, он ничего не возвращает. - person ghertyish; 22.03.2019
comment
Например, это ничего не возвращает: g.V().has('Id', 'B').repeat(__.inE('memberOf').subgraph('subGraph').outV().store('x')).cap('x').unfold().unfold().where(repeat(out('memberOf').simplePath()).until(has('Id', 'B'))). Разве это не должно возвращать все, потому что у всего подграфа должен быть обратный путь к B? - person ghertyish; 22.03.2019
comment
@ghertyish, как насчет g.V().has('Id', 'B').repeat(__.inE('memberOf').subgraph('subGraph').outV().store('x')).cap('x').unfold().where(repeat(out('memberOf').simplePath()).until(has('Id','B'))), в вашем последнем комментарии мне не нужно разворачивать его дважды, а только один раз, чтобы получить G. Я тестирую этот dsl на TinkerGraph, так как у меня нет экземпляра cosmosdb - person sel-fish; 26.03.2019