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

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

假设我需要在一个简单的地址簿中logging版本。 (地址簿logging存储为扁平json对象)。 我期望的历史:

  • 将很less使用
  • 将被一次全部使用,以“时间机器”的方式呈现
  • 不会有更多的版本比几百到一个单一的logging。 历史不会过期。

我正在考虑以下方法:

  • 创build一个新的对象集合来存储logging的历史logging或更改logging。 它将在每个版本中存储一个对象,并引用地址簿条目。 这样的logging看起来如下:

     {
      '_id':'new id',
      'user':user_id,
      '时间戳':时间戳,
      'address_book_id':'地址簿logging的ID' 
      'old_record':{'first_name':'Jon','last_name':'Doe'...}
     }
    

    这种方法可以修改为每个文档存储一个版本的数组。 但是这似乎是没有任何优势的较慢的方法。

  • 将版本存储为附加到地址簿条目的序列化(JSON)对象。 我不确定如何将这些对象附加到MongoDB文档。 也许作为一个string的数组。 ( 使用CouchDB进行简单文档版本控制后进行build模 )

第一个大问题是, “如何存储变更集”

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

我个人的做法是存储差异。 因为显示这些差异确实是一个特殊的行为,所以我会把差异放在不同的“历史”集合中。

我会使用不同的集合来节省内存空间。 您通常不需要完整的历史logging来进行简单的查询。 因此,通过将历史logging保留在对象之外,可以在查询数据时将其保留在通常访问的内存之外。

为了让我的生活变得简单,我将使历史文档包含时间戳差异字典。 像这样的东西:

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

为了让我的生活变得很简单,我将使用DataObjects(EntityWrapper,不pipe)来访问我的数据。 通常这些对象具有某种forms的历史,所以你可以很容易地重写save()方法来同时进行这个改变。

更新:2015-10

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

有一个名为“Vermongo”的版本控制scheme,解决了其他答复中尚未处理的一些方面。

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

Vermongo将完整的文档副本存储在一个影子集合中。 对于一些使用情况,这可能会造成太多的开销,但我认为这也简化了很多事情。

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

如果你正在寻找一个现成的解决scheme –

Mongoid已经build立了简单的版本控制

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

mongoid-history是一个Ruby插件,通过审计,撤消和重做提供了一个更为复杂的解决scheme

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

这是另一个使用单个文档的当前版本和所有旧版本的解决scheme:

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

data包含所有版本。 data数组是有序的 ,新版本只会得到$push到数组的末尾。 data.vid是版本号,它是一个递增的数字。

获取最新版本:

 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 condition将确保2是最新的vid

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

删除特定的版本:

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

而已!

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

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

 { 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

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

mongoose补丁历史

另一个select是使用mongoose历史插件。

 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. 
Interesting Posts