【问题标题】:How can I get an exclusive subgraph from a vertex?如何从顶点获得独占子图?
【发布时间】:2019-08-11 10:44:59
【问题描述】:

我最近不得不从使用 Cypher 更改为 Gremlin,我正在尝试转换一个允许用户“删除”一个节点和所有受此影响的子图节点的查询。它实际上并没有删除节点,而只是在受影响的节点上添加了一个“DELETED”标签。

我可以使用以下方法在 Gremlin 中获取子图:

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

但这并没有考虑到子图中的任何节点,这些节点可能有经过最初“删除”节点的路由,因此不应成为孤立节点。

如果你看上面的图表; B 是被删除的节点。它的子图将包括 D、E、G 和 H。但是,由于 E 仍然有一条通过 C 回到 A 的路线,我们不想“删除”它。 D、G 和 H 将没有返回 A 的路线,因此也应删除。

我的 Cypher 查询是这样工作的(在 C# 中使用 Neo4jClient.Cypher):

// 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 在这个问题和 this one 中的帮助,我现在可以使用:

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.

【问题讨论】:

    标签: c# cypher azure-cosmosdb gremlin azure-cosmosdb-gremlinapi


    【解决方案1】:

    首先,将子图中的顶点保存为candidates

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

    然后,过滤candidates,留下那些没有得到不包括Vertex('B')的路径的Vertex('A')

    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'))
    

    【讨论】:

    • 太棒了!感谢您的回复。我对这种方法的唯一担心是“A”可能会成长为拥有数千个后代,因此首先找到所有后代然后按它们过滤可能是一个问题。有什么办法可以让我先查看“B”的子图,然后再处理它的后代?
    • 我认为这是正确的,因为这是正确的方法,而且我很确定这适用于完全支持 Gremlin 的数据库。不幸的是,我使用的 CosmosDB 不支持 next().traversal() 和 toList() 步骤。 Gremlin.NET 似乎也不支持将查询分配给变量,就像您将第一个查询分配给“候选人”一样。这可能是不可能的,但你能想出任何方法来解决这些限制吗?
    • 所以我已经达到了可以使用以下命令在第一个查询的结果上调用 where 函数的地步:g.V().has('Id', 'B').repeat(__.inE('memberOf').subgraph('subGraph').outV().store('x')).cap('x').unfold().unfold().where(has('Id', 'G'))(我不确定为什么我需要展开它两次,但这就是我发现唯一似乎有效的东西)。这行得通,只返回 G,但是,当我输入重复位时,它不会返回任何内容。
    • 例如,这不会返回任何内容: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')))。这不应该返回所有内容,因为整个 subGraph 应该有一条返回 B 的路线吗?
    • @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实例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多