这篇回答基于Ian Robinson关于时间版本化图的文章。
我不知道这篇回答是否覆盖了问题的所有要求,但我相信它可以提供一些见解。
此外,我认为您只对结构性版本控制感兴趣(也就是说:您不关心关于域用户名称随时间变化的查询)。最后,我使用了您图模型的部分表示,但我相信这里展示的概念可以应用于整个图表。
初始图状态:
考虑以下Cypher语句创建一个初始图状态:
CREATE (admin:Admin)
CREATE (person1:Person {person_id : 1})
CREATE (person2:Person {person_id : 2})
CREATE (person3:Person {person_id : 3})
CREATE (domain1:Domain {domain_id : 1})
CREATE (device1:Device {device_id : 1})
CREATE (person1)-[:ADMIN {from : 0, to : 1000}]->(admin)
CREATE (person1)-[:CONNECTED_DEVICE {from : 0, to : 1000}]->(device1)
CREATE (domain1)-[:MEMBER]->(person1)
CREATE (domain1)-[:MEMBER]->(person2)
CREATE (domain1)-[:MEMBER]->(person3)
结果:
![Initial Graph state](https://istack.dev59.com/TajsU.webp)
上面的图形有3个人节点。这些节点都是一个域节点的成员。具有person_id = 1
的人节点连接到具有device_id = 1
的设备。同时,person_id = 1
是当前管理员。在:ADMIN
和:CONNECTED_DEVICE
关系中的属性from
和to
被用来管理图形结构的历史记录。 from
代表时间的起点,to
代表时间的终点。为了简化,我使用0作为图形的初始时间,1000作为结束时间常量。在真实世界的图形中,毫秒级别的当前时间可以用于表示时间点。此外,Long.MAX_VALUE
也可以用作EOT常量。一个带有to = 1000
的关系意味着与之相关的周期没有当前的上限。
查询:
使用此图形,要获取当前管理员,可以执行以下操作:
MATCH (person:Person)-[:ADMIN {to:1000}]->(:Admin)
RETURN person
结果将是:
╒═══════════════╕
│"person" │
╞═══════════════╡
│{"person_id":1}│
└───────────────┘
给定一个设备,获取当前连接的用户:
MATCH (:Device {device_id : 1})<-[:CONNECTED_DEVICE {to : 1000}]-(person:Person)
RETURN person
结果如下:
╒═══════════════╕
│"person" │
╞═══════════════╡
│{"person_id":1}│
└───────────────┘
使用 End-Of-Time 常量来查询当前管理员和设备上当前连接的用户。
查询设备连接/断开事件:
MATCH (device:Device {device_id : 1})<-[r:CONNECTED_DEVICE]-(person:Person)
RETURN person AS person, device AS device, r.from AS from, r.to AS to
ORDER BY r.from
结果为:
╒═══════════════╤═══════════════╤══════╤════╕
│"person" │"device" │"from"│"to"│
╞═══════════════╪═══════════════╪══════╪════╡
│{"person_id":1}│{"device_id":1}│0 │1000│
└───────────────┴───────────────┴──────┴────┘
以上结果表明,person_id = 1
从一开始直到现在都连接着 device_id = 1
。
更改图形结构
考虑当前时刻为30。现在 user_id = 1
与 device_id = 1
断开连接,user_id = 2
连接上它。为了表示这种结构性变化,我将运行以下查询:
MATCH (person1:Person)-[old:CONNECTED_DEVICE {to : 1000}]->(device:Device {device_id : 1})
MATCH (person2:Person {person_id : 2})
SET old.to = 30
CREATE (person2)-[:CONNECTED_DEVICE {from : 31, to: 1000}]->(device)
生成的图表如下:
![结构变化后的图表](https://istack.dev59.com/M94yt.webp)
进行这次结构变化之后,device_id = 1
的连接历史将如下所示:
MATCH (device:Device {device_id : 1})<-[r:CONNECTED_DEVICE]-(person:Person)
RETURN person AS person, device AS device, r.from AS from, r.to AS to
ORDER BY r.from
╒═══════════════╤═══════════════╤══════╤════╕
│"person" │"device" │"from"│"to"│
╞═══════════════╪═══════════════╪══════╪════╡
│{"person_id":1}│{"device_id":1}│0 │30 │
├───────────────┼───────────────┼──────┼────┤
│{"person_id":2}│{"device_id":1}│31 │1000│
└───────────────┴───────────────┴──────┴────┘
上述结果表明,
user_id = 1
在0到30时间内连接到
device_id = 1
。
person_id = 2
目前连接到
device_id = 1
。
现在连接到
device_id = 1
的当前用户是
person_id = 2
:
MATCH (:Device {device_id : 1})<-[:CONNECTED_DEVICE {to : 1000}]-(person:Person)
RETURN person
╒═══════════════╕
│"person" │
╞═══════════════╡
│{"person_id":2}│
└───────────────┘
相同的方法可以应用于管理管理员历史记录。
显然,这种方法有一些缺点:
- 需要管理一组额外的关联关系
- 查询更加昂贵
- 查询更加复杂
但是,如果您真的需要一个版本控制模式,我相信这种方法是一个不错的选择或(至少)一个良好的起点。