代码之家  ›  专栏  ›  技术社区  ›  Piotr Czapla

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

  •  276
  • Piotr Czapla  · 技术社区  · 14 年前

    您能分享一下您的想法吗?您将如何在MongoDB中实现数据版本控制。(我问过) similar question regarding 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文档中。可能是一个字符串数组。 ( Modelled after Simple Document Versioning with CouchDB )

    9 回复  |  直到 7 年前
        1
  •  140
  •   Gates VP    9 年前

    潜入这个领域的第一个大问题是 “您希望如何存储变更集” ?

    1. 差异?
    2. 完整的记录副本?

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

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

    为了让我的生活更容易,我会让一个历史文档包含一个时间戳差异的字典。像这样:

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

    为了让我的生活变得简单,我将把我用来访问数据的数据对象(EntityWrapper,随便什么)的这一部分变成现实。通常,这些对象具有某种形式的历史,因此您可以轻松地重写 save() 方法同时进行此更改。

    更新:2015-10

    好像现在有 a spec for handling JSON diffs . 这似乎是存储差异/更改的更强大的方法。

        2
  •  30
  •   David Pfeffer    12 年前

    有一个称为“vermongo”的版本控制方案,它处理了一些在其他回复中没有处理的方面。

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

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

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

        3
  •  19
  •   Benjamin M    10 年前

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

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

    data 包含 全部的 版本。这个 数据 数组是 命令 ,新版本只能 $push ed到数组的结尾。 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 维生素D 最新版本和 3 是否插入新版本。因为你需要最新版本的 维生素D 很容易得到下一个版本的 维生素D : nextVID = oldVID + 1 .

    这个 $and 条件将确保 是最新的 维生素D .

    这样就不需要唯一的索引,但是应用程序逻辑必须注意增加 维生素D 关于插入。

    删除特定版本:

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

    就是这样!

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

        4
  •  12
  •   s01ipsist    12 年前

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

    MongoID内置了简单的版本控制

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

    MongoID历史是一个Ruby插件,它提供了一个非常复杂的解决方案,包括审计、撤消和重做。

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

        5
  •  9
  •   Daniel Watrous    12 年前

    我研究过这个解决方案,它包含数据的已发布、草稿和历史版本:

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

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

    对于那些可能在 爪哇 ,下面是一个示例:

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

    如果你愿意的话,包括你能输入的所有代码

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

        6
  •  3
  •   bmw15    8 年前

    如果您正在使用Mongoose,我发现以下插件是 JSON Patch 格式

    mongoose-patch-history

        7
  •  2
  •   Muhammad Reda    8 年前

    另一个选择是使用 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.
    
        8
  •  1
  •   helcode srinivasan Elangovan    7 年前

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

    nicklozon/meteor-collection-revisions

    另一种声音选择是使用流星Vermongo( here )

        9
  •  -2
  •   Demel    7 年前

    尝试使用javers。好的图书馆。

    推荐文章