PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

So führen Sie den Bearbeitungsverlauf eines großen Zeichenfolgenfelds in einer relationalen Datenbank

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

Ich werde die Einzelheiten meiner Implementierung hier teilen

Um Deltas zu erstellen und den Volltext zu rekonstruieren, verwende ich den fantastischen google-diff-match-patch-Bibliothek . Sie können die implementierungsunabhängige API-Dokumentation lesen um die Codebeispiele unten besser zu verstehen, obwohl es sowieso ziemlich lesbar ist.

google-diff-match-patch hat Java- und JS-Implementierungen, sodass ich damit die Deltas mit Java auf dem Server berechnen kann. Ich habe mich dafür entschieden, jedes Delta in einen String zu konvertieren, damit es einfach in der Datenbank gespeichert und von der JS-Bibliothek auf dem Client problemlos verwendet werden kann. Mehr dazu weiter unten.

public String getBackwardsDelta(String editedBlogPost, String existingBlogPost) {
    diff_match_patch dmp = new diff_match_patch();
    LinkedList<diff_match_patch.Patch> patches = 
        dmp.patch_make(editedBlogPost, existingBlogPost);
    return dmp.patch_toText(patches);
}

NB. Ich habe eine Weile gebraucht, um herauszufinden, wie ich den offiziellen Build von google-diff-match-patch herunterladen kann mit Maven. Es befindet sich nicht im zentralen Repo von Maven, sondern in einem eigenen Repo auf googlecode.com. Nur um anzumerken, einige Leute haben es gegabelt und ihre gegabelten Versionen in Maven Central abgelegt, aber wenn Sie wirklich die offizielle Version wollen, können Sie das Repo und die Abhängigkeit in Ihrer pom.xml hinzufügen wie folgt

<repository>
  <id>google-diff-patch-match</id>
  <name>google-diff-patch-match</name>
  <url>https://google-diff-match-patch.googlecode.com/svn/trunk/maven/</url>
</repository>

<dependency>
  <groupId>diff_match_patch</groupId>
  <artifactId>diff_match_patch</artifactId>
  <version>current</version>
</dependency>

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

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

getTextFromDelta: function(originalText, delta) {
  var DMP = require('diff-match-patch'); // get the constructor function
  var dmp = new DMP();
  var patches = dmp.patch_fromText(delta);
  return dmp.patch_apply(patches, originalText)[0];
}

Und das war's, es funktioniert fantastisch.

Zum Speichern der Änderungen der Blogbeiträge verwende ich einfach eine Tabelle BLOG_POST_EDITS wo ich die Blog-Post-ID speichere, einen Zeitstempel, wann die Bearbeitung vorgenommen wurde (den ich später verwende, um die Bearbeitungen korrekt anzuordnen, um die Kette zu erstellen, wenn die Volltextversionen auf dem Client rekonstruiert werden) und das Rückwärts-Delta zwischen dem aktuellen Live Blogpost im BLOG_POST Tabelle und die eingehende bearbeitete Version des Blogbeitrags.

Ich habe mich dafür entschieden, eine „Kette“ von Deltas zu speichern, weil sie gut zu meinem Anwendungsfall passt und am Ende des Servercodes einfacher ist. Es bedeutet, dass ich, um Version M von N zu rekonstruieren, dem Client eine Kette von N-(M-1)-Deltas vom Live-Blog-Post-Volltext an Version M zurücksenden muss. Aber in meinem Anwendungsfall muss ich das tun Ich möchte sowieso jedes Mal die ganze Kette senden, 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 zurück zu jeder (wiederhergestellten) Version jedes Mal neu berechnet werden, wenn eine Bearbeitung vorgenommen wird, aber dies würde mehr Arbeit und Komplexität bedeuten Server.