我最近不得不从使用 Cypher 更改为 Gremlin,并且我正在尝试转换一个允许用户“删除”一个节点和所有受此影响的子图节点的查询。它实际上并没有删除节点,而只是在受影响的节点上添加了一个“已删除”标签。
我可以使用以下方法在 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 在这个问题和这个问题中的帮助,我现在可以使用:
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.