MongoDB中实现数据版本控制的方法

你能分享一下你如何在MongoDB中实现数据版本控制的想法吗?(我已经问过关于卡桑德拉的类似问题。如果你有任何想法,哪个db更好,请分享)

假设我需要对一个简单地址簿中的记录进行版本化。(地址簿记录存储为平面json对象)。我希望历史:

  • 是否会不经常使用
  • 会被一次性用“时间机器”的方式呈现出来吗
  • 每张唱片的版本不会超过几百个。 .历史不会过期

我正在考虑以下方法:

  • 创建一个新的对象集合来存储记录的历史或记录的更改。它将为每个版本存储一个对象,并引用地址簿条目。这些记录如下:

    {
    '_id': 'new id',
    'user': user_id,
    'timestamp': timestamp,
    'address_book_id': 'id of the address book record'
    'old_record': {'first_name': 'Jon', 'last_name':'Doe' ...}
    }
    

    可以修改此方法以存储每个文档的版本数组。但这似乎是一种更慢的方法,没有任何好处

  • 存储版本为附加到地址簿条目的序列化(JSON)对象。我不确定如何将这样的对象附加到MongoDB文档。也许是一个字符串数组。 李(模仿CouchDB的简单文档版本控制) < / p > < / >

145750 次浏览

第一个大问题是“您想如何存储更改集”;?

  1. 差别吗?
  2. 完整的唱片副本?

我个人的方法是储存差异。因为这些差异的显示确实是一个特殊的动作,我会把差异放在一个不同的“历史”中。收集。

我将使用不同的集合来节省内存空间。对于一个简单的查询,通常不需要完整的历史记录。因此,当数据被查询时,通过将历史记录排除在对象之外,你也可以将它排除在常用访问的内存之外。

为了简化我的工作,我将创建一个历史文档,其中包含一个带有时间戳的差异字典。就像这样:

{
_id : "id of address book record",
changes : {
1234567 : { "city" : "Omaha", "state" : "Nebraska" },
1234568 : { "city" : "Kansas City", "state" : "Missouri" }
}
}

为了使我的工作更加简单,我将使我的数据对象(EntityWrapper,等等)的这一部分用于访问我的数据。一般来说,这些对象都有某种形式的历史记录,所以你可以很容易地覆盖save()方法来同时进行此更改。

更新:2015 - 10

看起来现在有处理JSON差异的规范。这似乎是一种更健壮的存储差异/更改的方式。

如果你正在寻找一个现成的解决方案-

Mongoid内置了简单的版本控制

http://mongoid.org/en/mongoid/docs/extras.html#versioning

mongoid-history是一个Ruby插件,它提供了一个非常复杂的解决方案,包括审计、撤销和重做

https://github.com/aq1018/mongoid-history

有一个名为“Vermongo”的版本控制方案,它解决了其他回复中没有处理的一些方面。

其中一个问题是并发更新,另一个问题是删除文档。

Vermongo将完整的文档副本存储在影子集合中。对于某些用例,这可能会导致太多的开销,但我认为它也简化了许多事情。

https://github.com/thiloplanz/v7files/wiki/Vermongo

我通过这个解决方案,容纳了数据的公开版本、草稿版本和历史版本:

{
published: {},
draft: {},
history: {
"1" : {
metadata: <value>,
document: {}
},
...
}
}

我在这里进一步解释模型:http://software.danielwatrous.com/representing-revision-data-in-mongodb/

对于那些可能在Java中实现类似的东西的人,这里有一个例子:

http://software.danielwatrous.com/using-java-to-work-with-versioned-data/

包括您可以派生的所有代码

https://github.com/dwatrous/mongodb-revision-objects

下面是另一种解决方案,使用一个文档针对当前版本和所有旧版本:

{
_id: ObjectId("..."),
data: [
{ vid: 1, content: "foo" },
{ vid: 2, content: "bar" }
]
}

data包含所有版本。data数组是命令,新版本只会得到$pushed数组的结尾。data.vid是版本id,它是一个递增的数字。

获取最新版本:

find(
{ "_id":ObjectId("...") },
{ "data":{ $slice:-1 } }
)

通过vid获取一个特定的版本:

find(
{ "_id":ObjectId("...") },
{ "data":{ $elemMatch:{ "vid":1 } } }
)

只返回指定的字段:

find(
{ "_id":ObjectId("...") },
{ "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)

插入新版本:(并防止并发插入/更新)

update(
{
"_id":ObjectId("..."),
$and:[
{ "data.vid":{ $not:{ $gt:2 } } },
{ "data.vid":2 }
]
},
{ $push:{ "data":{ "vid":3, "content":"baz" } } }
)

2是当前最新版本的vid,而3是插入的新版本。因为你需要最新版本的vid,所以很容易得到下一个版本的vid: nextVID = oldVID + 1

$and条件将确保2是最新的vid

这样就不需要唯一的索引,但应用程序逻辑必须负责在插入时增加vid

删除特定版本:

update(
{ "_id":ObjectId("...") },
{ $pull:{ "data":{ "vid":2 } } }
)

就是这样!

(请记住每个文档限制为16MB)

如果你正在使用mongoose,我发现下面的插件是JSON补丁格式的有用实现

mongoose-patch-history . sh

另一个选择是使用mongoose-history plugin。

let mongoose = require('mongoose');
let mongooseHistory = require('mongoose-history');
let Schema = mongoose.Schema;


let MySchema = Post = new Schema({
title: String,
status: Boolean
});


MySchema.plugin(mongooseHistory);
// The plugin will automatically create a new collection with the schema name + "_history".
// In this case, collection with name "my_schema_history" will be created.

我已经在一个meteor/MongoDB项目中使用了下面的包,它工作得很好,主要的优点是它将历史/修订存储在同一个文档的数组中,因此不需要额外的发布或中间件来访问更改历史。它可以支持有限数量的先前版本(例如最近的十个版本),它还支持更改连接(因此在特定时期内发生的所有更改将被一个修订覆盖)。

nicklozon/meteor-collection-revisions

另一个声音选项是使用Meteor Vermongo (在这里)