Dobre pytanie, sam też się tym zajmowałem.
Utwórz nową wersję po każdej zmianie
Natknąłem się na moduł Versioning sterownika Mongoid dla Rubiego. Sam go nie używałem, ale z tego, co udało mi się znaleźć, dodaje numer wersji do każdego dokumentu. Starsze wersje są osadzone w samym dokumencie. Główną wadą jest to, że cały dokument jest duplikowany przy każdej zmianie , co spowoduje, że w przypadku dużych dokumentów będzie przechowywanych wiele zduplikowanych treści. Takie podejście jest jednak dobre, gdy masz do czynienia z dokumentami o małych rozmiarach i/lub nie aktualizujesz dokumentów zbyt często.
Przechowuj zmiany tylko w nowej wersji
Innym podejściem byłoby przechowywanie tylko zmienionych pól w nowej wersji . Następnie możesz „spłaszczyć” swoją historię, aby zrekonstruować dowolną wersję dokumentu. Jest to jednak dość skomplikowane, ponieważ musisz śledzić zmiany w swoim modelu oraz przechowywać aktualizacje i usunięcia w taki sposób, aby Twoja aplikacja mogła zrekonstruować aktualny dokument. Może to być trudne, ponieważ masz do czynienia z ustrukturyzowanymi dokumentami, a nie płaskimi tabelami SQL.
Zapisz zmiany w dokumencie
Każde pole może mieć również indywidualną historię. W ten sposób odtworzenie dokumentów do danej wersji jest znacznie prostsze. W swojej aplikacji nie musisz jawnie śledzić zmian, ale po prostu utwórz nową wersję właściwości, gdy zmienisz jej wartość. Dokument może wyglądać mniej więcej tak:
{
_id: "4c6b9456f61f000000007ba6"
title: [
{ version: 1, value: "Hello world" },
{ version: 6, value: "Foo" }
],
body: [
{ version: 1, value: "Is this thing on?" },
{ version: 2, value: "What should I write?" },
{ version: 6, value: "This is the new body" }
],
tags: [
{ version: 1, value: [ "test", "trivial" ] },
{ version: 6, value: [ "foo", "test" ] }
],
comments: [
{
author: "joe", // Unversioned field
body: [
{ version: 3, value: "Something cool" }
]
},
{
author: "xxx",
body: [
{ version: 4, value: "Spam" },
{ version: 5, deleted: true }
]
},
{
author: "jim",
body: [
{ version: 7, value: "Not bad" },
{ version: 8, value: "Not bad at all" }
]
}
]
}
Oznaczenie części dokumentu jako usuniętej w wersji jest jednak nadal dość niezręczne. Możesz wprowadzić state
pole dla części, które można usunąć/przywrócić z aplikacji:
{
author: "xxx",
body: [
{ version: 4, value: "Spam" }
],
state: [
{ version: 4, deleted: false },
{ version: 5, deleted: true }
]
}
W każdym z tych podejść można przechowywać aktualną i spłaszczoną wersję w jednym zbiorze, a dane historyczne w osobnym zbiorze. Powinno to skrócić czas wykonywania zapytań, jeśli interesuje Cię tylko najnowsza wersja dokumentu. Ale gdy potrzebujesz zarówno najnowszej wersji, jak i danych historycznych, musisz wykonać dwa zapytania zamiast jednego. Zatem wybór użycia jednej kolekcji zamiast dwóch oddzielnych kolekcji powinien zależeć od jak często Twoja aplikacja potrzebuje wersji historycznych .
Większość z tych odpowiedzi to tylko zrzut moich myśli, właściwie jeszcze tego nie próbowałem. Patrząc wstecz, pierwsza opcja jest prawdopodobnie najłatwiejszym i najlepszym rozwiązaniem, chyba że narzut zduplikowanych danych jest bardzo istotny dla Twojej aplikacji. Druga opcja jest dość złożona i prawdopodobnie nie jest warta wysiłku. Trzecia opcja jest zasadniczo optymalizacją opcji drugiej i powinna być łatwiejsza do wdrożenia, ale prawdopodobnie nie jest warta wysiłku implementacyjnego, chyba że naprawdę nie możesz skorzystać z opcji pierwszej.
Nie możemy się doczekać opinii na temat tego i innych rozwiązań problemu :)