精华
Neo4j官方教程:第23节,版本控制
教程目录 引自:https://neo4j.com/docs/getting-started/data-modeling/versioning/
版本控制
每次重构数据模型时,都会创建新的版本。跟踪数据结构的变化或显示当前和过去的值对于审计目的、趋势分析等非常有价值。本页面概述了可以用来跟踪随时间变化的数据的不同建模方式。
实体的版本控制
通过对相关实体进行版本控制,可以跟踪数据的变化。这种策略在以下情况下非常有用:
- 访问图中特定实体(例如节点)的多个版本(例如产品在不同时间的名称)。
- 仅检索最新版本(例如产品的当前名称)。
示例:图中显示实体的不同版本,其属性值随时间变化
- 实体版本控制的特点:
- 实体
Product
通过显式关系链接到其不同版本。 - 实体
Product
是不可变的。只有存储在不同版本(State
节点)中的属性会发生变化。 LATEST
关系将实体Product
链接到其最新版本(State
),该版本也是版本 2(V2
)。
- 实体
优缺点
优点 | 缺点 |
---|---|
建模、查询和维护简单。 | 更新节点需要删除 LATEST 关系,并创建实体与其最新版本之间的新关系。 |
对最终用户显式,无需任何转换。 | 如果不使用其他版本控制模式,可能会受到限制,因为如果不是最新版本,可能很难知道要检索哪个版本。 |
查询示例
以下是使用实体版本控制策略的常见查询示例:
获取 ID 为 ‘1’ 的产品版本 2 的名称
MATCH (:Product {id:1})-[:V2]->(s:State)
RETURN s.name
获取 ID 为 ‘1’ 的产品最新版本的名称
MATCH (:Product {id:1})-[:LATEST]->(s:State)
RETURN s.name
基于时间的实体版本控制
实体版本控制的一种变体是基于时间的方法。这在以下情况下非常有用:
- 图快照:通过检索图中某个时间点的所有有效元素(节点和关系)(例如,2023 年 6 月 12 日星期一可用的产品)。
- 图差异:比较两个不同时间戳的图快照(例如,哪些节点被添加、哪些被删除、哪些保持不变)。
- 时间序列遍历:仅遍历图中某个时间点的有效元素(节点或关系),以找到连接基于时间事件的关系的时间顺序(例如,带有车站之间的行程关系的共享单车图)。
- 图历史:建模数据变化的历史。
示例:基于时间的实体版本控制
- 基于时间的版本控制特点:
- 每个元素都有专门的
validFrom
/validTo
时间属性。 - 节点只有在其有效时间范围重叠时才能共享关系。
- 信息可能会重复。
- 图的完整历史可用。
- 每个元素都有专门的
优缺点
优点 | 缺点 |
---|---|
每个元素都有明确的时间间隔,在该时间间隔内元素有效。 | 如果节点状态发生变化,节点必须被复制,并分配新的有效时间间隔。 |
状态绑定到特定元素(无需额外关系)。 | 更新节点需要创建连接到新节点/状态的新关系,并分配新的有效时间间隔。 |
可以聚合所有元素(或某个时间点的有效元素)。 | 数据重复无法避免。 |
查询示例
以下是使用基于时间的实体版本控制策略的常见查询示例:
获取产品 “Rice Cooker” 的当前价格
MATCH (p:Product)
WHERE p.name = "Rice Cooker" AND p.validTo = ∞
RETURN p.price
获取产品 “Rice Cooker” 在 11 月的价格
MATCH (p:Product)
WHERE p.name = "Rice Cooker"
AND datetime(p.validFrom) <= datetime("November") <= datetime(p.validTo)
RETURN p.price
获取当前产品目录及价格
MATCH ()-[r:HAS_PRODUCT]->(p)
WHERE r.validTo = ∞
RETURN p.name, p.price
链表
链表是一种建模策略,当对象的顺序很重要时非常有用。
链表的用途:
- 事件顺序,例如获取银行账户执行的交易顺序。
- 基于关系获取列表中的前一个和下一个元素(例如播放列表中的下一首歌曲,或撤销文本文档中的操作)。
示例:使用链表模型设计进行版本控制
- 链表特点:
- 实体
Product
链接到序列的第一个元素,也可以链接到最后一个元素。 - 与实体版本控制一样,实体
Product
在此处也是不可变的。 - 序列中的每个元素通过
NEXT
关系链接到下一个元素。
- 实体
优缺点
优点 | 缺点 |
---|---|
使用关系获取下一个/前一个元素效率高。 | 如果不使用其他版本控制模式,可能仅限于非常具体的用例。 |
建模和维护简单。 | 很难找到不是第一个或最后一个的特定版本。 |
对最终用户显式。 | - |
查询示例
以下是使用链表版本控制策略的常见查询示例:
获取产品 “Professional chair” 的下一个名称
MATCH (:State{name: "Professional chair"})-[:NEXT]->(s:State)
RETURN s.name
获取 ID 为 ‘1’ 的产品的前一个名称
MATCH (:Product {id:1})-[:LAST]->(:State)<-[:NEXT]-(s:State)
RETURN s.name
时间线树
如建模设计中所述,时间线树是一种常见的建模设计。当您想要跟踪变化时,它可以是一种有用的策略。在此示例中,时间线结构从年份扩展到天,其余的非时间数据节点是图中包含重要数据的节点。
示例:图中显示两个不同的时间线按年份划分
查询示例
如果您想找到某个时间段内发生的所有购买,例如 2012 年 12 月的所有购买,可以从 2012 年导航到 12 月,然后从该分支下的所有叶节点(没有后代的节点)中获取数据:
MATCH (root:Timeline)-[:IN_YEAR]->(year:Year {value:2012})-[:IN_MONTH]->(month:Month {value:12})
WITH month
MATCH (month)-[:ON_DAY]->(day)
MATCH (purchase:Purchase)-[:OCCURRED]->(day)
RETURN purchase
组合方法
一些复杂的用例需要结合上述一种或多种建模技术,因为每种技术都有其优点和缺点。
示例:结合时间线树的更复杂版本控制方法
- 具体组合取决于用例。
- 应考虑查询时间和事务频率等因素。