Способы реализации контроля версий данных в MongoDB - PullRequest
280 голосов
/ 15 ноября 2010

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

Предположим, мне нужно записать записи в простой адресной книге.(Записи в адресной книге хранятся в виде плоских объектов 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 )

Ответы [ 8 ]

142 голосов
/ 16 ноября 2010

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

  1. Различия?
  2. Целые копии записей?

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

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

Чтобы упростить мою жизнь, я бы сделал исторический документ, содержащий словарь времени.штампованные различия.Примерно так:

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

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

ОБНОВЛЕНИЕ: 2015-10

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

30 голосов
/ 27 октября 2012

Существует схема управления версиями под названием «Vermongo», которая затрагивает некоторые аспекты, которые не были рассмотрены в других ответах.

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

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

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

19 голосов
/ 14 марта 2015

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

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

data содержит все версии.Массив data имеет порядок , упорядочен , новые версии получат $push ed только до конца массива.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)

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 МБ на документ)

12 голосов
/ 11 августа 2012

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

Mongoid имеет простое управление версиями

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

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

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

9 голосов
/ 09 июля 2013

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

{
  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

3 голосов
/ 21 июля 2017

Другой вариант - использовать плагин 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.
3 голосов
/ 10 февраля 2017

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

mongoose-patch-history

1 голос
/ 25 декабря 2017

Я использовал приведенный ниже пакет для проекта meteor / MongoDB, и он хорошо работает, главное преимущество в том, что он хранит историю / ревизии в массиве в одном и том же документе, поэтому нет необходимости в дополнительных публикациях или промежуточном программном обеспечении для доступаСменная историю.Он может поддерживать ограниченное количество предыдущих версий (например, последние десять версий), а также поддерживает конкатенацию изменений (поэтому все изменения, произошедшие в течение определенного периода, будут охвачены одной ревизией).

nicklozon/ meteor-collection-revisions

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

...