2014-01-22 5 views
9

Ich habe einen DB, in dem es eine Tabelle shows mit einer mehrsprachigen Spalte title gibt. Ich möchte durch Hinzufügen eines Index zur Volltextsuche optimieren, wie so:Volltextsuche Index auf einer mehrsprachigen Spalte

CREATE INDEX title_idx ON shows USING gin(to_tsvector(title)); 

ich diesen Fehler:

ERROR: functions in index expression must be marked IMMUTABLE 

Es im Grunde fragt mich die Sprache Parameter hinzuzufügen to_tsvector unveränderlich zu machen. das Ergebnis wäre:

CREATE INDEX title_idx ON shows USING gin(to_tsvector(LANGUAGE, title)); 

wo LANGUAGE einer meiner Zielsprachen sein würde.

Ist es möglich, einen Index zu erstellen, der für mehrere Sprachen funktioniert?

Antwort

28

Ja, aber Sie brauchen eine zweite Spalte, die die Sprache des Textes identifiziert. Angenommen, Sie haben eine Spalte doc_language in die Tabelle eingefügt. Sie könnten dann schreiben:

CREATE INDEX title_idx ON shows USING gin(to_tsvector(doc_language, title)); 

Natürlich erfordert dies, dass Sie die Sprache des Subjekts Text, etwas was in der Praxis zu tun, kann hart sein. Wenn Sie Stemming usw. nicht benötigen, können Sie einfach die Sprache simple verwenden, aber ich nehme an, Sie hätten das schon getan, wenn es eine Option wäre.

Wenn Sie eine feste und begrenzte Anzahl von Sprachen haben, können Sie alternativ die Vektoren für die verschiedenen Sprachen verketten. Z.B .:

regress=> SELECT to_tsvector('english', 'cafés') || to_tsvector('french', 'cafés') || to_tsvector('simple', 'cafés'); 
      ?column?   
---------------------------- 
'caf':2 'café':1 'cafés':3 
(1 row) 

Das wird eine tsquery für cafés in einer dieser drei Sprachen entsprechen.

Als Index:

CREATE INDEX title_idx ON shows USING gin((
    to_tsvector('english', title) || 
    to_tsvector('french', title) || 
    to_tsvector('simple', title) 
)); 

aber das ist ungeschickt in Abfragen verwendet werden, wie die Planer über passenden Index quals nicht sehr klug ist. So würde ich es in einer Funktion wickeln:

CREATE FUNCTION to_tsvector_multilang(text) RETURNS tsvector AS $$ 
SELECT to_tsvector('english', $1) || 
     to_tsvector('french', $1) || 
     to_tsvector('simple', $1) 
$$ LANGUAGE sql IMMUTABLE; 

CREATE INDEX title_idx ON shows USING gin(to_tsvector_multilang(title)); 

Wenn Sie möchten, können sogar Lust bekommen: übergeben Sie die Liste der verfügbaren Sprachen als Array (aber nicht vergessen, es wird genau die gleichen sein müssen, um für ein Indexqualitätsübereinstimmung zu arbeiten). Verwenden Sie Prioritäten mit setweight, so bevorzugen Sie eine Übereinstimmung in Englisch zu einer auf Französisch, sagen. Alle möglichen Optionen.

+0

Danke, ich wusste nicht, dass Tsvectors zusammengeführt werden können. Das ist großartig. Upvote von mir :) – knitti

+0

Großartige Antwort! –

+0

Es gibt ein zusätzliches "(" auf der letzten Zeile des Codes. (Kann nicht weniger als 6 Zeichen bearbeiten ...) – Victor

0

Ich habe gerade eine Postgres-Funktion gemacht, um die Textsprache zu testen. Es ist nicht perfekt, aber es funktioniert für lange Texte.

CREATE OR REPLACE FUNCTION get_language(t text) RETURNS regconfig AS $$ 
DECLARE 
    ret regconfig; 
    BEGIN 
      WITH l as (SELECT cfgname, to_tsvector(cfgname::regconfig, title) as vector, length(to_tsvector(cfgname::regconfig, title)) as len 
     FROM pg_ts_config, (select t as title) as ti) 
    SELECT cfgname::regconfig 
    INTO ret 
    FROM l 
    WHERE len=(SELECT MIN(len) FROM l) 
    ORDER BY cfgname='simple' DESC, cfgname ASC 
    LIMIT 1; 
    RETURN ret; 
    END; 
$$ LANGUAGE plpgsql; 

Suchen Sie einfach nach dem kürzesten Tsvector für den gegebenen Text (so versucht es jede ts-Konfiguration von Postgres).