Sposoby wdrażania wersjonowania danych w MongoDB

298

Czy możesz podzielić się swoimi przemyśleniami, jak zaimplementować przechowywanie wersji danych w MongoDB. (Zadałem podobne pytanie dotyczące Cassandry . Jeśli masz jakieś przemyślenia, która db jest lepsza, proszę podziel się)

Załóżmy, że muszę zaktualizować rekordy w prostej książce adresowej. (Rekordy książki adresowej są przechowywane jako płaskie obiekty Json). Oczekuję, że historia:

  • będą używane rzadko
  • zostaną wykorzystane naraz, aby przedstawić go w sposób „maszyny czasu”
  • na jeden rekord nie będzie więcej niż kilkaset wersji. historia nie wygasa.

Rozważam następujące podejścia:

  • Utwórz nową kolekcję obiektów do przechowywania historii rekordów lub zmian w rekordach. Będzie przechowywać jeden obiekt na wersję z odniesieniem do pozycji książki adresowej. Takie zapisy wyglądałyby następująco:

    {
     „_id”: „nowy identyfikator”,
     „użytkownik”: identyfikator_użytkownika,
     „timestamp”: timestamp,
     „address_book_id”: „identyfikator rekordu książki adresowej” 
     „old_record”: {„first_name”: „Jon”, „last_name”: „Doe” ...}
    }
    

    To podejście można zmodyfikować, aby przechowywać tablicę wersji na dokument. Ale wydaje się, że jest to wolniejsze podejście bez żadnych korzyści.

  • Przechowuj wersje jako obiekt serializowany (JSON) dołączony do pozycji książki adresowej. Nie jestem pewien, jak dołączyć takie obiekty do dokumentów MongoDB. Być może jako tablica ciągów. ( Modelowane na podstawie prostej wersji dokumentów za pomocą CouchDB )

Piotr Czapla
źródło
1
Chcę wiedzieć, czy zmieniło się to od czasu odpowiedzi na pytanie? Nie wiem wiele o oplogu, ale czy było to wtedy, czy to coś zmieni?
Randy L
Moje podejście polega na traktowaniu wszystkich danych jako szeregów czasowych.

Odpowiedzi:

152

Pierwszym ważnym pytaniem podczas nurkowania jest „jak chcesz przechowywać zestawy zmian” ?

  1. Różnice?
  2. Całe kopie płyt?

Moim osobistym podejściem byłoby przechowywanie różnic. Ponieważ wyświetlanie tych różnic jest naprawdę specjalną akcją, umieściłbym różnice w innej kolekcji „historii”.

Chciałbym użyć innej kolekcji, aby zaoszczędzić miejsce w pamięci. Zwykle nie potrzebujesz pełnej historii dla prostego zapytania. Tak więc, utrzymując historię poza obiektem, możesz również trzymać ją poza pamięcią, do której często uzyskiwany jest dostęp, gdy dane są sprawdzane.

Aby ułatwić moje życie, stworzyłbym dokument historyczny zawierający słownik różnic znaczników czasu. Coś takiego:

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

Aby moje życie było naprawdę łatwe, uczyniłbym tę część moich obiektów DataObject (EntityWrapper, cokolwiek), których używam do uzyskiwania dostępu do moich danych. Zasadniczo obiekty te mają pewną formę historii, dzięki czemu można łatwo zastąpić save()metodę, aby wprowadzić tę zmianę w tym samym czasie.

AKTUALIZACJA: 2015-10

Wygląda na to, że istnieje teraz specyfikacja do obsługi różnic JSON . To wydaje się być bardziej niezawodnym sposobem przechowywania różnic / zmian.

Gates VP
źródło
2
Czy nie martwiłbyś się, że taki dokument historii (obiekt zmian) będzie z czasem rósł, a aktualizacje nie będą wydajne? Czy też obsługa dokumentów MongoDB łatwo rośnie?
Piotr Czapla
5
Spójrz na edycję. Dodawanie do changesjest naprawdę łatwe: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)wykona wstawkę, która zmieni tylko wymagane dane. Mongo tworzy dokumenty z „przestrzenią buforową” do obsługi tego rodzaju zmian. Obserwuje również, jak zmieniają się dokumenty w kolekcji i modyfikuje rozmiar bufora dla każdej kolekcji. Tak więc MongoDB jest przeznaczony do dokładnie tego rodzaju zmian (dodaj nową właściwość / push do tablicy).
Gates VP
2
Przeprowadziłem kilka testów i rzeczywiście rezerwacja miejsca działa całkiem dobrze. Nie byłem w stanie uchwycić utraty wydajności, gdy rekordy zostały przeniesione na koniec pliku danych.
Piotr Czapla
4
Możesz użyć github.com/mirek/node-rus-diff do wygenerowania różnic (zgodnych z MongoDB) dla twojej historii.
Mirek Rusin,
1
JSON patch RFC to sposób wyrażania difffs. Ma implementacje w kilku językach .
Jérôme,
31

Istnieje schemat kontroli wersji o nazwie „Vermongo”, który dotyczy niektórych aspektów, które nie zostały omówione w innych odpowiedziach.

Jednym z tych problemów są jednoczesne aktualizacje, drugim jest usuwanie dokumentów.

Vermongo przechowuje kompletne kopie dokumentów w kolekcji cieni. W niektórych przypadkach może to powodować zbyt duże obciążenie, ale myślę, że upraszcza to również wiele rzeczy.

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

Marian
źródło
5
Jak go właściwie wykorzystujesz?
hadees
6
Nie ma dokumentacji dotyczącej tego, jak ten projekt jest faktycznie wykorzystywany. Czy to jest coś, co w jakiś sposób żyje z Mongo? To jest biblioteka Java? Czy to tylko sposób myślenia o problemie? Nie podano żadnego pomysłu i żadnych wskazówek.
ftrotter
1
To jest właściwie aplikacja Java, a kod odradzania znajduje się
ftrotter
20

Oto inne rozwiązanie wykorzystujące jeden dokument dla bieżącej wersji i wszystkich starych wersji:

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

datazawiera wszystkie wersje. dataTablica jest uporządkowane , nowe wersje dostanie tylko $pushed do końca tablicy. data.vidto identyfikator wersji, który jest liczbą rosnącą.

Pobierz najnowszą wersję:

find(
    { "_id":ObjectId("...") },
    { "data":{ $slice:-1 } }
)

Uzyskaj konkretną wersję przez vid:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } } }
)

Zwróć tylko określone pola:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)

Wstaw nową wersję: (i zapobiegaj jednoczesnemu wstawianiu / aktualizacji)

update(
    {
        "_id":ObjectId("..."),
        $and:[
            { "data.vid":{ $not:{ $gt:2 } } },
            { "data.vid":2 }
        ]
    },
    { $push:{ "data":{ "vid":3, "content":"baz" } } }
)

2jest vidbieżącą najnowszą wersją i 3jest wstawiana nowa wersja. Bo trzeba najnowsza wersja użytkownika vid, jest to łatwe do zrobienia dostać kolejna wersja na vid: nextVID = oldVID + 1.

$andWarunek zapewni, że 2jest ostatni vid.

W ten sposób nie ma potrzeby tworzenia unikalnego indeksu, ale logika aplikacji musi zadbać o zwiększenie vidwkładki.

Usuń określoną wersję:

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

Otóż ​​to!

(pamiętaj limit 16 MB na dokument)

Benjamin M.
źródło
Dzięki pamięci mmapv1 za każdym razem, gdy do danych dodawana jest nowa wersja, istnieje możliwość przeniesienia dokumentu.
raok1997
Tak to prawda. Ale jeśli od czasu do czasu dodajesz nowe wersje, powinno to być pomijalne.
Benjamin M.
9

Pracowałem nad tym rozwiązaniem, które obejmuje opublikowane, szkice i historyczne wersje danych:

{
  published: {},
  draft: {},
  history: {
    "1" : {
      metadata: <value>,
      document: {}
    },
    ...
  }
}

Wyjaśnię ten model tutaj: http://software.danielwatrous.com/representing-revision-data-in-mongodb/

Dla tych, którzy mogą implementować coś takiego w Javie , oto przykład:

http://software.danielwatrous.com/using-java-to-work-with-versoted-data/

W tym cały kod, który możesz rozwidlić, jeśli chcesz

https://github.com/dwatrous/mongodb-revision-objects

Daniel Watrous
źródło
Niesamowite rzeczy :)
Jonathan
4

Jeśli używasz mangusty, znalazłem następującą wtyczkę, która jest przydatną implementacją formatu łatki JSON

mongoose-patch-history

bmw15
źródło
4

Inną opcją jest użycie wtyczki 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.
Muhammad Reda
źródło
1

Użyłem poniższego pakietu do projektu Mete / MongoDB i działa dobrze, główną zaletą jest to, że przechowuje historię / zmiany w tablicy w tym samym dokumencie, dlatego nie ma potrzeby dodatkowych publikacji lub oprogramowania pośredniego, aby uzyskać dostęp do historii zmian . Może obsługiwać ograniczoną liczbę poprzednich wersji (np. Ostatnich dziesięciu wersji), obsługuje także konkatenację zmian (więc wszystkie zmiany dokonane w określonym okresie zostaną objęte jedną wersją).

wersje nicklozon / meteor-collection-rewizje

Inną opcją dźwiękową jest użycie Meteor Vermongo ( tutaj )

kod dostępu
źródło