Polish TSearch in 8.3 (polski tsearch w postgresie 8.3)
English disclaimer: this post will be in Polish only, as it’s use for other nationalities is somehow limited
jakiś czas temu pisałem jak skonfigurować polskiego tsearcha pod postgresem 8.2.
od tamtej pory trochę się zmieniło, pojawił się 8.3, w którym tsearch jest wbudowany w bazowego postgresa, więc instalacja jest prostsza.
zakładam, że postgres jest skonfigurowany tak by działać w utf-8. jeśli jest to iso-8859-2, to trzeba po prostu pominąć kroki odpowiedzialne za zmianę kodowania plików.
zaczynamy od wizyty na znanej z poprzedniego postu stronie: http://kurnik.pl/dictionary/.
ona, przenosi nas na sjp.pl, ale to jest mało istotne. znajdujemy najnowszy słownik w formacie ispella, i pobieramy jego źródła. dziś plik nazywa się sjp-ispell-pl-20080422-src.tar.bz2 - jutro pewnie będzie inny
po ściągnięciu plik rozpakowuję:
tar xvjf sjp-ispell-pl-*-src.tar.bz2
a następnie, wchodzę do źródeł i wydaję sekwencję poleceń:
cd sjp-ispell-pl-[0-9]*
sort -u -t/ +0f -1 +0 -T /usr/tmp -o polish.med polish.all
for a in polish.aff polish.med; do cat $a | iconv -f iso8859-2 -t utf-8 > $a.utf8; done
cp polish.aff.utf8 `pg_config –sharedir`/tsearch_data/polish.affix
cp polish.med.utf8 `pg_config –sharedir`/tsearch_data/polish.dict
touch `pg_config –sharedir`/tsearch_data/polish.stop
ostatnie 3 polecenia mogą wymagać użycia sudo - zależnie od tego czy user z którego to odpalaliśmy miał prawa zapisu do katalogu “share” postgresa.
po zrobieniu tego pora na “przyjemności”.
łączę się psql’em do bazy w której chcę mieć tsearcha i wykonuję następujące sqlki:
CREATE TEXT SEARCH CONFIGURATION public.polish ( COPY = pg_catalog.english );
CREATE TEXT SEARCH DICTIONARY polish_ispell (
TEMPLATE = ispell,
DictFile = polish,
AffFile = polish,
StopWords = polish
);
ALTER TEXT SEARCH CONFIGURATION polish
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart,
word, hword, hword_part
WITH polish_ispell, simple;
i to zasadniczo wszystko.
już teraz powinniście zobaczyć, że parser działa i odmienia prawidłowo:
SELECT * FROM ts_debug(
'public.polish',
'W Szczebrzeszynie chrząszcz brzmi w trzcinie, i Szczebrzeszyn z tego słynie'
) where alias <> 'blank';
alias | description | token | dictionaries | dictionary | lexemes
-----------+-------------------+-----------------+------------------------+---------------+-----------------
asciiword | Word, all ASCII | W | {polish_ispell,simple} | polish_ispell | {w}
asciiword | Word, all ASCII | Szczebrzeszynie | {polish_ispell,simple} | polish_ispell | {szczebrzeszyn}
word | Word, all letters | chrząszcz | {polish_ispell,simple} | polish_ispell | {chrząszcz}
asciiword | Word, all ASCII | brzmi | {polish_ispell,simple} | polish_ispell | {brzmieć}
asciiword | Word, all ASCII | w | {polish_ispell,simple} | polish_ispell | {w}
asciiword | Word, all ASCII | trzcinie | {polish_ispell,simple} | polish_ispell | {trzcina}
asciiword | Word, all ASCII | i | {polish_ispell,simple} | polish_ispell | {i}
asciiword | Word, all ASCII | Szczebrzeszyn | {polish_ispell,simple} | polish_ispell | {szczebrzeszyn}
asciiword | Word, all ASCII | z | {polish_ispell,simple} | polish_ispell | {z}
asciiword | Word, all ASCII | tego | {polish_ispell,simple} | polish_ispell | {ten,ty}
word | Word, all letters | słynie | {polish_ispell,simple} | polish_ispell | {słynąć}
(11 rows)
teraz, zakładając, że mamy tabelkę:
CREATE TABLE test (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL DEFAULT '',
lead TEXT NOT NULL DEFAULT '',
body TEXT
);
możemy dodać do niej pełnotekstowe wyszukiwanie na 2 sposoby: prosty, ale brzydki i utrudniający później pisanie. albo ciut bardziej skomplikowany, ale za to ładniejszy.
metoda 1:
CREATE INDEX tsearch_test ON test USING gin(to_tsvector('public.polish', title || lead || body));
i koniec.
minus jest taki, że teraz trzeba będzie za każdym razem:
select * from test where to_tsvector('public.polish', title || lead || body) @@ 'trzciny';
metoda 2:
alter table test add column ft tsvector;
update test set ft=to_tsvector('public.polish', title || lead || body);
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON test FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(ft, 'public.polish', title, lead, body);
CREATE INDEX tsearch_test ON test USING gin( ft );
jak widać - roboty trochę więcej, ale za to zapytania będą ładniejsze:
select * from test where ft @@ 'trzciny'
dodatkowo - aby sobie ułatwić testowania można zrobić coś takiego:
alter database NAZWA_BAZY_DANYCH set default_text_search_config = 'public.polish';
dzięki czemu używając np. ts_debug() nie trzeba będzie podawać słownika (w 8.2 do detekcji używał encodingu, tu jest to inaczej rozwiązane).
mam nadzieję, że opis jest jasny. no i, że się przyda
April 23rd, 2008 at 09:02
Jedna uwaga. Wydaje mi się, że w przypadku systemu z LC_ALL=pl_PL.UTF-8 należy zmienić kodowanie pliku polish.all przed jego posortowaniem. W przeciwnym wypadku uzyskany efekt jest trochę inny od zamierzonego
May 12th, 2008 at 17:03
Świetne, dzięki za tą notke