Способы реализации контроля версий данных в MongoDB

253

Можете ли вы поделиться своими мыслями о том, как реализовать внедрение версий данных в 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)

  • 0
    Я хочу знать, изменилось ли это после ответа на вопрос? Я не знаю много об оплоге, но было ли это в то время, будет ли это иметь значение?
  • 0
    Мой подход заключается в том, чтобы рассматривать все данные как временные ряды.
Теги:
database-versioning

7 ответов

116
Лучший ответ

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

  • Diffs?
  • Всего записей?

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

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

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

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

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

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

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

  • 2
    Неужели вас не беспокоит, что такой исторический документ (объект изменений) со временем будет расти, а обновления станут неэффективными? Или MongoDB обрабатывает документ легко?
  • 5
    Посмотрите на редактирование. db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true) changes очень просто: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true) Это выполнит переход, который изменит только требуемые данные. Mongo создает документы с «буферным пространством» для обработки этого типа изменений. Он также следит за тем, как изменяются документы в коллекции, и изменяет размер буфера для каждой коллекции. Таким образом, MongoDB предназначен именно для этого типа изменений (добавление нового свойства / push в массив).
Показать ещё 14 комментариев
26

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

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

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

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

  • 0
    хорошая находка, спасибо
  • 5
    Как вы на самом деле используете это?
Показать ещё 2 комментария
11

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

{
    _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(
    {
        "_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 МБ за лимит документа)

  • 0
    С хранилищем mmapv1 каждый раз, когда в данные добавляется новая версия, существует вероятность, что документ будет перемещен.
  • 0
    Да это правильно. Но если вы просто добавляете новые версии время от времени, это должно пренебречь.
11

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

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

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

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

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

  • 16
    для языка программирования ruby.
  • 0
    Было бы очень хорошо иметь такую библиотеку для Java!
6

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

{
  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

  • 0
    Крутые вещи :)
0

Другой вариант - использовать 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.
0

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

mongoose-patch-history

Ещё вопросы

Сообщество Overcoder
Наверх
Меню