Gute Frage, ich habe mich auch selbst darum gekümmert.
Erstellen Sie bei jeder Änderung eine neue Version
Ich bin auf das Versionierungsmodul des Mongoid-Treibers für Ruby gestoßen. Ich habe es selbst nicht verwendet, aber nach allem, was ich finden konnte , fügt es jedem Dokument eine Versionsnummer hinzu. Ältere Versionen sind in das Dokument selbst eingebettet. Der Hauptnachteil besteht darin, dass bei jeder Änderung das gesamte Dokument dupliziert wird , was dazu führt, dass beim Umgang mit großen Dokumenten viel doppelter Inhalt gespeichert wird. Dieser Ansatz ist jedoch in Ordnung, wenn Sie mit kleinen Dokumenten arbeiten und / oder Dokumente nicht sehr oft aktualisieren.
Speichern Sie Änderungen nur in einer neuen Version
Ein anderer Ansatz wäre, nur die geänderten Felder in einer neuen Version zu speichern . Anschließend können Sie Ihren Verlauf reduzieren, um eine beliebige Version des Dokuments zu rekonstruieren. Dies ist jedoch recht komplex, da Sie Änderungen in Ihrem Modell verfolgen und Aktualisierungen und Löschvorgänge so speichern müssen, dass Ihre Anwendung das aktuelle Dokument rekonstruieren kann. Dies kann schwierig sein, da Sie sich eher mit strukturierten Dokumenten als mit flachen SQL-Tabellen befassen.
Speichern Sie Änderungen im Dokument
Jedes Feld kann auch einen eigenen Verlauf haben. Das Rekonstruieren von Dokumenten zu einer bestimmten Version ist auf diese Weise viel einfacher. In Ihrer Anwendung müssen Sie Änderungen nicht explizit verfolgen, sondern nur eine neue Version der Eigenschaft erstellen, wenn Sie ihren Wert ändern. Ein Dokument könnte ungefähr so aussehen:
{
_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" }
]
}
]
}
Das Markieren eines Teils des Dokuments als in einer Version gelöscht ist jedoch immer noch etwas umständlich. Sie können ein state
Feld für Teile einfügen, die aus Ihrer Anwendung gelöscht / wiederhergestellt werden können:
{
author: "xxx",
body: [
{ version: 4, value: "Spam" }
],
state: [
{ version: 4, deleted: false },
{ version: 5, deleted: true }
]
}
Mit jedem dieser Ansätze können Sie eine aktuelle und reduzierte Version in einer Sammlung und die Verlaufsdaten in einer separaten Sammlung speichern. Dies sollte die Abfragezeiten verbessern, wenn Sie nur an der neuesten Version eines Dokuments interessiert sind. Wenn Sie jedoch sowohl die neueste Version als auch historische Daten benötigen, müssen Sie statt einer zwei Abfragen durchführen. Die Wahl zwischen einer einzelnen Sammlung und zwei getrennten Sammlungen sollte daher davon abhängen, wie oft Ihre Anwendung die historischen Versionen benötigt .
Der größte Teil dieser Antwort ist nur ein Brain Dump meiner Gedanken, ich habe noch nichts davon ausprobiert. Rückblickend ist die erste Option wahrscheinlich die einfachste und beste Lösung, es sei denn, der Overhead doppelter Daten ist für Ihre Anwendung sehr bedeutend. Die zweite Option ist ziemlich komplex und wahrscheinlich nicht die Mühe wert. Die dritte Option ist im Grunde eine Optimierung von Option zwei und sollte einfacher zu implementieren sein, ist aber wahrscheinlich den Implementierungsaufwand nicht wert, es sei denn, Sie können wirklich nicht mit Option eins gehen.
Ich freue mich auf Feedback zu diesem und den Lösungen anderer Leute für das Problem :)