GraphGists

网络版本

最近,Ian Robinson 发表了一篇关于网络版本控制的不错的博文(点击此处阅读),介绍了如何使用身份节点和将结构与状态分离。在本篇 gist 中,我想通过引入另一种使用关系节点的方法来发表我的看法。这更多地是关于网络的结构,而不是节点的状态,但它以不同的方式处理版本控制。正如 Ian 在他的博文中所说,总需要权衡取舍,因为版本控制会增加存储和处理的额外负担。

关系节点?

什么是关系节点?

关系节点是两个网络节点之间的中间节点,表示节点之间的关系。

因此,如果 A 和 B 是两个网络节点,而不是使用[:RELTYPE]关系连接它们,则会在两者之间插入一个关系节点,并使用[:RS][RE]连接它,分别代表关系开始关系结束

Gist01

为什么要使用关系节点?

Neo4j(我首选的图数据库)当前 2.0.3 版本不允许在关系和节点之间创建关系。而如果我想表明两个网络节点之间的特定关系属于一个特定的网络(本身由一个节点表示),则需要这样做。

此外,我的用例要求我在网络之间共享网络节点,但在不同的结构中。

另一个原因是网络由节点之间的关系决定,而不是节点本身。如果我有两个节点 A 和 B,这并不能告诉我它们之间关系的信息,例如方向、类型或连接它们的多个关系。另一方面,如果我了解一个关系,我就会知道它的起始/结束节点及其类型。一个节点可以没有关系存在,而一个关系只能与起始和结束节点一起存在。

使用此方法的第四个原因是它允许我比较网络,例如回答“网络 1 的版本 3 能否与网络 2 的版本 1 共存?”之类的问题。但是,这不会是本篇 gist 的主题,因为它需要一些额外的思考时间。

创建网络

设置

创建网络的过程包括以下步骤:

  • 创建一个表示网络的节点。

  • 创建网络节点。

  • 创建通过[:RS][:RE]关系链接到网络节点的关系节点,并使用[:UPDATE {type:'ADD', version:1]链接到网络节点。

设置查询创建了网络 1 的第一个版本,将 A 链到 B 再到 C。请注意,[:UPDATE]关系的version属性可以简单地替换或添加时间戳属性,使图感知时间。

CREATE (_0:`nw` {`name`:"Network1"})
CREATE (_5:`nwnode` {`name`:"A"})
CREATE (_6:`nwnode` {`name`:"B"})
CREATE (_7:`nwnode` {`name`:"C"})
CREATE (_13:`relationnode` {`id`:1})
CREATE (_14:`relationnode` {`id`:2})
CREATE _5-[:`RS`]->_13
CREATE _6-[:`RS`]->_14
CREATE _13-[:`RE`]->_6
CREATE _14-[:`RE`]->_7
CREATE _0-[:`UPDATE` {`type`:"ADD", `version`:1}]->_14
CREATE _0-[:`UPDATE` {`type`:"ADD", `version`:1}]->_13

此网络中的关系为:

MATCH (s:nwnode)-[:RS]->(rn:relationnode)-[:RE]->(e:nwnode)
RETURN s.name as From,e.name as To ORDER BY From,To

向网络添加节点

如果必须向网络添加节点,我们需要:

  • 创建新节点。

  • 创建一个关系节点。

  • 创建一个[:UPDATE],这次版本为:2。

MATCH (nw:nw {name:"Network1"}),(sn:nwnode {name:"C"})
CREATE (_8:`nwnode` {`name`:"D"})
CREATE (_15:`relationnode` {`id`:3})
CREATE (sn)-[:`RS`]->_15-[:`RE`]->_8
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:2}]->_15

我们网络的版本 2 中的关系为:

MATCH (s:nwnode)-[:RS]->(rn:relationnode)-[:RE]->(e:nwnode)
RETURN s.name as From,e.name as To ORDER BY From,To

从网络中移除节点

如果必须从网络中移除节点,我们需要做的是向相关的关系节点添加[:UPDATE {type:'REMOVE', version:3}]关系。因此,完全从网络 1 中移除节点“C”只需:

  • 查找连接“C”与网络 1 的关系节点。

  • 在网络 1 和这些关系节点之间创建[:UPDATE {type:'REMOVE', version:3}]关系。

MATCH (nw:nw {name:"Network1"})-[:UPDATE]->(rn:relationnode)-[:RS|RE]-(n:nwnode {name:"C"})
WITH nw,COLLECT(DISTINCT rn) AS rns
FOREACH ( i IN rns |
  CREATE (nw)-[:`UPDATE` {`type`:"REMOVE", `version`:3}]->(i)
)

此查询给出了现在存在的关系的完整列表:

MATCH (nw:nw {name:"Network1"})-[u:UPDATE]->(rn:relationnode),(s:nwnode)-[:RS]->(rn)-[:RE]->(e:nwnode)
RETURN u.version AS Version,u.type AS UpdateType,s.name AS From, e.name AS To
ORDER BY Version,UpdateType,From

为了获取我们网络的版本 3 中的关系,我们必须将搜索限制在网络范围内,并且只考虑类型为“ADD”的最新[:UPDATE]事务。

您可以看到,通过移除 C,我们也把 D 从网络中切断了。太糟糕了,但幸运的是,我们也可以在现有节点之间创建关系。

如果必须向网络添加节点,我们需要:

  • 获取节点。

  • 创建一个关系节点。

  • 创建一个[:UPDATE],这次版本为:4。

MATCH (nw:nw {name:"Network1"}),(sn:nwnode {name:"B"}),(en:nwnode {name:"D"})
CREATE (rn:`relationnode` {`id`:7})
CREATE (sn)-[:`RS`]->(rn)-[:`RE`]->(en)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:4}]->(rn)

网络的更新版本为:

历史版本

可以通过仅考虑[:UPDATE]关系直到该版本来检索网络的特定版本。

网络 1 的版本 1

以及作为图形(仅在您访问此处时有效,即 2014 年 5 月 14 日 https://jexp.github.io/graphgist/?40364ac2a52f57aa520a。感谢 @Jim_Salmons 指出)。

网络 1 的版本 2

网络 1 的版本 3

添加第二个网络

使用此方法的原因之一是我需要能够在第二个网络中重用网络节点,并使用其自己的关系。

让我们创建一个具有 2 个版本的网络 2。

  • 版本 1

    • C 到 A

  • 版本 2

    • A 到 D

    • B 到 A

    • D 到 E

    • E 到 A

    • E 到 B

MATCH (na:nwnode {name:"A"}),(nb:nwnode {name:"B"}),(nc:nwnode {name:"C"}),(nd:nwnode {name:"D"})
CREATE (nw:`nw` {`name`:"Network2"})
CREATE (ne:`nwnode` {`name`:"E"})

CREATE (nc)-[:`RS`]->(rn1:relationnode)-[:`RE`]->(na)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:1}]->(rn1)
CREATE (nw)-[:`UPDATE` {`type`:"REMOVE", `version`:2}]->(rn1)

CREATE (nb)-[:`RS`]->(rn2:relationnode)-[:`RE`]->(na)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:2}]->(rn2)

CREATE (na)-[:`RS`]->(rn3:relationnode)-[:`RE`]->(nd)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:2}]->(rn3)

CREATE (nd)-[:`RS`]->(rn4:relationnode)-[:`RE`]->(ne)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:2}]->(rn4)

CREATE (ne)-[:`RS`]->(rn5:relationnode)-[:`RE`]->(nb)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:2}]->(rn5)

CREATE (ne)-[:`RS`]->(rn6:relationnode)-[:`RE`]->(na)
CREATE (nw)-[:`UPDATE` {`type`:"ADD", `version`:2}]->(rn6)

之后,当前网络 2 的关系为:

检查

当然,网络 1 的版本 2 仍然完好无损。

后续思考

这是我的第一个 gist。设置起来很有趣。如果能有 //graph 东西可以可视化查询结果,而不是整个图形,那就太好了。更新:我们在 https://jexp.github.io/graphgist/?40364ac2a52f57aa520a 上有了 :),并且我想在不久的将来也会在 https://gist.neo4j.org/ 上有。

我更喜欢尽可能以图形的方式做事。不确定我是否在这方面取得了成功,但我试图记住,打开和关闭节点和关系而不是进行遍历是有代价的。

[:UPDATE]关系添加另一个属性{status: "pending"}将允许我运行 Cypher 来回答诸如:“如果我这样做会怎样?”之类的问题。

正如我之前所说,向[:UPDATE]关系添加时间戳将使其感知时间。您还可以创建版本节点的链接列表并将[:UPDATE]链接到这些节点。

比较不同的网络将是我的下一个 gist 的内容。

我还将处理使用关系节点的可视化方面。有时您希望看到它们,有时它们会妨碍您,因为力导向布局可能会导致您不希望出现的角度。幸运的是,Cypher 允许您以可视化引擎可以处理的方式调整结果,并欺骗可视化引擎使其认为您提供给它的内容是关系。

感兴趣吗?在 @tomzeppenfeldt / @ophileon 上关注我。当然也欢迎任何评论。