16 августа 2007
Поиск с русской морфологией средствами PHP
На мой взгляд любой поиск по сайту должен обладать русской морфологией. На деле лишь единицы из проектов обладают этой возможностью. Разработчики ссылаются на сложность разработки и я до сего дня думал так же. На деле все оказалось проще — есть готовые словари. К примеру, phpMorphy.
Данный проект незаслуженно забыт программистами, хотя является удобным средством для организации поиска по сайту с учетом морфологии русского, английского или немецкого языков. Словари на каждый язык занимают порядка 2 Мб, сам скрипт — около 20 Кб. В составе phpMorphy нет инструментария дла работы с базой данных — его придется написать самостоятельно. Но в нем есть самое ценное:
- Определение словоформ указанного слова;
- Определение корня слова;
- Определение начальной формы слова.
Этого хватит слихвой для создания более-менее адекватного поиска. Лично я использовал определение корня слова, а затем каждый корень искал в полях, к примеру, так:
SELECT * FROM `tbl1` WHERE UPPER(`col1`) LIKE ‘%АФИШ%’;
UPPER() используется из-за того, что phpMorphy возвращает все слова в верхнем регистре.
Как я понял, последние версии phpMorphy не работают на PHP4, поэтому стоит начать миграцию на PHP5, хотя если ваши скрипты обновляются более-менее часто, правки будут незначительны.
Подготовка текста
Для обработки введенной пользователем строки удобно пользоваться регулярными выражениями — пример есть на http://php.spb.ru/php/word.html.
Работа со словарями
Для начала нужно скачать требуемые словари (на ваш выбор — русские, английские, немецкие) и готовые модули для работы с ними.
Распаковав модули, у нас получаются файлы со справкой, а также 3 папки: dicts, src и examples. В первую нужно распаковать словари, во второй находятся модули для работы со словарями, а в третьей — пример использования phpMorphy.
Приводить текст примера не буду, вы можете увидеть его самостоятельно. Остановлюсь лишь на его ключевых моментах.
Все, что находится выше строки:
// Hint: in this example words $word_one, $word_two are in russian language(cp1251 encoding)
используется для инициализации словарей. Там ничего не нужно править. Исключение составляют закомментированныя строки:
// $codepage = $morphy->getCodepage();
// setlocale(LC_CTYPE, array(‘ru_RU.CP1251’, ‘Russian_Russia.1251’));
которые в случае необходимости правите для изменения рабочей кодировки — в немецком это cp1252, а в английском cp1250. Но наверняка вам это не понадобится.
Ниже идет пример работы. У phpMorphy есть 2 режима работы — одиночный (single mode) и блочный (bulk mode). Первый используется для обработки каждого слова по отдельности, второй — для обработки целого массива слов. Остановимся на втором, как наиболее удобном, по крайней мере на мой взгляд.
Для начала задаем входные слова:
$word_one = ‘ПРОВЕРКА’;
$word_two = ‘МОРФОЛОГИЗАТОРА’;
…
$bulk_words = array($word_one, $word_two);
С помощью phpMorphy мы можем получить у них начальную форму:
$base_form = $morphy->getBaseForm($bulk_words);
получить все словоформы:
$all_forms = $morphy->getAllForms($bulk_words);
или получить корень слова:
$pseudo_root = $morphy->getPseudoRoot($bulk_words);
Что для вас — ваш выбор. Я остановился на определении корня слова — максимально короткий SQL-запрос при аналогичной точности выборки.
В качестве результата возвращается массив, который мы можем использовать для поиска. К примеру, так:
$request_words = array();
$columns = array(«title», «description»);
foreach($pseudo_root as $group_form)
if($group_form)
foreach($group_form as $one_form)
foreach($columns as $col)
$request_words[] = «UPPER(`».$col.»`) LIKE ‘%».$one_form.»%'»;$request = «SELECT * FROM `tbl1` WHERE («.join(» OR «,$request_words).»);»;
Дам комментарий — в $columns мы указываем список полей таблицы, по которым производим поиск. В $request, соответственно, формируется окончательный SQL-запрос, который остается лишь выполнить. 🙂
PS Попробовать поиск можно на KamWeb.ru. На данный момент результаты сотрируются по рейтингу сайта, но несложно организовать сортировку и по релевантности. Если кому-то нужны полные листинги — пишите.
thnx! Очень сильно помогло.
Спасибо, очень полезная статья.
Спасибо за статью, есть вопрос:
я так понял, что словарь русский в кодировке ср1251, а у меня в базе данные в UTF-8 для правильной работы скрипта надо наверное перевести словарь в UTF-8? Если да, то как это сделать? Заранее спасибо.
Да, используй iconv
Спасибо за очень полезную статью.
какие еще функции есть, прсто методом тыка сидеть и перебирать методы не есть хорошо…
«Лично я использовал определение корня слова, а затем каждый корень искал в полях»
Если слов будет достаточно много, и для каждого искать через LIKE соответствующие записи будет большая нагрузка на БД.
С корнем в русском языке бывают большие сложности. А для снижения нагрузки исплользуют конструкцию match… against. Но для этого обязательно нужно создать полнотекстовые индексы для соответствующих полей таблиц.
А существует ли что-либо на перле ?
Для перла легко прикручивается mystem от яндекса (http://company.yandex.ru/technology/mystem/)
Для пхп тоже, кстати, его можно использовать, но в случае провайдерского запрета на системные вызовы придется дописать обертку (на том же перле :))
http://sourceforge.net/projects/phpmorphy/ — более актуальная ссылка на скачку
Интересный класс.
Только запрос вида LIKE ‘%string%’ может очень сильно снизить производительность…
Большое спасибо за прекрасную статью. Вы мне очень помогли!
Любопытная хрень, однако… Не думал, что на халяву можно такую базу отыметь… Одна проблема — запросы по шаблону кромешно грузят базу. WHERE MATCH гораздо экономичней, да и сортирует в зависимости от плотности искомых слов… Однако ищет только по полному слову, а не по части 🙁
Какие идеи? Заслать запрос с WHERE MATCH (…) OR WHERE MATCH (…) со всеми вариантами слова?.. А если ищется БОЛЬШАЯ фраза?.. 8)
Есть идеи?
http://company.yandex.ru/technology/mystem/
к сожалению, проект вроде как уже давно не поддерживается, но все равно спасибо за полезный материал!
Здраствуйте, есть поиск основаный на phpmorphy подскажыте как организовать поиск сразу по нескольким словарям? (en rus ua) так как текст может быть на разных языках и даже одновременно сочитать 2 языка к примеру руский + английский…
Запросы типа LIKE ‘%текст%’ не айс. Дело не сколько производительности этих запросов, сколько в самом формате. Представим что у нас в тексте есть слова `водитель` и `производитель`. Так же представим, что поисковый запрос пришел по слову `водитель` согласно шаблону лайка будут выведены не только водители, но и производители! В некоторых задачах это фатально. И таких слов может набраться существенное количество. И то что по началу казалось хорошей идей просто перестает работать. В mysql можно организовать с помощью регулярок поиск в данной ситуации. Но работает он на столько медленно, что даже для среднего размера сайта не годится. И отсюда мы приходим к тому что сейчас является нормально при поиске: индексация.
Возникает проблема с буквой «Я» если я беру слово деревянный, то он у меня пишет так: дерев?нный. Как исправить это?