Введение в полнотекстовый поиск в PostgreSQL. Что надо знать о словарях. 1) Словарь - это программа, которая принимает на вход слово, а на выходе. выдает массив лексем, если словарь опознал слово. пустой массив ( void array ), если словарь знает слово, но оно является стоп-словом. NULL.
Это умеет делать MS Word. Есть словарь синонимов ASIS Виталия Тришина. Эта программа по крайней мере поможет найти начальную форму слова, и определить падеж/склонение и т.д. sun -sapient @ sun - sapient.
Этот протокол позволяет объектам во время работы программы, при Исходный код примера приложения- словаря можно найти в разделе статьи, получать определение и синонимы для заданного слова. запрос к базе данных, который объединяет таблицы word и synonym. Тезаурус представляет собой словарь синонимов и родственных слов (рис. 3.14). В программе Microsoft Word эта функция доступна для нескольких языков открывшемся меню указатель мыши на команду Синонимы ( Synonyms). Вот пример обработанной синонимайзером Word предыдущей фразы: Вована, где находится макрос word : http:// syn.zip. На странице есть ссылкаСкачать бесплатно словарь синонимов. ( Synonyms for the Most Commonly Used English Words). Употребляя синонимы - слова одной части речи, различные по звучанию и написанию.
если словарь не распознал слово. 2) Надо следить, чтобы все данные, которые используют словари,были в server_encoding. Встроенные словари включают:. Simple - возвращает входное слово в нижнем регистре или NULL.
если это стоп-слово. Ispell - шаблон для создания словарей, которые могут использовать словари Ispell [ISPELL]. которые доступны для большого количества языков. Также поддерживаются словари MySpell [MYSPELL] (OO < 2. 01) и Hunspell [HUNSPELL] (OO >= 2.
2). Большой список словарей доступен на странице [OODICTS]. Snowball stemmer - шаблон словаря, который по определенным правилам, специфическим для каждого языка, отрезает окончания у слов. Правила доступны для большого количества языков [SNOWBALL] и для 10 языков доступны в системе по умолчанию. Словарь принимает параметр, указывающий на положение файла со списком стоп-слов. synonym шаблон используется для создания словарей, которые заменяют одно слово на другое.
Для поддержки фраз используйте Thesaurus словарь. Одним из примеров использования синонимов - это решение лингвистических проблем. Например, слово 'Paris', распознается английским стеммером как 'pari'. Чтобы избежать этого, достаточно создать словарь синонимов и поставить его перед стеммером. thesaurus - шаблон для создания словарей, подобных словарю synonym. но с поддержкой фраз и нормализации слов. Покажем на примере астрономического тезауруса:.
cat tz_astro. txt.
Далее, мы создаем словарь tz_astro и кроме файла с синонимами указываем словарь, который будет использоваться для нормализации слов, так что 'supernovae stars' и 'supernovae star' будут опознанны как 'sn'. Далее, мы указываем, что английские слова будут обрабатываться сначала астрономическим тезаурусом.
Теперь тестируем:. 3) Тестировать словари можно с помощью функции lexize. 4) Словари можно добавлять в систему, см. пример [FTSBOOKAPPC]. Что нужно знать об индексах. Индексы используются только для ускорения операций. Результат выполнения запроса не зависит от использования индексов.
Индексы не всегда ускоряют операции. Для ускорения полнотекстового поиска можно использовать два индекса - на основе GiST [GIST] или GIN [GIN]. GIN индекс, или обобщенный обратный индекс - это структура данных, у которой для каждого ключа есть много значений. В случае полнотекстового поиска ключом является лексема, а значением - сортированный список идентификаторов документов, которые содержат эту лексему. Отметим, что позиционная информация не хранится в индексе, что связано с ограничениями PostgreSQL.
Так как в обратном индексе используется бинарное дерево для поиска ключей, то он слабо зависит от их количества и потому хорошо шкалируется. Этот индекс используется практически всеми большими поисковыми машинами, однако его использование в базах данных для индексирования изменяющихся документов затруднено, так как любые изменения (добавление нового документа, обновление или удаление) приводят к большому количеству обновлений индекса. Например, добавление нового документа, который содержит N уникальных лексем приводит к обновлению N записей в индексе.
Поэтому этот индекс лучше всего подходит для неменяющихся коллекций документов. GIN индекс поддерживает групповое обновление индекса, которое является очень эффективным, поэтому иногда быстрее создать индекс заново, чем обновлять индекс при добавке каждого документа. В тоже время, GiST индекс является "прямым" индексом, т. для каждого документа ставится в соответствие битовая сигнатура, в которой содержится информация о всех лексемах, которые содержаться в этом документе, поэтому добавление нового документа приводит к добавлению только одной сигнатуры. Для быстрого поиска сигнатуры хранятся в сигнатурном дереве RD-Tree (russian doll, матрешка), реализованная помощью GiST.
Сигнатура - это битовая строка фиксированной длины, в которой все биты изначально выставленны в '0'. С помощью хэш-функции слово отображается в определенный бит сигнатуры, который становится '1'. Сигнатура документа является наложением индивидуальных сигнатур всех слов. Такая техника называется superimposed coding и реализуется как bitwise OR, что является очень быстрой операцией. В этом примере, '11010000' является сигнатурой документа, состоящего из трех уникальных слов w1,w2,w3.
Сигнатура является некоторым компактным представлением документа, что приводит к значительному уменьшению размера коллекции. Кроме того, фиксированный размер cигнатуры сильно облегчает операции сравнения. Все это делает использование сигнатур вместо документов привлекательным с точки зрения производительности. При поиске, запрос можно аналогичным образом представить в виде сигнатуры и тогда процесс поиска будет заключаться в сравнении сигнатур. Если хотя бы одно положение '1' в сигнатурах не совпадает, то можно с уверенностью утверждать, что документ не содержит поисковый запрос.
Однако, если все '1' поисковой сигнатура совпадают с '1' сигнатуры документа, то это означает лишь то, что поисковый запрос может содержаться в документе и это требует проверки с использованием самого документа, а не его сигнатуры. Вероятностый ответ связан с использованием хеширования и суперпозиции. Ниже приводятся несколько примеров поисковых сигнатур.
Сигнатура Q2 является сигнатурой слова w1 и, таким образом, является правильным попаданием, в то время как сигнатура Q3 - ложным попаданием ( false drop ), несмотря на то, что она удовлетворяет сигнатуре документа. Ясно, что конечность размера сигнатуры и увеличение количества уникальных слов приводит к насыщению сигнатуры, т.
когда все биты будут '1', что сильно уменьшает избирательность сигнатуры и ухудшает производительность поиска.
Существуют несколько структур данных для хранения сигнатур, такие как сигнатурный файл (signature file),но они не являются индексами, так как требует полного просмотра. Дерево RD-Tree является аналогом R-Tree, приспособленное к работе со множествами для решения задачи поиска всех множеств, которые содержат в себе некое подмножество, является индексной структурой и может сильно ускорять поиск. Подробнее о RD-Tree можно прочитать в оригинальной статье [RDTREE]. В случает полнотекстового поиска, в качестве ключей выступают сигнатуры - сигнатуры документов находятся в концевых узлах, а во внутренних узлах находятся сигнатуры, которые удовлетворяют основному правилу дерева - родительская сигнатура содержит в себе сигнатуры всех потомков, т.
она является наложением (суперпозицией) всех сигнатур потомков ( наподобие тому, как получалась сигнатура документа). Поисковый запрос аналогично документу преобразовывается в поисковую сигнатуру и поиск происходит сравнением ее с сигнатурами в узлах в дереве. Из-за использования суперпозиции поиск по дереву может ответить однозначно только на то, что поисковая сигнатура точно не содержится в какой-либо сигнатуре, что позволяет не рассматривать не только эту сигнатуру, но и все поддерево под ней, что и приводит к значительному ускорению поиска. Например, для сигнатуры 11011000 правую ветку можно точно не рассматривать, однако она может находиться в левой ветке.
Очевидно, что чем больше глубина дерева, тем больше вероятность того, что сигнатура вырождается, т. начинает состоять из одних '1', а это приводит к тому, что приходится просматривать много веток и поиск замедляется.
В предельном случае, когда сигнатура состоит из одних '1', она становится бесполезной. Найденные результаты приходится дополнительно проверять на наличие "false drops", т.
проверять сами исходные документы, действительно ли они удовлетворяют поисковому запросу, что требует произвольного доступа к "heap" (таблице) и это сильно сказывается на производительности. Степень неоднозначности (lossiness), а следовательно и производительность GiST-индекса, зависит от кол-ва уникальных лексем и количества документов, что ограничивает применимость этого индекса для больших коллекций. Но это не вся правда о GiST-индексе.
На самом деле, в листьях могут храниться не сигнатуры, а сами tsvector-а, если они не превышают TOAST_INDEX_TARGET байт, что-то около 512 байт. В этом случае попадание является точным и проверять ничего не надо. К сожалению, пока нет возможности индексу сказать какое было попадание, но в будущем, когда появится такая возможность, эта оптимизация может очень хорошо работать для новостных сайтов, где документы не очень большие.
Чтобы изучить GiST-индекс, можно воспользоваться специальным модулем Gevel [GEVEL]. который выдает полезную информацию об индексе. Вот пример такой выдачи для индекса gist_idx_50 для базы, которая содержит небольшие сообщения.
Обратите внимание, что листья содержат как сами tsvector-а, так и сигнатуры, а внутренние ноды - только сигнатуры. Какой индекс использовать. После появления GIN-индекса, который хорошо шкалируется, может возникнуть ощущение, что GiST-индекс не нужен. Чтобы сравнить эти индексы мы взяли большую коллекцию абстрактов научных статей из arxiv. org (спасибо Сергею Карпову, который скачал и залил их в базу данных), которая содержит 459841 абстрактов. Вся база занимает чуть больше одного гигабайта.
Подробнее можно прочитать на wiki [GINGIST]. а здесь мы приведем только результаты (все времена приведены в миллисекундах). Тестировались три индекса - GiN-индекс и два GiST-индекса с разными факторами заполнения (fillfactor).
GiN-индекс пока не поддерживате fillfactor. Здесь count(*) - это простой поисковый запрос, а rank query - это поисковый запрос с ранжированием. Обновление индекса проверялось для 95,1035,10546 записей. Выводы:. создание индекса - GIN требует в 3 раза больше времени чем GiST. размер индекса - GiN-индекс в 2-3 раза больше GiST-индекса. время поиска - GiN-индекс в 3 раза быстрее, чем GiST-индекс.
обновление индекса - GiN-индекс обновляется в 10 раз медленнее. Таким образом, GiST-индекс надо использовать для обновляемых данных, а GiST - для статичных архивов. Разбиение данных на обновляемую часть и архив и использование соответствующих индексов, позволяет получать производительный поиск на больших коллекциях с обновляемым контентом.
Синхронизация полнотекстового индекса. Если ваша база данных хоть сколько-нибудь обновляется, то вам нужно будет следить за поддержанием полнотекстового индекс по мере добавление новых документов.
PostgreSQL позволяет автоматизировать этот процесс с помощью определения триггера, который запускается после добавления новой строки или обновления существующих записей. Встроенный триггер tsearch() позволяет легко настроить обновление индекса, можно задать несколько текстовых колонок и имя функции для обработки соответствующей колонки. Вот пример использования функции для замены знака @ на знак пробела.
Для более сложного случая, когда документ состоит из нескольких частей с разными весами можно написать процедуру на языке plpgsql (не забудьте разрешить его использование с помощью команды createlang plpgsql DBNAME ). Создадим тестовую табличку со следующей структурой. В этой функции мы для простоты опустили использование coalesce(). Как мы видим, вставка новых записей работает как и ожидалось. Проверим обновление.
Так как триггер запускается при любом обновлении или добавлении записей, то работа с таблицами может замедляться, если обновление полнотекстового индекса является очень дорогостоящей операцией, даже когда обновляются атрибуты, которые не имеют отношение к нему. Чтобы избежать лишней работы в функции fts_update можно вставить проверку на изменение текстового атрибута, например. Тестирование настроек. Зачастую бывает необходимо потестировать свою полнотекстовую конфигурацию.
Для этог существует встроенная функция ts_debug. которая наглядно показывает что происходит с текстом.
Она подробно описана в документации [FTSBOOKDEBUG]. мы приведем лишь пример:. Здесь заслуживает внимание последняя колонка, которая называется "Lexized token". В ней содержится имя словаря, который распознал токен и массив лексем, в который этот словарь преобразовал токен. Так как у нас настроен только один словарь pg_catalog.
en_stem. который к тому же распознает любые слова, то все токены им и распознались. Токен the распознался как стоп-слово, поэтому мы получили пустой массив и оно не будет проиндексировано. Остальные токены были приведены к некоторому нормальному виду. Можно указать явно название полнотекстовой конфигурации, что бы протестировать ее.
Как мы уже указывали выше, тестировать словари можно с помощью функции lexize. Парсеры также можно тестировать использую функцию parse.
зедсь tokid - это id типа токена.