如何深度优先地序列化节点?

时间:2019-11-25 07:22:02

标签: neo4j neo4j-apoc

我正在学习Neo4J。

我了解如何对节点进行深度优先搜索,但是在我的情况下,我想序列化所有节点,但要完全遍历一个分支,然后移至下一个分支,依此类推。

给出以下节点:

N1 { order: 1 }
  N2 { order: 1 }
    N4 { order: 1 }
      N5 { order: 1 }
      N6 { order: 2 }
  N3 { order: 2 }
    N7 { order: 1 }

节点之间的关系是名为part_of的有向节点。 E.i. N2-[part_of]->N1

我希望列表按以下顺序序列化:N1 N2 N4 N5 N6 N3 N7

最有效的方法是什么?

先谢谢了。

// E

1 个答案:

答案 0 :(得分:0)

您现在可能已经在Neo4j Graph Algorithms插件中发现了algo.dfs.stream函数,该函数可以使您半途而废:

MATCH (startNode: Node { name: 'N1' } )
CALL algo.dfs.stream('Node', 'PART_OF', 'BOTH', id(startNode))
YIELD nodeIds 
UNWIND nodeIds as nodeId
WITH algo.asNode(nodeId) as n
RETURN n

麻烦的是algo.dfs.stream不允许您控制节点在兄弟姐妹之间的遍历-最好的选择可能是在应用程序代码中做到这一点,编写DFS非常简单。

但是,在过去的几个小时中,我花了很多时间研究一种纯Cypher的方法,该方法确实通过您的order属性以稳定的顺序执行了深度优先搜索,令人发笑。出于以下几个原因,我衷心恳请您不要使用此代码:

  • 我很确定树中节点的数量至少达到O(n ^ 2)复杂度
  • 很多工作都是作为一个批次预先计算路径,而您自己的遍历代码可能是基于流的-这不适用于大型图形
  • “只是因为可以,并不意味着您应该这样做”

但是不将其张贴在某个地方似乎很浪费,所以我们在这里。

假设一些测试数据与您的样品匹配:

MERGE (n1: Node { order: 1, name: 'N1' })
MERGE (n2: Node { order: 1, name: 'N2' })
MERGE (n3: Node { order: 2, name: 'N3' })
MERGE (n4: Node { order: 1, name: 'N4' })
MERGE (n5: Node { order: 1, name: 'N5' })
MERGE (n6: Node { order: 2, name: 'N6' })
MERGE (n7: Node { order: 1, name: 'N7' })
MERGE (n2)-[:PART_OF]->(n1)
MERGE (n4)-[:PART_OF]->(n2)
MERGE (n5)-[:PART_OF]->(n4)
MERGE (n6)-[:PART_OF]->(n4)
MERGE (n3)-[:PART_OF]->(n1)
MERGE (n7)-[:PART_OF]->(n3)

以下内容将产生DFS遍历,其中通过对节点的order属性进行排序来选择同级节点。

MATCH (root: Node { name: 'N1' }), pathFromRoot=shortestPath((root)<-[:PART_OF*]-(leaf: Node)) WHERE NOT ()-[:PART_OF]->(leaf)
WITH nodes(pathFromRoot) AS pathFromRootNodes
WITH pathFromRootNodes, reduce(pathString = "", pathElement IN pathFromRootNodes | pathString + '/' + right("00000" + toString(pathElement.order), 6)) AS orderPathString ORDER BY orderPathString
WITH reduce(concatPaths = [], p IN collect(pathFromRootNodes) | concatPaths + p) AS allPaths
WITH reduce(distinctNodes = [], n IN allPaths | CASE WHEN n IN distinctNodes THEN distinctNodes ELSE distinctNodes + n end) AS traversalOrder
RETURN [x in traversalOrder | x.name]

我不会逐行解释,但要旨是:

  • 我们构建了从根到每个叶节点的路径集
  • 对于每条路径,我们构造一个合成键,该键将每个节点上的order属性组合在一起,使得通过此键对路径进行排序可以得出到达每个叶节点的顺序
    • 这可能是最重要的一点-我们填充'order'属性,以便我们可以在路径上使用词法ASCII排序来产生遍历顺序
  • 我们将排序路径的列表弄平,并对其中的节点进行重复数据消除以获取遍历顺序