Neo4j Cypher:快速查找最大的断开连接的子图

时间:2017-01-12 15:54:26

标签: neo4j

我有一个包含一百万个节点的图表。其中有许多断开连接的子图。我想知道什么是最大的断开连接的子图。

例如,在此图示例中,我们得到了三个断开连接的子图,因此对于这种情况,输出将为7.

我尝试了这个但是花了很长时间,

match p = ()-[*]-() return MAX(length(p)) as l order by l desc limit 1

1 个答案:

答案 0 :(得分:1)

您的查询将只返回两个单独节点之间的最长路径,而不是最大连接子图的大小。

不幸的是,Neo4j目前对子图操作没有任何原生支持,我认为APOC程序在这里也没有。

Cypher有一些方法可以找到子图,但我能想到的查询不是快速或高效的,而且很可能超时大图。这是一个,而且,不建议这样做,它很可能会超时,但如果它有效,那就太棒了:

MATCH (n)-[*0..]-(subgraphNode)
WITH n, COUNT(DISTINCT subgraphNode) as subSize
RETURN MAX(subSize)

如果这是一个经常运行的查询,或者经常运行,而不是只运行一次,那么我建议一种跟踪子图的方法。

虽然我可以提供一种创建子图跟踪的方法,但是在图形操作(合并子图,分成较小的子图或创建新子图)中保持更新的方法必然会比较棘手,你很可能需要某种Java扩展来执行事务后处理来维护它。

此外,这种方法最好在没有写入操作的维护窗口期间完成。

最终目标是将单个:子图节点附加到每个断开连接的子图上,这将使子图上的未来操作变得更加容易,包括查找最大的断开连接子图的情况。

实现该目标的总体方法是首先标记图中的所有节点(标签为:Unprocessed),然后,在批处理查询中:未处理的节点,找到它们所属的整个断开连接的子图,附加单个:子图节点,然后从子图中删除:未处理的标签。

首先,标记数据库中的所有节点:

MATCH (n)
SET n:Unprocessed

接下来,批处理操作。您将要使用APOC过程来允许批处理(这也将利用从我们处理它们时未处理的标签中移除的整个子图...我们不希望冗余地对子图执行操作。)

CALL apoc.periodic.commit("
// only process a batch of :Unproccessed nodes at a time
MATCH (n:Unprocessed)
WITH n LIMIT {limit}
// subgraphNode will be all nodes in the subgraph including n
MATCH (n)-[*0..]-(subgraphNode)
WITH DISTINCT n, subgraphNode
REMOVE subgraphNode:Unprocessed
// find attach point node in each subgraph with smallest id
WITH n, min(id(subgraphNode)) as attachId
WITH DISTINCT attachId
MATCH (attachNode)
WHERE id(attachNode) = attachId
CREATE (attachNode)<-[:SUBGRAPH]-(:Subgraph)
RETURN count(*)
",{limit:100})

您可以根据需要调整限制。下限实际上可能更好,因为这可以减少同一子图的节点上的冗余操作。

现在所有断开连接的子图都附加了:子图节点,您可以对每个子图进行更快速,更简单的查询。因此,要查找最大的断开连接子图,可以使用:

MATCH (sub:Subgraph)-[*]-(subgraphNode)
WITH sub, COUNT(DISTINCT subgraphNode) as subSize
RETURN MAX(subSize)

修改

与使用变量关系匹配相比,我发现了一种更快的方法来收集子图节点。使用NODE_GLOBAL唯一性的APOC路径扩展器功能应该更快。以下是修改后使用此方法的相关查询。

CALL apoc.periodic.commit("
// only process a batch of :Unproccessed nodes at a time
MATCH (n:Unprocessed)
WITH n LIMIT {limit}
// subgraphNode will be all nodes in the subgraph including n
CALL apoc.path.expandConfig(n,{bfs:true, uniqueness:"NODE_GLOBAL"}) 
  YIELD path
WITH n, LAST(NODES(path)) as subgraphNode
REMOVE subgraphNode:Unprocessed
// find attach point node in each subgraph with smallest id
WITH n, min(id(subgraphNode)) as attachId
WITH DISTINCT attachId
MATCH (attachNode)
WHERE id(attachNode) = attachId
CREATE (attachNode)<-[:SUBGRAPH]-(:Subgraph)
RETURN count(*)
",{limit:100})

每个子图的处理:

MATCH (sub:Subgraph)
CALL apoc.path.expandConfig(sub,{minLevel:1, bfs:true, uniqueness:"NODE_GLOBAL"}) 
  YIELD path
WITH sub, LAST(NODES(path)) as subgraphNode
WITH sub, COUNT(DISTINCT subgraphNode) as subSize
RETURN MAX(subSize)