Ежовый угол

Icon

Сеть, Рунет, телеком, Иркутск

Поиск с русской морфологией средствами PHP

На мой взгляд любой поиск по сайту должен обладать русской морфологией. На деле лишь единицы из проектов обладают этой возможностью. Разработчики ссылаются на сложность разработки и я до сего дня думал так же. На деле все оказалось проще — есть готовые словари. К примеру, phpMorphy.

Данный проект незаслуженно забыт программистами, хотя является удобным средством для организации поиска по сайту с учетом морфологии русского, английского или немецкого языков. Словари на каждый язык занимают порядка 2 Мб, сам скрипт — около 20 Кб. В составе phpMorphy нет инструментария дла работы с базой данных — его придется написать самостоятельно. Но в нем есть самое ценное:

  1. Определение словоформ указанного слова;
  2. Определение корня слова;
  3. Определение начальной формы слова.

Этого хватит слихвой для создания более-менее адекватного поиска. Лично я использовал определение корня слова, а затем каждый корень искал в полях, к примеру, так:

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. На данный момент результаты сотрируются по рейтингу сайта, но несложно организовать сортировку и по релевантности. Если кому-то нужны полные листинги — пишите.

Category: программирование, проекты

Tagged:

16 Responses

  1. psyhomo:

    thnx! Очень сильно помогло.

  2. zamma:

    Спасибо, очень полезная статья.

  3. Igor:

    Спасибо за статью, есть вопрос:
    я так понял, что словарь русский в кодировке ср1251, а у меня в базе данные в UTF-8 для правильной работы скрипта надо наверное перевести словарь в UTF-8? Если да, то как это сделать? Заранее спасибо.

  4. bool:

    Да, используй iconv

  5. Pikasso:

    Спасибо за очень полезную статью.

  6. CrashX:

    какие еще функции есть, прсто методом тыка сидеть и перебирать методы не есть хорошо…

  7. valerad12:

    «Лично я использовал определение корня слова, а затем каждый корень искал в полях»

    Если слов будет достаточно много, и для каждого искать через LIKE соответствующие записи будет большая нагрузка на БД.

    • Дядя Вова:

      С корнем в русском языке бывают большие сложности. А для снижения нагрузки исплользуют конструкцию match… against. Но для этого обязательно нужно создать полнотекстовые индексы для соответствующих полей таблиц.

  8. Имя:

    А существует ли что-либо на перле ?

  9. Для перла легко прикручивается mystem от яндекса (http://company.yandex.ru/technology/mystem/)

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

  10. bullgare:

    http://sourceforge.net/projects/phpmorphy/ — более актуальная ссылка на скачку

  11. Alexander:

    Интересный класс.

    Только запрос вида LIKE ‘%string%’ может очень сильно снизить производительность…

  12. Большое спасибо за прекрасную статью. Вы мне очень помогли!

  13. Константин:

    Любопытная хрень, однако… Не думал, что на халяву можно такую базу отыметь… Одна проблема — запросы по шаблону кромешно грузят базу. WHERE MATCH гораздо экономичней, да и сортирует в зависимости от плотности искомых слов… Однако ищет только по полному слову, а не по части :(
    Какие идеи? Заслать запрос с WHERE MATCH (…) OR WHERE MATCH (…) со всеми вариантами слова?.. А если ищется БОЛЬШАЯ фраза?.. 8)
    Есть идеи?

  14. Кирилл Жуковский:

    к сожалению, проект вроде как уже давно не поддерживается, но все равно спасибо за полезный материал!


Оставить комментарий

Реклама

Статистика

Рейтинг блогов