Wie bearbeite ich den Verlauf eines großen Zeichenkettenfeldes in der relationalen Datenbank?

8

N.B. Ich denke, dass Antworten eher designorientiert und daher im Wesentlichen agnostisch sind, aber ich verwende Java + Hibernate mit Postgres, wenn es eine besonders geeignete Lösung gibt, die diese Technologien verwendet.

Ich habe eine Tabelle mit einem bestimmten Feld, das große Strings enthält, sagen wir Blogposts, die im Durchschnitt +10000 Zeichen umfassen.

In meiner App können Sie Blogposts so oft bearbeiten, wie Sie möchten, und die neueste Version wird immer sofort nach einem Update angezeigt. Allerdings muss die App einen vollständigen Versionsverlauf dieser Änderungen speichern, damit sie angezeigt werden können.

Eine offensichtliche Strategie besteht darin, eine separate Tabelle zu behalten, etwa blog_post_history , in der Blog-Post-Zeilen bei der Erstellung doppelt eingefügt werden, und jede nachfolgende Aktualisierung auf die Tabelle 'live' blog_post mit einer inkrementierenden Versionsnummer. Diese Versionen sind also alle verfügbar, wenn sie in Zukunft benötigt werden. Ich habe erwogen, etwas wie Hibernate Envers zu verwenden, um das einzurichten.

Es scheint jedoch bemerkenswert ineffizient zu sein, mehrere Versionen eines Textblocks mit 10000 Zeichen zu speichern (und - vielleicht noch wichtiger - zu übertragen), bei dem der einzige Unterschied darin besteht, Tippfehler zu korrigieren, ein paar Wörter hinzuzufügen usw. Aufgrund von Bei der Art der Bearbeitung von Blogposts gibt es wahrscheinlich eher kleine kleine Änderungen als kleinere Änderungen.

Gibt es einen besseren Weg?

Ich denke etwas in der Richtung des Speicherns nur Deltas zwischen der aktuellen und vorherigen Version, wenn eine Bearbeitung vorgenommen wird, und dann rekonstruieren die Versionsgeschichte von diesen Deltas programmatisch, wenn es angefordert wird, vielleicht auf dem Client, so dass die Daten gesendet werden Der Draht ist minimiert.

Ich würde höchstwahrscheinlich die neueste Version als Volltext speichern, da ich diese am häufigsten für die Anforderung optimieren möchte, und dann eine Kette von Deltas speichern, die rückwärts von der aktuellen Version rekonstruiert werden historische Versionen, wann und wie sie angefordert werden.

    
davnicwil 16.07.2015, 18:35
quelle

2 Antworten

1

Eine Lösung, an der ich gerade arbeite, die bisher gut funktioniert, implementiert das Design, das ich in der Frage vorgeschlagen habe

  

Ich denke etwas in der Richtung des Speicherns nur Deltas zwischen der aktuellen und vorherigen Version, wenn eine Bearbeitung vorgenommen wird, und dann rekonstruieren die Versionsgeschichte von diesen Deltas programmatisch, wenn es angefordert wird, vielleicht auf dem Client, so dass die Daten gesendet werden Der Draht ist minimiert.

     

Ich würde höchstwahrscheinlich die neueste Version als Volltext speichern, da ich diese für die häufigste Anforderung optimieren möchte, und dann eine Kette von Deltas speichern, die von der aktuellen Version rückwärts gehen, um historische Versionen zu rekonstruieren sind gefragt.

Ich teile hier die Einzelheiten meiner Implementierung

Zum Erstellen von Deltas und zum Rekonstruieren des Volltexts verwende ich das fantastische google-diff -match-patch-Bibliothek . Sie können die agnostische API-Dokumentation lesen, um die folgenden Codebeispiele besser zu verstehen. obwohl es sowieso gut lesbar ist.

google-diff-match-patch hat Java- und JS-Implementierungen, damit ich es verwenden kann, um die Deltas mit Java auf dem Server zu berechnen. Ich entschied mich, jedes Delta in einen String zu konvertieren, so dass es leicht in der Datenbank gespeichert werden kann und leicht von der JS-Bibliothek auf dem Client genutzt werden kann. Mehr dazu unten.

%Vor%

NB. etwas, was mich eine Weile brauchte, um herauszufinden, wie man den offiziellen Build von google-diff-match-patch mit maven herunterzieht. Es ist nicht in der maven central Repo, sondern in ihrem eigenen Repo auf googlecode.com. Nur um es zu erwähnen, einige Leute haben es gegabelt und ihre gegabelten Versionen in maven central platziert, aber wenn du die offizielle Version wirklich willst, kannst du das Repo und die Abhängigkeit in deinem pom.xml wie folgt hinzufügen

%Vor%

Für das Frontend übergebe ich den letzten Blog-Post-Volltext, zusammen mit einer Kette von Deltas, die zeitlich rückwärts gehen und jede Bearbeitung darstellen, und rekonstruiere dann den vollständigen Text jeder Version im Browser in JS.

Um die Bibliothek zu bekommen, benutze ich npm + browserify. Die Bibliothek ist auf npm verfügbar als diff-match-patch . Version 1.0.0 ist die einzige Version.

%Vor%

Und das ist es, es funktioniert fantastisch.

Um die Änderungen der Blog-Posts zu speichern, verwende ich einfach eine Tabelle BLOG_POST_EDITS , in der ich die Blog-Post-ID ablege, einen Zeitstempel der Bearbeitungszeit (mit der ich später die Bearbeitungen korrekt ordne die Kette beim Rekonstruieren der Volltextversionen auf dem Client) und das Rückwärtsdelta zwischen dem aktuellen Live-Blogpost in der BLOG_POST -Tabelle und der eingehenden bearbeiteten Version des Blogposts.

Ich habe mich dafür entschieden, eine "Kette" von Deltas zu speichern, weil sie gut zu meinem Anwendungsfall passt, und ist einfacher am Ende des Servercodes. Es bedeutet, um die Version M von N zu rekonstruieren, muss ich dem Kunden eine Kette von N- (M-1) Deltas vom Live-Blogpost-Volltext an die Version M zurücksenden. Aber in meinem Anwendungsfall passiere ich Ich möchte trotzdem jedes Mal die ganze Kette schicken, also ist das in Ordnung.

Für eine etwas bessere Over-the-Wire-Effizienz beim Anfordern bestimmter Versionen könnten alle Deltas von der neu bearbeiteten Blogpost-Version zu jeder (wiederhergestellten) Version jedes Mal neu berechnet werden, wenn eine Bearbeitung vorgenommen wird, aber dies würde mehr Arbeit bedeuten und Komplexität auf dem Server.

    
davnicwil 18.07.2015 02:00
quelle
1

Ich antworte nicht über das Speichern von Diff oder vollständigen Änderungen, auch wenn meiner Meinung nach nur ein Leistungstest tatsächlich antworten kann, welche Lösung besser ist, weil vollständiges Protokoll des Inhalts größere Datenbank bedeutet, aber weniger Arbeit für Server.

Ich möchte im Gegenteil teilen, meine Erfahrung für die Geschichte mit postgresql. Ich habe es sehr erfolgreich auf der Server-Website, nur auf PostgreSQL arbeiten, ohne Code zu schreiben. Mit diesem Satz von Funktionen, Triggern und Erweiterungen auf PostgreSQL

Ссылка

Sie sind einfach und leicht zu implementieren, und Sie können den Verlauf Ihres Codes vergessen, aber Sie können nur aus der Protokolltabelle lesen, um Unterschiede im Inhalt darzustellen.

Also meine Anwendung wurde in PHP mit YII-Framework mit DB-Schemata und Struktur von mir für Daten, mit nur wenigen Tabelle als Service für Framework selbst geschrieben (Benutzer, Rollen und allgemeine Protokoll) und das ist wichtig, weil wenn die Datenstruktur in der db ist zu kompliziert die unten zusammengefasste Vorgehensweise ist immer noch gültig, aber komplizierter.

Nach der Installation der Postgresql-Erweiterung Tabellog finden Sie hier Ссылка

Sie können folgendermaßen vorgehen: Zuerst müssen Sie die Tabelle (Mytabelle) mit dem Inhalt auswählen, den Sie für die Historie behalten möchten. Duplizierst diese mytable (ich habe es in ein neues Schema log.mytable getan), indem du einige neue Spalten hinzufügst, um den Verlauf zu verfolgen (wie in README in tablogog extension beschrieben).

Sie müssen einige einfache Funktionen für postgresl in pgplsql

erstellen
%Vor%

Jetzt müssen Sie einen Trigger für Ihre MYTABLE als

erstellen
  

ERSTELLEN SIE TRIGGER MYTABLE_TRG NACH UPDATE ODER EINFÜGEN ODER LÖSCHEN ON MYTABLE   FÜR JEDE ZEILE AUSFÜHREN SIE PROCEDURE table_log ('log.mytable');

Das ist alles. Bei jedem INSERT, UPDATE oder DELETE behalten Sie den Verlauf und Sie können alte Versionen einfach mit der Funktion, die Sie zuvor erstellt haben, wiederherstellen und so den App-Code ausführen und SQL die Funktion aufrufen.

In meiner App habe ich an mehreren Stellen ein Symbol für den Verlauf hinzugefügt, und mit einem Klick öffne ich einen Dialog mit Formular und Optionen in der Tabelle, um den gesamten Verlauf anzuzeigen und die Version auszuwählen, die Sie wiederherstellen können.

Wenn Sie in der Formularerstellung das Inhaltsformular log.mytable auswählen, könnten Sie meiner Meinung nach eine Funktion hinzufügen, die den Unterschied von allen Versionen mit dem aktuellen extrahiert, aber es ist einfach, wenn Sie den vollständigen Inhalt für jede Version in der Datenbank speichern weil es im Gegenteil schwierig sein könnte, eine Version nahe der letzten wiederherzustellen. In der Tat, wenn Sie Unterschiede halten, beachten Sie, dass sie mit dem nächsten nicht mit dem Strom verglichen werden.

Ein weiterer Vorteil ist, dass alles serverseitig ist und keine Verzögerung für das Schreiben zusätzlicher Daten auf der Client-Seite wahrgenommen werden kann.

Die Funktion zum Präsentieren nur des unten genannten Unterschieds könnte auch eine pgplsql-Funktion sein, um auf diese Weise zu vermeiden, alle Versionen in vollem Inhalt an den Client zu senden, die manchmal sehr groß sein können, aber dies hängt vom Inhaltstyp ab weniger für HTML und komplexer für andere Arten von Inhalten.

Meine App war ziemlich komplex, aber die Historie für Änderungen auf diese Weise zu behalten ist einfach und sauber und ich habe es nach getan vergessen, weil es immer reibungslos funktionierte.

Luca

    
Luca Marletta 17.11.2015 13:07
quelle