Способи реалізації версій даних у MongoDB


298

Чи можете ви поділитися своїми думками, як би ви впровадили версію даних у MongoDB. (Я задавав подібне запитання щодо Кассандри . Якщо у вас є якісь думки, який db краще для цього, будь ласка, поділіться)

Припустимо, мені потрібно версії записів у простій адресній книзі. (Записи адресних книг зберігаються як плоскі об'єкти json). Я очікую, що історія:

  • буде використовуватися нечасто
  • будуть використані всі одразу, щоб представити це у "машині часу"
  • не буде більше версій, ніж кілька сотень до одного запису. історія не закінчиться

Я розглядаю такі підходи:

  • Створіть нову колекцію об’єктів для зберігання історії записів або змін у записах. Він зберігатиме один об’єкт у кожній версії із посиланням на запис у адресної книги. Такі записи виглядають так:

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

    Цей підхід можна змінити для зберігання масиву версій на документ. Але це здається повільнішим підходом без будь-яких переваг.

  • Зберігайте версії як серіалізований (JSON) об’єкт, приєднаний до записів адресної книги. Я не впевнений, як приєднати такі об’єкти до документів MongoDB. Можливо, як масив струн. ( За зразком після простої версії документа з CouchDB )


1
Хочу знати, чи змінилося це з моменту відповіді на запитання? Я не знаю багато про Oplog, але це було б у той час, чи мало би це значення?
Ренді Л

Мій підхід полягає в тому, щоб вважати всі дані як часовий ряд.

Відповіді:


152

Перше велике питання, коли занурюєтесь у це, - це "як ви хочете зберігати набори змін" ?

  1. Відмінності?
  2. Цілі копії записів?

Мій особистий підхід полягає в тому, щоб зберігати різні. Оскільки показ цих різниць - це дійсно особлива дія, я б поставив 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 . Це здається більш надійним способом зберігання відмінностей / змін.


2
Чи не хвилюєтесь ви, що такий документ історії (об’єкт змін) зростатиме з часом, а оновлення стануть неефективними? Або документ, що обробляє документ MongoDB, легко росте?
Пьотр Чапла

5
Погляньте на правки. Додавання changesдійсно просте: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)це дозволить виконати оновлення, яке змінить лише необхідні дані. Монго створює документи з "буферним простором" для обробки такого типу змін. Він також спостерігає, як документи в колекції змінюються та змінюють розмір буфера для кожної колекції. Отже, MongoDB розроблений саме для такого типу змін (додайте нове властивість / push до масиву).
Гейтс VP

2
Я провів тестування, і дійсно бронювання простору працює досить добре. Я не зміг зрозуміти втрати продуктивності, коли записи були перерозподілені до кінця файлу даних.
Пьотр Чапла

4
Ви можете використовувати github.com/mirek/node-rus-diff для генерації (сумісний з MongoDB) різниці для вашої історії.
Мірек Русін

1
JSON Patch RFC надає спосіб висловити difffs. Він має реалізацію на декількох мовах .
Jérôme

31

Існує схема версій під назвою "Вермонго", яка стосується деяких аспектів, які не були розроблені в інших відповідях.

Одне з цих питань - це одночасні оновлення, інше - видалення документів.

Vermongo зберігає повні копії документів у тіньовій колекції. У деяких випадках використання це може спричинити занадто великі накладні витрати, але я думаю, що це також спрощує багато речей.

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


5
Як ти насправді ним користуєшся?
hadees

6
Немає документації про те, як реально використовується цей проект. Це щось, що живе разом із Монго? Це бібліотека Java? Це просто спосіб мислення щодо проблеми? Ніякої ідеї та підказки не дається.
ftrotter

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 } } }
)

Це воно!

(пам’ятайте про обмеження 16MB на документ)


З накопичувачем mmapv1 щоразу, коли до даних додається нова версія, є можливість переміщення документа.
raok1997

Так, правильно. Але якщо ви просто раз додаєте нові версії, це слід знехтувати.
Бенджамін М

12

Якщо ви шукаєте готове до впровадження рішення -

Монгоїд створив просту версію

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

mongoid-history - це плагін Ruby, який забезпечує значно складніше рішення з аудитом, скасуванням та повторним використанням

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


18
для мови програмування рубіну.
ftrotter

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

Іншим варіантом є використання плагіну історії мангусти .

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 / meteor-collection-reitions

Ще один звуковий варіант - використовувати Meteor Vermongo ( тут )

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.