Ежовый угол

Avatar

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

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



RSS Feed | Trackback URI

Комментариев: 14 »


+ Comment by zamma
Понедельник, 7 апреля 2008 @ 16:48

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

 

+ Comment by Igor
Четверг, 10 апреля 2008 @ 18:36

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


+ Comment by bool
Четверг, 7 августа 2008 @ 05:42

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

 
 

+ Comment by Pikasso
Вторник, 23 сентября 2008 @ 19:29

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

 

+ Comment by CrashX
Вторник, 21 октября 2008 @ 07:55

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

 

+ Comment by valerad12
Среда, 5 ноября 2008 @ 21:20

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

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

 

+ Comment by Имя
Среда, 18 февраля 2009 @ 22:18

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

 

+ Comment by o_rey@livejournal
Четверг, 19 марта 2009 @ 00:43

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

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

 

+ Comment by bullgare
Вторник, 24 марта 2009 @ 16:12

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

 

+ Comment by Alexander
Четверг, 27 августа 2009 @ 19:30

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

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

 

+ Comment by Sergey
Суббота, 19 сентября 2009 @ 18:13

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

 

+ Comment by Константин
Воскресенье, 29 ноября 2009 @ 08:18

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

 

+ Comment by Дядя Вова
Суббота, 9 января 2010 @ 07:55

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

 

Or use your OpenID:

Поиск по сайту