Neo4j如何建模时间版图

Tom*_*nič 11 graph neo4j cypher

我的部分图表具有以下架构:

在此输入图像描述

图的主要部分是域,其中有一些人链接到它.Person对电子邮件属性有一个独特的约束,因为我也有来自其他来源的数据,这非常适合.

在我的情况下,一个人可以是管理员,他有一些链接到他的设备/日历.我从一个SQL数据库中获取这些数据,我导入了几个表来组合整个图片.我从一个表开始,它有两列,管理员的电子邮件和他的用户ID.此用户标识仅适用于生产数据库,并且不会全局用于其他源.这就是为什么我使用电子邮件作为人的全球ID.我目前正在使用以下查询来导入所有生产表都链接到的用户ID.我总是得到用户设置和信息的当前快照.此查询每天运行4次:

CALL apoc.load.jdbc(url, import_query) yield row
MERGE (p:Person{email:row.email})
SET p.user_id = row.id
Run Code Online (Sandbox Code Playgroud)

然后我从其他表导入链接到此用户ID的所有数据.

现在问题出现了,因为来自生产数据库的用户可以更改他的电子邮件.所以我现在导入这个的方式我将最终得到两个具有相同user_id的人,随后所有设备/日历将链接到两个人,因为他们都共享相同的user_id.所以这不是对现实的准确表述.我们还需要捕获设备连接/断开连接到特定user_id的时间,因为可以连接/断开设备并将其借给具有不同管理员(user_id)的朋友.

如何更改我的图形模型(导入查询),以便:

  1. 查询当前是谁的管理员不需要复杂的查询
  2. 查询当前已连接设备的人不需要复杂查询
  3. 查询历史可能会更复杂一些.

Bru*_*res 11

这个答案基于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)
Run Code Online (Sandbox Code Playgroud)

结果:

初始图形状态

上图有3个人节点.这些节点是域节点的成员.人员节点与person_id = 1设备连接device_id = 1.此外,person_id = 1是当前的管理员.性能fromto内部的:ADMIN:CONNECTED_DEVICE关系来管理图形结构的历史.from表示时间的起点和to终点.为简化起见,我使用0作为图的初始时间,使用1000作为时间结束常量.在现实世界图中,当前时间(以毫秒为单位)可用于表示时间点.此外,Long.MAX_VALUE可以替代地用作EOT常数.与...的关系to = 1000意味着与其关联的时段没有当前上限.

查询:

使用此图表,要获得当前管理员,我可以执行以下操作:

MATCH (person:Person)-[:ADMIN {to:1000}]->(:Admin)
RETURN person
Run Code Online (Sandbox Code Playgroud)

结果将是:

?????????????????
?"person"       ?
?????????????????
?{"person_id":1}?
?????????????????
Run Code Online (Sandbox Code Playgroud)

给定一个设备,以获取当前连接的用户:

MATCH (:Device {device_id : 1})<-[:CONNECTED_DEVICE {to : 1000}]-(person:Person)
RETURN person
Run Code Online (Sandbox Code Playgroud)

导致:

?????????????????
?"person"       ?
?????????????????
?{"person_id":1}?
?????????????????
Run Code Online (Sandbox Code Playgroud)

要查询当前管理员和连接到设备的当前人员,请使用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
Run Code Online (Sandbox Code Playgroud)

导致:

?????????????????????????????????????????????
?"person"       ?"device"       ?"from"?"to"?
?????????????????????????????????????????????
?{"person_id":1}?{"device_id":1}?0     ?1000?
?????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

上述结果表明,直到今天才开始person_id = 1连接device_id = 1.

更改图形结构

考虑当前时间点是30.现在user_id = 1断开连接device_id = 1.user_id = 2将连接到它.为了表示这种结构变化,我将运行以下查询:

// Get the current connected person
MATCH (person1:Person)-[old:CONNECTED_DEVICE {to : 1000}]->(device:Device {device_id : 1})
// get person_id = 2
MATCH (person2:Person {person_id : 2}) 
 // set 30 as the end time of the connection between person_id = 1 and device_id = 1
SET old.to = 30
// set person_id = 2 as the current connected user to device_id = 1
// (from time point 31 to now)
CREATE (person2)-[:CONNECTED_DEVICE {from : 31, to: 1000}]->(device) 
Run Code Online (Sandbox Code Playgroud)

结果图将是:

结构变化后的图表

在此结构更改后,连接历史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?
?????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

以上结果表明user_id = 1连接device_id = 1时间为0到30次.person_id = 2目前已连接到device_id = 1.

现在连接的当前人device_id = 1person_id = 2:

MATCH (:Device {device_id : 1})<-[:CONNECTED_DEVICE {to : 1000}]-(person:Person)
RETURN person

?????????????????
?"person"       ?
?????????????????
?{"person_id":2}?
?????????????????
Run Code Online (Sandbox Code Playgroud)

可以应用相同的方法来管理管理历史记录.

显然这种方法有一些缺点:

  • 需要管理一组额外的关系
  • 更昂贵的查询
  • 更复杂的查询

但是如果你真的需要版本控制模式,我相信这种方法是一个很好的选择,或者(至少)是一个很好的起点.