Способы реализации управления версиями данных в MongoDB

можете ли вы поделиться своими мыслями, как бы вы реализовали управление версиями данных в MongoDB. (Я спросил аналогичный вопрос относительно Кассандры. Если у вас есть какие-либо мысли, которые db лучше для этого, пожалуйста, поделитесь)

предположим, что мне нужны записи версий в простой адресной книге. (Записи адресной книги хранятся как плоские объекты json). Я ожидаю, что история:

  • будет использоваться нечасто
  • будет использоваться все сразу, чтобы представить его в стиле "Машины времени"!--9-->
  • не будет больше версий, чем несколько сотен в одну запись. история не заканчивается.

Я рассматриваю следующие подходы:

  • создайте новую коллекцию объектов для хранения истории записей или изменений записей. Он будет хранить один объект на версию со ссылкой на запись адресной книги. Такие записи выглядели бы следующим образом:

    {
     '_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)

9 ответов


первый большой вопрос при погружении в это "как вы хотите хранить наборы изменений"?

  1. диффов?
  2. все записи копии?

мой личный подход состоял бы в том, чтобы хранить различия. Поскольку отображение этих различий действительно является специальным действием, я бы поместил различия в другую коллекцию "история".

Я бы использовал другую коллекцию для экономии места в памяти. Вам обычно не нужна полная история для простой запрос. Таким образом, сохраняя историю из объекта, вы также можете сохранить ее из общедоступной памяти при запросе этих данных.

чтобы сделать мою жизнь легкой, я бы сделал исторический документ, содержащий словарь временных меток различий. Что-то вроде этого:--5-->

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

чтобы сделать мою жизнь действительно легкой, я бы сделал эту часть моих DataObjects (EntityWrapper, что угодно), которые я использую для доступа к моим данным. Как правило, эти объекты имеют некоторую форму истории, так что вы может легко переопределить save() способ, чтобы сделать это изменение в то же время.

обновление: 2015-10

похоже, что теперь есть спецификация для обработки различий JSON. Это кажется более надежным способом хранения различий / изменений.


существует схема управления версиями под названием "Вермонго", которая затрагивает некоторые аспекты, которые не рассматривались в других ответах.

одна из этих проблем-одновременное обновление, другая-удаление документов.

Vermongo хранит полные копии документов в теневой коллекции. Для некоторых случаев использования это может вызвать слишком много накладных расходов, но я думаю, что это также упрощает многие вещи.

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


вот еще одно решение, использующее один документ для текущей версии и всех старых версий:

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

data содержит все версий. The data массив приказал новые версии только $pushed до конца массива. 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 - это vid текущей последней версии и 3 вставляется новая версия. Потому что вам нужна самая последняя версия vid, это легко сделать, чтобы получить следующую версию vid: nextVID = oldVID + 1.

на $and условие обеспечит, что 2 последний vid.

таким образом, нет необходимости в уникальном индексе, но логика приложения должна заботиться о приращении vid вставить.

удалить определенную версию:

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

вот именно!

(помните ограничение 16 Мб на документ)


Если вы ищете готовое к рулону решение -

Mongoid имеет встроенную простую версию

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

mongoid-history-это плагин Ruby, который предоставляет значительно более сложное решение с аудитом, отменой и повтором

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


Я работал через это решение, которое вмещает опубликованные, черновики и исторические версии данных:

{
  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


Если вы используете мангуста, я нашел следующий плагин, чтобы быть полезной реализацией JSON Patch


другой вариант-использовать Мангуст-история плагин.

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, и он работает хорошо, основное преимущество заключается в том, что он хранит историю / ревизии в массиве в том же документе, следовательно, нет необходимости в дополнительных публикациях или промежуточном ПО для доступа к истории изменений. Он может поддерживать ограниченное количество предыдущих версий (ex. последние десять версий), он также поддерживает изменение-конкатенацию (поэтому все изменения, произошедшие в течение определенного периода, будут покрыты одним пересмотр.)

никлозон / Метеор-коллекция-редакции

другой звуковой вариант - использовать Meteor Vermongo (здесь)


попробуйте использовать Javers. Хорошая библиотека.