морфология — Блог Валерия Леонтьева https://valera.ws Место публикации личных заметок. Технологии, управление, бизнес, жизнь Mon, 24 Nov 2008 08:34:26 +0000 ru-RU hourly 1 https://wordpress.org/?v=5.6.2 https://valera.ws/wp-content/uploads/2020/02/favicon.png морфология — Блог Валерия Леонтьева https://valera.ws 32 32 Поиск в MySQL. Часть 3 «FULLTEXT IN BOOLEAN MODE» https://valera.ws/2008.04.15~fulltext-in-mysql/ https://valera.ws/2008.04.15~fulltext-in-mysql/#comments Tue, 15 Apr 2008 14:53:10 +0000 http://valera.ws/2008.04.15~fulltext-in-mysql/ Читать далее Поиск в MySQL. Часть 3 «FULLTEXT IN BOOLEAN MODE» ]]> Поиск с учетом русской морфологии

Поиск в MySQL. Часть 3 «FULLTEXT IN BOOLEAN MODE»

В первой части рассказа о поиске в MySQL рассказывается про использование полнотекстного индекса FULLTEXT. При поиске неких ключевых слов в большом массиве текста, хранящегося в БД, без использования индекса не обойтись. Однако родные возможности полнотекстового поиска в СУБД MySQL не обеспечиват функционал для поиска с учетом русской морфологии. Решение этой проблемы описывалось во второй части «Поиск с учетом русской морфологии». И вот недавно в описанном алгоритме обнаружился большой недостаток. Что это за недостаток и как с ним бороться и описано в этой статье.

Для начала хочу заметить один нюанс. Не так давно вышел релиз СУБД PostgreSQL 8.3. И одной из важнейших его особенностей стало то, что полнотекстный поиск, причем еще и с поддержкой русской морфологии, включен в ядро СУБД. Теперь не надо использовать различные плагины и примочки, чтобы пользоваться возможностями полнотекстного о поиска. А возможности эти на порядок мощнее и щире возможностей fulltext-поиска в MySQL. По-этому, если поиск информации в базе для вашего проекта является одной из центральных «фишек», стоит смотреть в сторону Postgres.

Если же у вас нет выбора СУБД, или требования к поиску невысокие, можно пользоваться и моим решением на базе MySQL. Только необходимо учесть поправки для поискового запроса, описанные ниже.

По делу. При использовании конструкции MATCH … AGAINST СУБД ищет указанные в запросе ключевые слова в индексе, если подходящий найден, или в указанном поле данных. Поиск может проходить в трех разных режимах: в натуральном (обычном) и логическом (boolean mode). Общее между этими режимами то, что в обоих случаях фраза поиска, переданная в AGAINST будет разделена на слова. И при поиске будут находиться записи, в которых встречается хотя бы одно из указанных слов.

Например, при указании фразы «трудовой договор» будут найдены все строки, в которых встречается слово «трудовой» и все строки со словом «договор». На практике такой подход оказывается неприемлемым, так как выдает слишком много лишних результатов. Особенно ярко выражается проблема, когда одно слово очень популярное и находится в огромном количестве строк. В этом случае уточняющее слово практически не имеет смысла. И релевантность тут не спасет. Популярное слово может встретиться в строке 50 раз. В этом случае строка получит релевантность значительно выше, чем строка в которой оба слова встретились по одному разу.

В результате поиск по такому принципу в большом массиве информации становится неюзабельным. Обойти проблему можно с помощью второго режима поиска — поиска в логическом режиме. Его преимущество в том, что с помощью некоторых специальных операторов можно управлять значимостью и ролью слов в запросе, а также искать по фразам.

Для включения логического режима используется синтаксис: MATCH (…) AGAINST (‘…’ IN BOOLEAN MODE);

Оператор пишется в кавычках непосредственно перед ключевым словом, к которому он относится. Операторы, которые воспринимает анализатор запроса:

  • — (дефис, символизирующий «минус»). Этот оператор указывает, что данное ключевое слово не должно содержаться в поле (NOT);
  • + указывает, что слово должно обязательно содержаться в запросе (AND);
  • > и < соответственно увеличивают и уменьшают вес слова при подсчете релевантности;
  • ( и ) группируют слова для применения к ним оператора
  • ~ имеет смысл, схожий с «минусом», но оно слово не исключает, а просто уменьшает релевантность строки, в которой слово встретилось;
  • * — оператор усечения, ставится в конце слова и имеет классический смысл (например, слов*);
  • » (кавычка). В кавычки берутся фразы, по которым требуется искать целиком.

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

Чтобы обеспечить точный поиск и релевантность вывода, передадим в запрос ключевые слова в обработанном виде. Первой частью пустим в кавычках фразу, составленную из слов, которые ввел пользователь (естественно, предварительно отфильтровав спец-символы). Установим перед ней оператор повышения релевантности «>». Далее через пробел открываем скобки. В них нужно указать слова, полученные от морфологического движка. Перед скобками ставим оператор уменьшения релевантности. Слова пишутся через пробел, а перед каждым словом ставится оператор «+».

Для наглядности приведем пример. При поиске по фразе «отпуск за работу» запрос будет выглядеть так: … MATCH (…) AGAINST (‘>»отпуск за работу» <(+отпуск +работу)’ IN BOOLEAN MODE) …

Кроме этого, из-за логического режима поиска необходимо вручную указать порядок вывода (сортировку) найденных строк. Для этого добавляем в выборку еще один столбец, который определяется так выражением MATCH … AGAINST с псевдонимом. Причем оно должно быть идентично выражению в блоке WHERE. Тогда СУБД будет выполнять поиск только один раз. Ну а в конец запроса нужно дописать, что сортировка идет по полю (через псевдоним) в порядке убывания.

Итак, теперь пример полного результирующего запроса. Поиск по фразе «отпуск за работу» в таблице table в полях text и text_index, по которым был предварительно создан полнотекстный индекс:

SELECT SQL_CALC_FOUND_ROWS *,
MATCH (text,text_index) AGAINST (‘>»отпуск за работу» <(+отпуск +работа)’ IN BOOLEAN MODE) AS rel
FROM table
WHERE MATCH (text,text_index) AGAINST (‘>»отпуск за работу» <(+отпуск +работа)’ IN BOOLEAN MODE)
ORDER BY rel DESC LIMIT 0, 10

Результаты такого поиска получается вполне неплохими и очень даже приемлемыми.

]]>
https://valera.ws/2008.04.15~fulltext-in-mysql/feed/ 14
Поиск в MySQL. Часть 2 «Поиск с учетом русской морфологии» https://valera.ws/2007.09.05~morpho_search_in_mysql/ https://valera.ws/2007.09.05~morpho_search_in_mysql/#comments Wed, 05 Sep 2007 08:23:10 +0000 http://valera.ws/2007.09.05~morpho_search_in_mysql/ Читать далее Поиск в MySQL. Часть 2 «Поиск с учетом русской морфологии» ]]> Поиск с учетом русской морфологииПоиск в MySQL. Часть 2 «Поиск с учетом русской морфологии»

В этой статье описывается идея создания поиска в базе MySQL на основе индекса FULLTEXT с учетом русской морфологии. Никаких модулей для PHP или MySQL и других программ устанавливать на хостинг не нужно. И это важное преимущество данного алгоритма. Алгоритм подойдет для сайтов с малой и средней нагрузкой. Для крупных порталов, конечно, следует искать более скоростные и производительные решения (например, Sphinx). Мой вариант поиска работает значительно быстрее поиска на основе регулярных выражений и подойдет для большого количества web-проектов. Кстати, и не только web :)

Основан алгоритм на уже готовых решениях. Движок для  работы с морфологией – phpMorphy. Это морфологический модуль, написанный на PHP. Он основан на словарях проекта AOT. Оба продукта распространяются под лицензией LGPL.

Вкратце про проект phpMorphy.

Возможности:

  • Получение базовой формы слова
  • Получение всех словоформ слова
  • Получение грамматических характеристик для каждой словоформы
  • Предсказание ненайденных в словаре слов

Характеристики:

Анализ производится по словарю, размер словаря для русского языка ~4Mb. Скорость работы ~700 слов в секунду в нормальном режиме и ~1000 слов с загруженным в память словарем (без предсказания).

Подключение движка морфологии.

Для начала, конечно, нужно скачать модуль phpMorphy с официального сайта. В архиве находятся два каталога: dicts – словари АОТ, src – исходники phpMorphy. Разместите эти два каталога на сервере (желательно в общем каталоге phpMorphy). Далее для работы с модулем необходимо подключить в скрипте сайта файл src/common.php. Например так:

require_once(’phpmorphy/src/common.php’);

Библиотека подключена. Теперь вкратце о ее настройках. Порядок создания объекта:

$dict_bundle = new phpMorphy_FilesBundle(’phpmorphy/dicts’, ‘rus’); // создается объект словарей, //передается путь к каталогу со словарями. В данном случае используется русский словарь.

// Создаем объект словарей

$morphy = new phpMorphy($dict_bundle, array(’storage’ => PHPMORPHY_STORAGE_MEM, ‘with_gramtab’ => false, ‘predict_by_suffix’ => true, ‘predict_by_db’ => true));

Подробнее об опциях можно почитать в README к скрипту, а вот про ‘storage’ стоит упомянуть и тут. Это важная настройка.

phpMorphy поддерживает следующие варианты работы со словарями:

  • PHPMORPHY_STORAGE_FILE – использует файловые операции (fread, fseek) для доступа к словарям. Этот вариант самый медленных, но требует меньше всего памяти.
  • PHPMORPHY_STORAGE_SHM – загружается словари в общую память (используя расширение PHP shmop). Этот режим предпочтителен.
  • PHPMORPHY_STORAGE_MEM – загружает словари в память каждый раз, когда phpMorphy инициализируется. Этот режим используется, когда расширение shmop не включено. Скорость у PHPMORPHY_STORAGE_SHM и PHPMORPHY_STORAGE_MEM одинаковая.

Так как в начале статьи оговорено, что никаких дополнительных расширений мы не используется, в примере указано ’storage’ => PHPMORPHY_STORAGE_MEM. Если с такими настройками ваш поиск «вылетает» с ошибками о недостатке памяти, или злостный хостер ругается, что вы расходуете память больше положенного, включите’storage’ => PHPMORPHY_STORAGE_FILE.

Сразу хочу обратить ваше внимание, что алгоритм поиска будет правильно работать только при правильно настроенной локали (PHP setlocale) и кодировке в БД.

Изменения в таблицах БД.

Теперь поговорим об организации условий для поиска в БД. Напомню, что поиск у нас основан на индексах, что определяет высокую скорость выполнения поисковых запросов (подробнее в части 1).

Допустим, у нас есть некая таблица сообщений, по которой будет осуществляться поиск. В таблице есть поля: message_id, message_text,  message_date. Искать нужно по полю message_text. Для организации поиска с учетом морфологии необходимо создать еще одно поле. Назовем его message_words_index. И как раз для этого поля необходимо создать индекс FULLTEXT вместе с полем message_text.

Пример создания такой таблицы:

CREATE TABLE `fn_messages` (`message_id` int(10) unsigned NOT NULL auto_increment,
`message_text` text NOT NULL,   `message_date` timestamp
NOT NULL default CURRENT_TIMESTAMP,
`message_words_index` text NOT NULL,
PRIMARY KEY  (`message_id`),
FULLTEXT KEY `fn_messages_words_index` (`message_text`,`message_words_index`)
) ENGINE=MyISAM;

Таблица готова к работе с полнотекстовым морфологическим поиском.

Внесение данных в таблицу.

При добавлении записей в таблицу, их нужно готовить для проведения поиска. Первые три поля заполняются как обычно, а в последнее вносятся слова сообщения в обработанной начальной форме. Что бы получить начальную форму слов, используется phpMorphy. Создадим функцию function Words2BaseForm($text), в которую передается исходный текст сообщения, а возвращает она в виде строки набор слов в начальных формах. Например:

Передано Получено
При реорганизации предприятия в мае менялись формы и условия контрактов. Когда набирали новых сотрудников с ними контракт не заключали, ждали пока новые формы не будут разработаны. Прошло 3 месяца. Как нам правильно заключить контракты с вновь принятыми людьми, т.к. задним числом мы заключить не можем (с введением новых контрактов изменились и условия оплаты)? БЫТЬ ВВЕДЕНИЕ ЖДАТЬ ЗАДНИЙ ИЗМЕНИТЬСЯ КОГДА ЧЕЛОВЕК МЕНЯТЬСЯ НАБИРАТЬ ОПЛАТА ФОРМА ПОКА РАЗРАБОТАТЬ СОТРУДНИК ЧИСЛО ВНОВЬ ЗАКЛЮЧИТЬ КОНТРАКТ КОНТРАКТОВЫЙ МЕСЯЦ МОЧЬ НОВЫЙ НОВОЕ ПРАВИЛЬНЫЙ ПРАВИЛЬНО ПРЕДПРИЯТИЕ УСЛОВИЕ ПРИНЯТЬ ПРИНЯТЫЙ ПРОЙТИ ПРОШЛЫЙ РЕОРГАНИЗАЦИЯ ЗКЛЮЧАТЬ

Содержимое столбца «Получено» будет помещено в поле message_words_index. Скачать функцию function Words2BaseForm($text) можно здесь.

Поиск данных.

При получении запроса на поиск для обработки фразы поиска используется другая функция, созданная для работы с phpMorphy: function Words2AllForms($text). Принимая разделенные пробелом слова для поиска, она возвращает эти слова во всех их морфологических формах. Таким образом, в полях message_text и message_words_index будут искаться все формы слов, а результаты выведутся по релевантности. Можно создавать индекс и искать только по полю message_words_index, но в этом случае вывод по релевантности будет нарушен.

Замечу, что создание дополнительного поля message_words_index необходимо еще и потому, что система phpMorphy может привести к начальной форме даже те слова, которых нет в словарях. Она это делает по анализу слова в соответствии с
правилами морфологии языка. А вот обратная система не сработает. Поэтому поиск по начальной форме даст более точные результаты.

Итак, передаем поисковый запрос в функцию function Words2AllForms($text), а возвращенный ей результат помещаем в запрос на выборку (вместе с изначальным условием для соблюдения релевантности). Скачать функцию function Words2AllForms($text) можно здесь.

Запрос имеет следующий вид:

SELECT SQL_CALC_FOUND_ROWS * from messages WHERE MATCH (message_text, message_words_index) AGAINST (изначальный_текст, обработанный_текст) LIMIT 0, 10;

Подсветка найденных слов.

Не буду писать про организацию подсветки много слов. Скажу лишь, что она основана на словоформах, полученных из function Words2AllForms($text). Скачать PHP-код подсветки найденных слов можно здесь.

Заключение.

Протестировать данный алгоритм поиска можно на сайте http://kadrovik.by.

Сама идея поиска «распространяется» под лицензией BSD и принадлежит мне (Валерию Леонтьеву). При перепечатке статьи ссылка на эту страницу обязательна. Обращаю внимание, что использованные в алгоритме библиотеки распространяются по лицензии LGPL.

Ссылки.

Продолжение! Внимание, при использовании описанной технологии обязательно прочитайте продолжение статьи. Правки в этот материал решено не вносить. Поиск в MySQL. Часть 3 «FULLTEXT IN BOOLEAN MODE»

]]>
https://valera.ws/2007.09.05~morpho_search_in_mysql/feed/ 20