Skip to content

Instantly share code, notes, and snippets.

@tbk303
Created December 18, 2014 22:28
Show Gist options
  • Save tbk303/f9a4fec284161534f92c to your computer and use it in GitHub Desktop.
Save tbk303/f9a4fec284161534f92c to your computer and use it in GitHub Desktop.
Volltextsuche mit PostgreSQL

Volltextsuche mit PostgreSQL

Solr, ElasticSearch, Sphinx, ... alles überbewertet ;)

Das erledigt PostgreSQL quasi nebenbei. Inklusive einem riesen Vorteil: Der Such-Index ist nie out-of-sync mit dem primären Datenspeicher.

Theorie

"A document is the unit of searching in a full text search system; for example, a magazine article or email message."

Ein Dokument kann aus mehreren Attributen (z.B. Titel, Einleitung, Haupttext) bestehen und auch auf mehrere Tabellen verteilt sein.

  • Wörter, die interessant sind, extrahieren
  • Mapping von Wort auf Dokumenten-IDs speichern
  • Wörter schnell auffindbar machen

Postgres to the Rescue

This is where the Magic happens:

SELECT to_tsvector('This is a very meaningful example');

SELECT to_tsvector('Dies ist ein toller Beispieltext');

SELECT to_tsvector('german', 'Dies ist ein toller Beispieltext');

Abbildung von normalisierten Wortstämmen auf die Position in der ursprünglichen Zeichenkette.

Aber wie wird nach normalisierten Wörtern gesucht?

SELECT to_tsvector('This is a very meaningful example') @@ to_tsquery('meaningful');

SELECT to_tsvector('This is a very meaningful example') @@ to_tsquery('meaningful & example');

SELECT to_tsvector('This is a very meaningful example') @@ to_tsquery('mea:*');

SELECT id FROM documents WHERE to_tsvector(title) @@ to_tsquery('foobar');

Funktioniert für andere Sprachen analog. Manchmal will man keine Normalisierung und keine Stop-Wörter: 'simple' als Sprache wählen.

Die to_tsquery Funktion unterstützt auch diverse Operatoren wie !, &, |, Klammerung und Präfix-Suche.

Geschwindigkeit

Postgres bringt verschiedene Indizes für die to_tsvector Daten mit, darunter auch den klassischen invertierten Index. Die Nutzung ist denkbar einfach:

CREATE INDEX ON foos USING gin(to_tsvector('german', title));

Und Rails?

Noch einfacher:

# Gemfile
gem 'textacular'

# Code
FooModel.basic_search(title: 'Beispiel')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment