好问题,我自己也正在调查。
每次更改都创建一个新版本
我遇到了Ruby的Mongoid驱动程序的Versioning模块。我还没有亲自使用过它,但是从我发现的情况来看,它会为每个文档添加一个版本号。旧版本嵌入在文档本身中。主要缺点是,每次更改都会复制整个文档,这将导致在处理大型文档时存储大量重复内容。这种方法很好,但是当您处理小型文档和/或不经常更新文档时。
仅将更改存储在新版本中
另一种方法是仅将更改的字段存储在新版本中。然后,您可以“拉平”历史记录以重建文档的任何版本。但是,这非常复杂,因为您需要跟踪模型中的更改并以应用程序可以重建最新文档的方式存储更新和删除。这可能很棘手,因为您要处理结构化文档而不是平面SQL表。
将更改存储在文档中
每个字段也可以有各自的历史记录。用这种方法将文档重建为给定的版本要容易得多。在您的应用程序中,您不必显式跟踪更改,而只需在更改属性值时创建该属性的新版本。文档可能看起来像这样:
{
_id: "4c6b9456f61f000000007ba6"
title: [
{ version: 1, value: "Hello world" },
{ version: 6, value: "Foo" }
],
body: [
{ version: 1, value: "Is this thing on?" },
{ version: 2, value: "What should I write?" },
{ version: 6, value: "This is the new body" }
],
tags: [
{ version: 1, value: [ "test", "trivial" ] },
{ version: 6, value: [ "foo", "test" ] }
],
comments: [
{
author: "joe", // Unversioned field
body: [
{ version: 3, value: "Something cool" }
]
},
{
author: "xxx",
body: [
{ version: 4, value: "Spam" },
{ version: 5, deleted: true }
]
},
{
author: "jim",
body: [
{ version: 7, value: "Not bad" },
{ version: 8, value: "Not bad at all" }
]
}
]
}
不过,将文档的一部分标记为版本中的删除仍然有些尴尬。您可以state
为可以从应用程序中删除/还原的零件引入一个字段:
{
author: "xxx",
body: [
{ version: 4, value: "Spam" }
],
state: [
{ version: 4, deleted: false },
{ version: 5, deleted: true }
]
}
使用这些方法中的每一种,您都可以将一个最新的扁平化版本存储在一个集合中,将历史数据存储在一个单独的集合中。如果您只对文档的最新版本感兴趣,这将缩短查询时间。但是,当您同时需要最新版本和历史数据时,则需要执行两个查询,而不是一个。因此,选择使用单个集合还是两个单独的集合应该取决于您的应用程序需要历史版本的频率。
这个答案大部分只是我思想的转储,我实际上还没有尝试过。回顾一下,第一个选择可能是最简单,最好的解决方案,除非重复数据的开销对您的应用程序而言非常重要。第二种选择非常复杂,可能不值得付出努力。第三种选择基本上是对第二种选择的优化,应该更容易实现,但是除非您真的不能采用第一种选择,否则可能不值得花大力气进行实施。
期待对此反馈以及其他人对问题的解决方案:)