Полнотекстовый поиск в PostgreSQL

Задача поиска по тексту предполагает работу со сложными индексами и большими объемами данных. Использование стандартных средств, таких как оператор LIKE в СУБД не дает нужных результатов по причине того, что: они не имеют возможность обеспечить поддержку множества форм одного слова; они не предоставляют данных для ранжирования документов; очень медленные, потому что каждый раз просматривают весь документ без индексации.

Поэтому для решения этих проблем образовалась целая группа отдельных инструментов. Все технологии полнотекстового поиска работают по одному принципу. На основе текстовых данных строится индекс, который способен очень быстро искать соответствия по ключевым словам. 

Существует два пути использования полнотекстового поиска: встроенные возможности СУБД и внешние индексаторы. 

Рассмотрим азы полнотекстового поиска на примере PostgreSQL, в котором есть встроенные механизмы для его организации. 

Для этого существуют типы данных - tsvector, который является хранилищем для лексем документа, оптимизированноый для поиска, и tsquery - для запроса с поддержкой логических операций. Тип tsvector кроме лексем может хранить информацию о положении лексемы в документе и ее весе (важности), которая потом может использоваться для вычисления ранжирующей информации.  Плюс есть полнотекстовый оператор @@ с поддержкой GiST и GIN индексов.  

GiST-индекс надо использовать для обновляемых данных, а GIN - для статичных архивов. Обусловлено это тем, что GIN индекс быстрее в поиске, но значительно дольше строится, дольше обновляется и имеет больший размер чем GiST. Разбиение данных на обновляемую часть и архив и использование соответствующих индексов, позволяет получать производительный поиск на больших объемах данных с обновляемым контентом.

Объекты tsvector и tsquery можно получить при помощи функций to_tsvector и to_tsquery следующим образом: 

select to_tsvector('a fat  cat sat on a mat'); 
select to_tsquery('fat & cats'); 

Пусть у нас есть таблица документов documents, объявленная следующим образом: 

CREATE TABLE documents 
(
  id SERIAL NOT NULL PRIMARY KEY, 
  title CHARACTER VARYING(255) NOT NULL, 
  body TEXT, 
  tsv tsvector 
); 

Поле tsv содержит tsvector, который оптимизирован для поиска и хранит позиционную информацию лексемы в документе и ее вес, на основе которого мы построим полнотекстовый индекс. 

CREATE INDEX documents_tsv_idx ON documents USING GIST (tsv);   

Для поддержания индекса в актуальном состоянии сделаем триггер на добавление и обновление записей: 

CREATE FUNCTION doc_tsv_trigger() RETURNS trigger AS $$   
BEGIN 
  NEW.weighted_tsv := 
    setweight(to_tsvector('english',COALESCE(NEW.title,'')), 'A') || 
    setweight(to_tsvector('english',COALESCE(NEW.body,'')), 'B'); 
  RETURN NEW; 
END 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER upd_tsvector  
BEFORE INSERT OR UPDATE  ON documents  
FOR EACH ROW  
EXECUTE PROCEDURE doc_tsv_trigger();  
 

C помощью функции setweight мы прописали разные веса лексемам из разных частей записи. Теперь можно выполнить полнотекстовых поиск, следующим образом: 

SELECT title, data, rank_cd(tsv, q)  
FROM documents, to_tsquery('test & search' ) AS q 
WHERE tsv @@ q 
ORDER BY rank_cd desc; 

Функция rank_cd возвращает ранг документа относительно поискового запроса. Чем выше ранг, тем лучше результат поиска. 

Для того, чтобы понять какие слова значимы и учитываются в индексе существует функция ts_debug, которая по исходной строке определяет список токенов, используемые словари и результируещие лексемы: 

select * from ts_debug('as 12 cats привет мир'); 

   alias   |    description    | token  |  dictionaries  |  dictionary  | lexemes 
-----------+-------------------+--------+----------------+--------------+---------- 
 asciiword | Word, all ASCII   | as     | {english_stem} | english_stem | {} 
 blank     | Space symbols     |        | {}             |              | 
 uint      | Unsigned integer  | 12     | {simple}       | simple       | {12} 
 blank     | Space symbols     |       +| {}             |              | 
           |                   |        |                |              | 
 asciiword | Word, all ASCII   | cats   | {english_stem} | english_stem | {cat} 
 blank     | Space symbols     |        | {}             |              | 
 word      | Word, all letters | привет | {english_stem} | english_stem | {привет} 
 blank     | Space symbols     |        | {}             |              | 
 word      | Word, all letters | мир    | {english_stem} | english_stem | {мир} 

Для получения более детальной информации по используемым словарям можно обратиться к документации по PostgreSQL.

15.09.2016









 
архив

подписка