Verwendung der Volltextsuche in PostgreSQL unter Ubuntu 16.04

Einführung

Volltextsuche (FTS) ist eine Technik, die von Suchmaschinen verwendet wird, um Ergebnisse in einer Datenbank zu finden. Es kann verwendet werden, um Suchergebnisse auf Websites wie Shops, Suchmaschinen, Zeitungen und mehr zu veröffentlichen.

Insbesondere ruft FTS Dokumente ab, bei denen es sich um Datenbankentitäten handelt, die Textdaten enthalten, die den Suchkriterien nicht perfekt entsprechen. Dies bedeutet, dass bei der Suche nach "Katzen und Hunden" durch eine von FTS unterstützte Anwendung Ergebnisse zurückgegeben werden können, die die Wörter getrennt enthalten (nur "Katzen" oder "Hunde") und die Wörter in einer anderen Reihenfolge enthalten ("Hunde und Katzen") oder enthalten Varianten der Wörter ("Katze" oder "Hund"). Dies gibt Anwendungen einen Vorteil darin, zu erraten, was der Benutzer meint, und schneller relevantere Ergebnisse zurückzugeben.

Technisch gesehen ermöglichen Datenbankmanagementsysteme (DBMS) wie PostgreSQL normalerweise teilweise Textsuchen mit LIKE-Klauseln. Diese Anforderungen sind jedoch bei großen Datenmengen tendenziell unzureichend. Sie beschränken sich auch darauf, die genauen Eingaben des Benutzers abzugleichen. Dies bedeutet, dass eine Abfrage möglicherweise keine Ergebnisse liefert, selbst wenn Dokumente mit relevanten Informationen vorhanden sind.

Mit FTS können Sie eine leistungsfähigere Textsuchmaschine erstellen, ohne zusätzliche Abhängigkeiten von erweiterten Tools einführen zu müssen. In diesem Lernprogramm verwenden wir PostgreSQL, um Daten zu speichern, die Artikel für eine hypothetische Nachrichtenwebsite enthalten. Anschließend erfahren Sie, wie Sie die Datenbank mit FTS abfragen und nur die besten Übereinstimmungen auswählen. Als letzten Schritt werden wir einige Leistungsverbesserungen für Volltextsuchanfragen implementieren.

Voraussetzungen

Bevor Sie mit diesem Handbuch beginnen, benötigen Sie Folgendes:

Wenn Sie einen PostgreSQL-Server einrichten, ohne das obige Tutorial zu befolgen, stellen Sie sicher, dass Sie das Paket "+ postgresql-contrib " mit " sudo apt-get list postgresql-contrib +" verwenden.

Schritt 1 - Beispieldaten erstellen

Zunächst benötigen wir einige Daten, um das Volltextsuch-Plugin zu testen. Erstellen wir also einige Beispieldaten. Wenn Sie bereits eine eigene Tabelle mit Textwerten haben, können Sie mit Schritt 2 fortfahren und die entsprechenden Ersetzungen vornehmen, während Sie den Anweisungen folgen.

Andernfalls besteht der erste Schritt darin, von seinem Server aus eine Verbindung zur PostgreSQL-Datenbank herzustellen. Da Sie eine Verbindung über denselben Host herstellen, müssen Sie standardmäßig kein Kennwort eingeben.

sudo -u postgres psql sammy

Dadurch wird eine interaktive PostgreSQL-Sitzung eingerichtet, die den Namen der Datenbank angibt, mit der Sie arbeiten. In unserem Fall ist dies "+". Sie sollten eine Eingabeaufforderung ` sammy = # +` für die Datenbank sehen.

Als nächstes erstellen Sie eine Beispieltabelle in der Datenbank mit dem Namen "+ news +". Jeder Eintrag in dieser Tabelle repräsentiert einen Nachrichtenartikel mit einem Titel, etwas Inhalt und dem Namen des Autors sowie einer eindeutigen Kennung.

CREATE TABLE news (
  id SERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  author TEXT NOT NULL
);

"+ id " ist der Primärindex der Tabelle mit dem speziellen Typ " SERIAL +", der einen automatischen Inkrementierungszähler für die Tabelle erstellt. Dies ist eine eindeutige Kennung, die automatisch zum Datenbankindex wechselt. In Schritt 3 werden wir mehr über diesen Index sprechen, wenn wir uns Leistungsverbesserungen ansehen.

Als nächstes fügen Sie der Tabelle einige Beispieldaten mit dem Befehl + INSERT + hinzu. Diese Beispieldaten im folgenden Befehl repräsentieren einige Beispielnachrichtenartikel.

INSERT INTO news (id, title, content, author) VALUES
   (1, 'Pacific Northwest high-speed rail line', 'Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.', 'Greg'),
   (2, 'Hitting the beach was voted the best part of life in the region', 'Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.', 'Ethan'),
   (3, 'Machine Learning from scratch', 'Bare bones implementations of some of the foundational models and algorithms.', 'Jo');

Nachdem die Datenbank einige Daten enthält, nach denen gesucht werden muss, können wir versuchen, einige Abfragen zu schreiben.

Schritt 2 - Dokumente vorbereiten und durchsuchen

Der erste Schritt besteht darin, ein Dokument mit mehreren Textspalten aus der Datenbanktabelle zu erstellen. Anschließend können wir die resultierende Zeichenfolge in einen Vektor von Wörtern umwandeln, den wir in den Abfragen verwenden werden.

Zuerst müssen wir alle Spalten mit der PostgreSQL-Verkettungsfunktion "+ || " und der Transformationsfunktion " to_tsvector () +" zusammenfügen.

SELECT title || '. ' || content as document, to_tsvector(title || '. ' || content) as metadata FROM news WHERE id = 1;

Dies gibt den ersten Datensatz als ganzes Dokument sowie dessen transformierte Version zurück, die für die Suche verwendet werden soll.

Output-[ RECORD 1 ]-----------------------------------------------------
document    | Pacific Northwest high-speed rail line. Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

metadata    | '140':18 'current':8 'high':4 'high-spe':3 'ideal':29 'line':7 'mile':19 'none':25 'northwest':2 'option':14 'pacif':1 'rail':6 'seattl':21 'speed':5 'travel':16 'vancouv':23

Möglicherweise stellen Sie fest, dass die transformierte Version weniger Wörter enthält: "+ Metadaten " in der obigen Ausgabe als das ursprüngliche " Dokument ". Einige der Wörter sind unterschiedlich und an jedes Wort sind ein Semikolon und eine Nummer angehängt. Dies liegt daran, dass die Funktion ` to_tsvector () ` jedes Wort normalisiert, damit wir abweichende Formen desselben Wortes finden und das Ergebnis dann alphabetisch sortieren können. Die Nummer ist die Position der Welt im "" - Dokument. Es kann zusätzliche durch Kommas getrennte Positionen geben, wenn das normalisierte Wort mehr als einmal vorkommt.

Jetzt können wir dieses konvertierte Dokument verwenden, um die FTS-Funktionen zu nutzen, indem wir nach dem Begriff „Explorations“ suchen.

SELECT * FROM news WHERE to_tsvector(title || '. ' || content) @@ to_tsquery('Explorations');

Sehen wir uns die Funktionen und Operatoren an, die wir hier verwendet haben.

Die Funktion + to_tsquery () + übersetzt den Parameter, der eine direkte oder leicht angepasste Benutzersuche sein kann, in ein Textsuchkriterium, das die Eingabe auf die gleiche Weise reduziert wie + to_tsvector () +. Darüber hinaus können Sie mit dieser Funktion festlegen, welche Sprache verwendet werden soll und ob alle Wörter im Ergebnis enthalten sein müssen oder nur eines davon.

Der Operator "+ @@ " gibt an, ob der " tsvector " mit dem " tsquery " oder einem anderen " tsvector " übereinstimmt. Es gibt " true" oder "+ false" zurück, was die Verwendung als Teil der "+ WHERE" -Kriterien vereinfacht.

Output-[ RECORD 1 ]-----------------------------------------------------
id      | 2
title   | Hitting the beach was voted the best part of life in the region
content | Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.
author  | Ethan

Die Abfrage gab das Dokument zurück, das das Wort "Exploring" enthielt, obwohl das für die Suche verwendete Wort "Explorations" war. Die Verwendung eines "+ LIKE +" - Operators anstelle von "FTS" hätte hier zu einem leeren Ergebnis geführt.

Nachdem wir nun wissen, wie Sie Dokumente für FTS vorbereiten und Abfragen strukturieren, wollen wir die Leistung von FTS verbessern.

Schritt 3 - Verbesserung der FTS-Leistung

Das Generieren eines Dokuments bei jeder Verwendung einer FTS-Abfrage kann zu einem Leistungsproblem werden, wenn große Datasets oder kleinere Server verwendet werden. Eine gute Lösung hierfür, die wir hier implementieren, besteht darin, das transformierte Dokument beim Einfügen der Zeile zu generieren und zusammen mit den anderen Daten zu speichern. Auf diese Weise können wir es einfach mit einer Abfrage abrufen, anstatt es jedes Mal generieren zu müssen.

Erstellen Sie zunächst eine zusätzliche Spalte mit dem Namen "+" für die vorhandene Tabelle " news +".

ALTER TABLE news ADD "document" tsvector;

Wir müssen jetzt eine andere Abfrage verwenden, um Daten in die Tabelle einzufügen. Im Gegensatz zu Schritt 2 müssen wir hier auch das transformierte Dokument vorbereiten und wie folgt in die neue Spalte "+ document +" einfügen:

INSERT INTO news (id, title, content, author, document)
VALUES (4, 'Sleep deprivation curing depression', 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.', 'Patel', to_tsvector('Sleep deprivation curing depression' || '. ' || 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.'));

Wenn Sie der vorhandenen Tabelle eine neue Spalte hinzufügen, müssen Sie zunächst leere Werte für die Spalte "+ document +" hinzufügen. Jetzt müssen wir es mit den generierten Werten aktualisieren.

Verwenden Sie den Befehl "+ UPDATE", um die fehlenden Daten hinzuzufügen.

UPDATE news SET document = to_tsvector(title || '. ' || content) WHERE document IS NULL;

Das Hinzufügen dieser Zeilen zu unserer Tabelle ist eine gute Leistungsverbesserung. In großen Datasets können jedoch weiterhin Probleme auftreten, da die Datenbank weiterhin die gesamte Tabelle durchsuchen muss, um die Zeilen zu finden, die den Suchkriterien entsprechen. Eine einfache Lösung hierfür ist die Verwendung von Indizes.

Der Datenbankindex ist eine Datenstruktur, in der Daten getrennt von den Hauptdaten gespeichert werden, wodurch die Leistung von Datenabrufvorgängen verbessert wird. Es wird nach Änderungen des Tabelleninhalts auf Kosten zusätzlicher Schreibvorgänge und vergleichsweise wenig Speicherplatz aktualisiert. Aufgrund der geringen Größe und der angepassten Datenstruktur können Indizes wesentlich effektiver ausgeführt werden als der Haupttabellenbereich zum Auswählen von Abfragen.

Letztendlich helfen Indizes der Datenbank beim schnelleren Auffinden von Zeilen, indem sie mithilfe spezieller Datenstrukturen und Algorithmen suchen. PostgreSQL verfügt über mehrere Indextypen, die für bestimmte Abfragetypen geeignet sind. Die relevantesten für diesen Anwendungsfall sind GiST-Indizes und GIN-Indizes. Der Hauptunterschied zwischen ihnen besteht darin, wie schnell sie Dokumente aus der Tabelle abrufen können. Die Erstellung von GIN ist beim Hinzufügen neuer Daten langsamer, die Abfrage jedoch schneller. GIST wird schneller erstellt, erfordert jedoch zusätzliche Datenlesevorgänge.

Da das Abrufen von Daten mit GiST etwa dreimal langsamer ist als mit GIN, erstellen wir hier einen GIN-Index.

CREATE INDEX idx_fts_search ON news USING gin(document);

Unter Verwendung der indizierten "+ document" -Spalte wurde auch eine "+ SELECT" -Abfrage etwas einfacher.

SELECT title, content FROM news WHERE document @@ to_tsquery('Travel | Cure');

Die Ausgabe sieht folgendermaßen aus:

Output-[ RECORD 1 ]-----------------------------------------------------
title   | Sleep deprivation curing depression
content | Clinicians have long known that there is a strong link between sleep, sunlight and mood.
-[ RECORD 2 ]-----------------------------------------------------
title   | Pacific Northwest high-speed rail line
content | Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

Wenn Sie fertig sind, können Sie die Datenbankkonsole mit + \ q + verlassen.

Fazit

Dieses Handbuch befasste sich mit der Verwendung der Volltextsuche in PostgreSQL, einschließlich der Vorbereitung und Speicherung des Metadatendokuments und der Verwendung eines Index zur Verbesserung der Leistung. Weitere Informationen zu FTS in PostgreSQL finden Sie unter offizielle PostgreSQL-Dokumentation zur Volltextsuche.

Related