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

Pio*_*pla 289 database-versioning mongodb

您能否分享一下您如何在MongoDB中实现数据版本控制?(我问过关于Cassandra的类似问题.如果你有任何想法,哪个数据库更好,请分享)

假设我需要在简单的地址簿中对记录进行版本控制.(地址簿记录存储为平面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进行简单文档版本控制后建模)

Gat*_* VP 146

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

  1. 比较速度?
  2. 整个记录副本?

我个人的方法是存储差异.因为这些差异的显示实际上是一个特殊的动作,所以我会将差异放在不同的"历史"集合中.

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

为了让我的生活更轻松,我会让历史文档包含时间戳差异字典.像这样的东西:

{
    _id : "id of address book record",
    changes : { 
                1234567 : { "city" : "Omaha", "state" : "Nebraska" },
                1234568 : { "city" : "Kansas City", "state" : "Missouri" }
               }
}
Run Code Online (Sandbox Code Playgroud)

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

更新时间:2015-10

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

  • 看看编辑.添加到`changes`非常简单:`db.hist.update({_ id:ID},{$ set {changes.12345:CHANGES}},true)`这将执行一个仅更改所需数据的upsert.Mongo使用"缓冲区空间"创建文档来处理这种类型的更改.它还可以监视集合中的文档如何更改并修改每个集合的缓冲区大小.所以MongoDB就是为这种类型的更改而设计的(添加新属性/推送到数组). (5认同)
  • 您可以使用https://github.com/mirek/node-rus-diff为您的历史记录生成(MongoDB兼容)差异. (4认同)
  • 您不担心这样的历史文档(更改对象)会及时增长并且更新效率低下吗?或MongoDB轻松处理文档增长? (2认同)
  • 我做了一些测试,确实空间预订工作得很好.当记录重新分配到数据文件的末尾时,我无法捕获性能损失. (2认同)

Mar*_*ian 30

有一种称为"Vermongo"的版本控制方案,它解决了其他答复中未涉及的一些方面.

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

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

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

  • 没有关于如何实际使用该项目的文档.是不是以某种方式与Mongo生活在一起?它是一个Java库?它只是一种思考问题的方式吗?不知道也没有提示. (6认同)
  • 你是如何实际使用它的? (5认同)

Ben*_*n M 19

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

{
    _id: ObjectId("..."),
    data: [
        { vid: 1, content: "foo" },
        { vid: 2, content: "bar" }
    ]
}
Run Code Online (Sandbox Code Playgroud)

data包含所有版本.该data阵列是有序的,新版本将只能得到$push编到数组的末尾.data.vid是版本ID,这是一个递增的数字.

获取最新版本:

find(
    { "_id":ObjectId("...") },
    { "data":{ $slice:-1 } }
)
Run Code Online (Sandbox Code Playgroud)

获取特定版本vid:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } } }
)
Run Code Online (Sandbox Code Playgroud)

仅返回指定的字段:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这种方式不需要唯一索引,但应用程序逻辑必须注意增加vid插入.

删除特定版本:

update(
    { "_id":ObjectId("...") },
    { $pull:{ "data":{ "vid":2 } } }
)
Run Code Online (Sandbox Code Playgroud)

而已!

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


s01*_*ist 12

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

Mongoid内置了简单版本

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

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

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

  • 对于ruby编程语言. (17认同)

Dan*_*ous 9

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

{
  published: {},
  draft: {},
  history: {
    "1" : {
      metadata: <value>,
      document: {}
    },
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

我在这里进一步解释模型: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

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

猫鼬补丁历史