在neo4j中比较或区分两个图

时间:2014-06-30 08:55:22

标签: neo4j graph-databases

我在neo4j数据库中有两个断开连接的图形。它们是非常相似的网络,但有一个版本是几个月后的同一个图形。

有没有办法可以轻松比较这两个图表,看看对网络进行的任何添加,删除或编辑?

3 个答案:

答案 0 :(得分:1)

我猜使用基于文本的工具最容易实现差异化。

我能想到的一种方法是使用https://github.com/jexp/neo4j-shell-tools将两个子图导出到GraphML,然后从unix中应用常规diff

另一个人将使用dump in neo4j-shell并将结果与​​上述差异。

答案 1 :(得分:1)

这在很大程度上取决于你想要的差异以及图形本身的约束。

  • 如果节点和关系具有标识符属性(不是内部Neo4j ID),那么您可以简单地下拉每个图形的节点和关系,并跟踪添加,删除或更改的内容(区分属性)。
  • 如果关系不是唯一标识(通过属性),但是节点是,则自然键是起始节点,结束节点和类型,因为不能存在重复关系。
  • 如果既没有托管标识符,但属性是不可变的,那么可以跨节点比较那些(可能代价高昂),然后是方法中的关系。

答案 2 :(得分:1)

如果您希望使用纯Cypher解决方案比较两个图的结构,则可以尝试以下方法(基于Mark Needham的文章,从图https://markhneedham.com/blog/2014/05/20/neo4j-2-0-creating-adjacency-matrices/创建邻接矩阵)

基本思想是构造两个邻接矩阵,每个邻接图要与每个节点标识符(业务标识符而不是节点ID)的列和行进行比较,然后对这两个矩阵执行一些代数运算以找到差异。

问题在于,如果图不包含相同的节点标识符,则邻接矩阵将具有不同的维,从而使实际比较变得更加困难,因此诀窍是产生两个大小相同的矩阵,并用邻接矩阵填充一个来自第一张图,第二张来自第二张图。

考虑以下两个图形:

Two similar graphs

图1中的所有节点都标记为:G1,图2中的所有节点都标记为:G2

第1步是从两个图表中找到所有唯一的节点标识符,在这种情况下为'name'属性:

match (g:G1)
with collect(g.name) as g1Names
match (g:G2)
with g1Names + collect(g.name) as collectedNames
unwind collectedNames as allNames
with collect(distinct allNames) as uniqueNames

uniqueNames现在包含两个图中所有唯一标识符的列表。 (有必要先放开收集的名称,然后再将它们收集起来,因为不同的运算符不在列表上起作用-还有更多的收集和放出方式!)

接下来,将创建两个新的唯一标识符列表,以表示第一张图的邻接矩阵的两个维度。

unwind uniqueNames as dim1
unwind uniqueNames as dim2

然后执行可选匹配,以创建第一个图形G1中每个节点与每个其他节点的笛卡尔积。

optional match p = (g1:G1 {name: dim1})-->(g2:G1 {name: dim2})

匹配的路径将存在或从上述match语句返回null。现在将这些转换为节点之间的边缘计数,如果没有连接,则转换为零(邻接矩阵的本质)。匹配的路径经过排序,以在创建矩阵时保持矩阵中行和列的顺序正确。 uniqueNames被传递,因为它将用于构造第二个图的邻接矩阵。

with uniqueNames, dim1, dim2, case when p is null then 0 else count(p) end as edgeCount
order by dim1, dim2

接下来,将边缘汇总为第二维的值列表

with uniqueNames, dim1 as g1DimNames, collect(edgeCount) as g1Matrix
order by g1DimNames

对第二张图重复上面的整个操作,以生成第二张邻接矩阵。

with uniqueNames, g1DimNames, g1Matrix
unwind uniqueNames as dim1
unwind uniqueNames as dim2
optional match p = (g1:G2 {name: dim1})-->(g2:G2 {name: dim2})
with g1DimNames, g1Matrix, dim1, dim2, case when p is null then 0 else count(p) end as edges
order by dim1, dim2
with g1DimNames, g1Matrix, dim1 as g2DimNames, collect(edges) as g2Matrix
order by g1DimNames, g2DimNames

此时g1DimNamesg1Matrixg2DimNamesg2Matrix形成笛卡尔积。通过使用filter语句删除重复的行来分析该产品

with filter( x in collect([g1DimNames, g1Matrix, g2DimNames, g2Matrix]) where x[0] = x[2]) as factored

最后一步是确定两个矩阵之间的差异,这只是查找上面分解结果中不同的行而已。

with  filter(x in factored where x[1] <> x[3]) as diffs
unwind diffs as result
return result

然后我们得到一个结果,该结果显示了不同之处和方式:

Result set

解释结果:前两列代表第一张图的邻接矩阵的子集,后两列代表第二张图的对应的逐行邻接矩阵。字母字符代表节点名称,数字列表代表矩阵中每个原始列(在这种情况下为A到G)的对应行。

看看“ A”行,我们可以得出结论:图1中的节点“ A”拥有节点“ B”和“ C”,图2中的节点“ A”拥有一次节点“ B”和节点“ C”两次

对于“ D”行,节点“ D”在图1中不拥有任何节点,而在图2中拥有节点“ F”和“ G”。

此方法至少有两个警告:

  1. 即使在很小的图中,创建笛卡尔积也是缓慢的。 (我有 一直在用这种技术比较XML模式并比较两个 包含约200个节点的图形大约需要30秒, 与上面示例中的14ms相比, 大小的服务器)。

  2. 当a超过一个时,读取结果矩阵并不容易 数量不多的节点,因为很难跟踪哪一列 对应于哪个节点。 (为了解决这个问题,我已经导出了 结果到一个csv,然后插入节点名称(来自uniqueNames) 进入电子表格的第一行。