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

298

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

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

  • будет использоваться нечасто
  • будет использоваться сразу, чтобы представить его в стиле «машины времени»
  • не будет больше версий, чем несколько сотен на одну запись. история не закончится.

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

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

    {
     '_id': 'новый идентификатор',
     'user': user_id,
     «отметка времени»: отметка времени,
     'address_book_id': 'идентификатор записи адресной книги' 
     'old_record': {'first_name': 'Jon', 'last_name': 'Doe' ...}
    }
    

    Этот подход может быть изменен для хранения массива версий для каждого документа. Но это, кажется, медленный подход без каких-либо преимуществ.

  • Хранить версии как сериализованный (JSON) объект, прикрепленный к записям адресной книги. Я не уверен, как прикрепить такие объекты к документам MongoDB. Возможно, как массив строк. ( Смоделировано после простого управления версиями документов с помощью CouchDB )

Петр Чапла
источник
1
Я хочу знать, изменилось ли это с момента ответа на вопрос? Я не знаю много об оплоге, но было ли это в то время, будет ли это иметь значение?
Рэнди Л
Мой подход заключается в том, чтобы рассматривать все данные как временные ряды.

Ответы:

152

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

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

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

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

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

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

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

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

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

Гейтс В.П.
источник
2
Неужели вы не переживаете, что такой исторический документ (объект изменений) со временем будет расти, а обновления станут неэффективными? Или MongoDB обрабатывает документ легко?
Петр Чапла
5
Посмотрите на редактирование. Добавить к нему changesдействительно легко: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)это приведет к упадку, который изменит только необходимые данные. Mongo создает документы с «буферным пространством» для обработки этого типа изменений. Он также следит за тем, как изменяются документы в коллекции, и изменяет размер буфера для каждой коллекции. Поэтому MongoDB предназначен именно для этого типа изменений (добавление нового свойства / push в массив).
Гейтс VP
2
Я провел некоторое тестирование, и действительно, резервирование места работает довольно хорошо. Я не смог отследить потерю производительности, когда записи были перераспределены в конец файла данных.
Петр Чапла
4
Вы можете использовать github.com/mirek/node-rus-diff для создания (совместимых с MongoDB) различий для вашей истории.
Мирек Русин
1
JSON Patch RFC предоставляет способ выразить difffs. Имеет реализации на нескольких языках .
Жером
31

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

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

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

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

Мэриан
источник
5
Как вы на самом деле используете это?
hadees
6
Нет никакой документации о том, как этот проект фактически используется. Это то, что как-то живет с Монго? Это библиотека Java? Это просто способ думать о проблеме? Никаких идей и подсказок не дано.
13
1
На самом деле это Java-приложение, и соответствующий код находится здесь: github.com/thiloplanz/v7files/blob/master/src/main/java/v7db/…
ftrotter
20

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

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

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

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

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

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

Это оно!

(помните 16 МБ на лимит документа)

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

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

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

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

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

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

s01ipsist
источник
18
для рубинового языка программирования.
2013 года
9

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

{
  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

Дэниел Уотроус
источник
Потрясающие вещи :)
Джонатан
4

Другой вариант - использовать плагин 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.
Мухаммед Реда
источник
1

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

nicklozon / Метеор-коллекция-пересмотры

Другой вариант звука - использовать Метеор Вермонго ( здесь )

helcode
источник