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

时间:2010-11-15 14:07:47

标签: mongodb database-versioning

您能否分享一下您如何在MongoDB中实现数据版本控制? (我问过similar question regarding Cassandra。如果您有任何想法,那么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文档。也许是一个字符串数组。 (Modelled after Simple Document Versioning with CouchDB

9 个答案:

答案 0 :(得分:141)

潜入这个问题的第一个重要问题是“你想如何存储变更集”

  1. 的diff?
  2. 整个记录副本?
  3. 我个人的做法是存储差异。因为这些差异的显示实际上是一个特殊的动作,我会把差异放在一个不同的“历史”集合中。

    我会使用不同的集合来节省内存空间。您通常不希望简单查询的完整历史记录。因此,通过将历史记录保留在对象之外,您还可以在查询数据时将其保留在常用内存中。

    为了让我的生活轻松,我会制作一份历史文件,其中包含时间戳差异词典。像这样:

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

    为了让我的生活变得非常轻松,我会将这部分用于访问我的数据的DataObjects(EntityWrapper,无论如何)。通常,这些对象具有某种形式的历史记录,因此您可以轻松覆盖save()方法以同时进行此更改。

    更新时间:2015-10

    看起来现在有a spec for handling JSON diffs。这似乎是一种更健壮的方式来存储差异/变化。

答案 1 :(得分:30)

有一个名为“Vermongo”的版本控制方案,它解决了其他答复中未涉及的某些方面。

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

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

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

答案 2 :(得分:19)

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

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

data包含所有版本。 data数组有序,新版本只会$push到数组的末尾。 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是当前最新版本的vid3是插入的新版本。由于您需要最新版本的vid,因此很容易获得下一个版本vidnextVID = oldVID + 1

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

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

删除特定版本:

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

那就是它!

(记住每个文件限制16MB)

答案 3 :(得分:12)

如果您正在寻找即时解决方案 -

Mongoid内置了简单的版本控制

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

mongoid-history是一个Ruby插件,提供了一个更复杂的审计,撤销和重做的解决方案

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

答案 4 :(得分:9)

我通过这个解决方案来处理数据的已发布,草稿和历史版本:

{
  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

答案 5 :(得分:3)

如果您使用的是mongoose,我发现以下插件是JSON Patch格式的有用实现

mongoose-patch-history

答案 6 :(得分:2)

另一种选择是使用mongoose-history插件。

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.

答案 7 :(得分:1)

我已经使用下面的包用于meteor / MongoDB项目,它运行良好,主要优点是它将历史/修订存储在同一文档中的数组中,因此无需额外的出版物或中间件来访问改变历史。它可以支持有限数量的先前版本(例如前十个版本),它还支持更改连接(因此在特定时间段内发生的所有更改都将由一个修订版覆盖)。

nicklozon/meteor-collection-revisions

另一个声音选项是使用Meteor Vermongo(here

答案 8 :(得分:-2)

尝试使用Javers。好图书馆。