21 окт. 2007 г.

PostgreSQL + Full Text Search

Данная статья описывает конфигурирование FTS в PostgreSQL .

О самом FTS: Full-Text Search in PostgreSQL by Oleg Bartunov, Teodor Sigaev

Исходная ситуация:
  • gentoo linux
  • локаль koi8r
  • база должна быть в utf8
  • полнотекстовый поиск с учётом лексического анализа
merge'им postgresql версии не ниже 8.2
# emerge =dev-db/postgresql-8.2.4-r1

При инициализации кластера бд (initdb) лучше изменить локаль на ru_RU.utf8, чтобы весь кластер в целом был utf8, либо при создании бд указываем encoding UTF8

Создаём пользователя, бд.

Вливаем tsearch2

$  psql -U dbuser dbname < /usr/share/postgresql/contrib/tsearch2.sql


Гут.

По-умолчанию, есть только en_stem и ru_stem - которые могут делать разбор слов без лексического анализа.

в path_to_db/postgresql.conf :
# actually, defaults to database encoding
# Замечание : только если текущая системная локаль koi8r !
client_encoding = koi8r
# These settings are initialized by initdb -- they might be changed
# locale for system error message strings
lc_messages = 'C'
# locale for monetary formatting
lc_monetary = 'ru_RU.utf8'
# locale for number formatting
lc_numeric = 'ru_RU.utf8'
# locale for time formatting
lc_time = 'ru_RU.utf8'


Установим, используемую локаль text search'а на utf8_russian :
DELETE FROM pg_ts_cfg WHERE ts_name='default_russian';

UPDATE pg_ts_cfg set locale='ru_RU.utf8' WHERE ts_name='utf8_russian';
UPDATE pg_ts_cfg set locale='C' WHERE ts_name='default';
UPDATE pg_ts_cfg set locale='' WHERE ts_name='simple';


Замечание: TSearch2 выбирает из pg_ts_cfg первую локаль из списка, после такой последовательности UPDATE'ов желаемая локаль будет первой в списке.

Вместо *_stem будем использовать *_ispell: Привязываем русские словари:
# emerge ispell-ru # iconv /usr/lib/ispell/russian.dict -t utf8 -o /usr/lib/ispell/russian_utf8.dict
# iconv /usr/lib/ispell/russian.aff -t utf8 -o /usr/lib/ispell/russian_utf8.aff


И английские:

# iconv /usr/lib/ispell/english.aff -t utf8 -o /usr/lib/ispell/english_utf8.aff


INSERT into pg_ts_dict SELECT 'ru_ispell', dict_init,
'DictFile="/usr/lib/ispell/russian_utf8.dict",
AffFile="/usr/lib/ispell/russian_utf8.aff",
StopFile="/usr/share/postgresql/contrib/russian.stop.utf8"',
dict_lexize FROM pg_ts_dict WHERE dict_name='ispell_template';

INSERT into pg_ts_dict SELECT 'en_ispell', dict_init,
'DictFile="/usr/lib/ispell/english.dict",
AffFile="/usr/lib/ispell/english_utf8.aff",
StopFile="/usr/share/postgresql/contrib/english.stop"',
dict_lexize FROM pg_ts_dict WHERE dict_name='ispell_template';

DELETE FROM pg_ts_cfgmap
WHERE ts_name IN ('default_russian', 'utf8_russian')
AND tok_alias IN ('hword', 'lhword', 'lpart_hword', 'lword', 'word',
'nlhword', 'nlpart_hword', 'nlword');

INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'hword', '{ru_ispell,ru_stem_utf8}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'word', '{ru_ispell,ru_stem_utf8}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'lhword', '{en_ispell,en_stem}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'lpart_hword', '{en_ispell,en_stem}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'lword', '{en_ispell,en_stem}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'nlhword', '{ru_ispell,ru_stem_utf8}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'nlpart_hword', '{ru_ispell,ru_stem_utf8}');
INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
VALUES ('utf8_russian', 'nlword', '{ru_ispell,ru_stem_utf8}');




Всё :)

select ts_debug( 'car, cars, мама, мамы, маму, мамой ');
ts_debug
--------------------------------------------------------------------------------
(utf8_russian,lword,"Latin word",car,"{en_ispell,en_stem}",'car')
(utf8_russian,lword,"Latin word",cars,"{en_ispell,en_stem}",'car')
(utf8_russian,nlword,"Non-latin word",мама,"{ru_ispell,ru_stem_utf8}",'мама')
(utf8_russian,nlword,"Non-latin word",мамы,"{ru_ispell,ru_stem_utf8}",'мама')
(utf8_russian,nlword,"Non-latin word",маму,"{ru_ispell,ru_stem_utf8}",'мама')
(utf8_russian,nlword,"Non-latin word",мамой,"{ru_ispell,ru_stem_utf8}",'мама')
(6 rows)


SELECT * from to_tsvector('default_russian', 'дон, донья, пень, пни, пней, пнями, пну');
to_tsvector
----------------------------------------------
'дон':1 'пень':3,4,5,6 'донья':2 'пнуть':4,7
(1 запись)

3 комментария:

Анонимный комментирует...

Хмммм ... замечательно

А не подскажете, как то же самое сделать в постгресе 7.4 ?

Попробовал, почти работает, но ispell похоже не помогает, мамы & маму так и остается мамы & маму, не переводится в каноническую форму.

Vladimir Dolzhenko комментирует...

про 7.* увы подсказать ничего не смогу, ибо не сталкивался - нужно гуглить.

Если "мамы & маму" так и остаётся "мамы & маму", значит, что ispell не работает и приоритет находится, скорей всего у strem

webolga комментирует...

Здравствуйте, может вы знаете...

С чего начать не знаю.
У меня нет словаря на русском..
т.е.
Выполняю такой запрос

CREATE TEXT SEARCH DICTIONARY mydict_russian_ispell (
TEMPLATE = ispell,
DictFile = russian,
AffFile = russian,
StopWords = russian
);

получаю ошибку
ERROR: could not open dictionary file "C:/Program Files (x86)/PostgreSQL/8.4/share/tsearch_data/russian.dict": No such file or directory

Подскажите в чем дело.