Морфологічний пошук російською мовою на php і mysql, статті, sono

Важливою функцією на сайті є пошук по розділу або по всьому сайту. Це дозволяє користувачеві швидко знайти необхідну інформацію. Найчастіше це питання вирішується за допомогою повнотекстового пошуку або пошуку по базі через вираз LIKE. Повнотекстовий пошук зручний тим, що перекладає питання ранжування сторінок за релевантністю на СУБД, дозволяє шукати фрази.

Але разом з тим накладає деякі обмеження, зокрема, змушує використовувати таблиці типу MyISAM, що не здійснює пошук слів, коротше 4-ох символів, створює додаткові індекси. Пошук через вираз LIKE менш зручний для розробника, так як працює повільніше, ускладнює пошук фраз. Загальним недоліком обох підходів є неможливість морфологічного пошуку. Наприклад, в каталозі товарів прісутствет товар "Букет з жовтих троянд", при пошуку по фразі "червоні троянди" користувач не побачить цей товар в результатах. А хотілося б, щоб машина хоч трохи розуміла природну мову. Вирішити цю проблему нескладно на PHP і в цьому нам допоможе phpMorphy. phpMorphy дозволяє вирішувати наступні завдання:

  • Лематизації (отримання нормальної форми слова)
  • Отримання всіх форм слова
  • Отримання граматичної інформації для слова (частина мови, відмінок, відмінювання і т.д.)
  • Зміна форми слова відповідно до заданих граматичними характеристиками
  • Зміна форми слова за заданим зразком

Підтримуються різні кодування:

  • все однобайтові (windows-1251, iso-8859- * і т.п.)
  • Unicode кодування - utf-8, utf-16le / be, utf-32, ucs2, ucs4.

Слід врахувати що використання цієї бібліотеки створить деяку додаткове навантаження на сервер при ініціалізації і роботі.

Для початку скачайте архів бібліотеки і розпакуйте його, наприклад, в папку "phpmorphy". По можливості в директорії недоступною web сервера. Далі, скачайте словники з певною мовою і кодуванням і розпакуйте їх в папку "phpmorphy / dicts" даної бібліотеки. Для ініціалізації бібліотеки підключіть файл common.php з папки "phpmorphy / src" і виставите настройки:

Для пошуку ми будемо використовувати наступний підхід: слова в пошуковій фразі будемо приводити до їх початковій формі, індексувати контент для пошуку також, приводячи окремі слова до початкової формі, і в залежності від частоти повторення і місцезнаходження слова - задавати йому вагу, далі - шукати по таблиці індексів пошукову фразу і ранжувати результати за вагою.

Для початку проіндексуємо контент, за яким буде відбуватися пошук. У нашому прикладі пошук відбувається за кольорами в каталозі товарів. У товару є ідентифікатор, найменування, короткий і довгий описи. Кодування - UTF-8, collate - utf8_bin. Створимо таблицю з товарами:

і таблицю, в якій будуть зберігатися індекси:

тут `word` - ключове слово,` prod_id` - ідентифікатор товару, `weight` - вага слова. Кожен запис характеризується унікальною парою слово-товар.

Далі візьмемо з бази всі записи про товари:

При роботі з бібліотекою PhpMorphy cледует пам'ятати, що терміни в словниках зберігаються в верхньому регістрі, для зручності переведемо контент в верхній регістр. Також можлива поява слів з буквою "е". collate utf8_bin гарантує нам відсутність помилок, пов'язаних з унікальністю ключів в таблиці `prod2search`. так як літери "е" і "е" не вважатимуться одним символом, проте буде неправильним якщо слова "ЗЕЛЕНИЙ" і "ЗЕЛЕНИЙ" будуть індексуватися окремо. Тому ми замінюємо букву "е" на "е". У детальному описі можуть міститися html-теги, їх ми не будемо індексувати і від них слід позбутися.

Тут наведемо скрипт, що індексує товари, а далі дамо пояснення.

Дане регулярний вираз розбиває контент на окремі слова. Для кирилиці використовується модифікатор "u". Слід пам'ятати, що буква "е" не попадає в діапазон "а-я". Регулярний вираз коректно обробить такий рядок:

Масив $ word_pma [1] містить список окремих слів.

під $ morphy ми маємо на увазі екземпляр класу phpMorphy. $ Morphy-> lemmatize () призводить слово в рядку або масив слів до початкової формі і повертає масив виду "Початкове слово" => Масив зі списком слів в початковій формі. Метод lemmatize () за умовчанням порівнює слово за словниками, при негативному результаті намагається утворити початкову форму за внутрішніми правилами, якщо і це не вдається замість масиву повертається FALSE. У нашому прикладі ми використовували тільки російський словник, але вибирали також і англійські слова. Тому при неможливості утворити початкову форму ми індексували входить слово:

повторюється в коді кілька разів тому що, нам необхідно виставити різну вагу словами в залежності від їх місцезнаходження. Словам в заголовку ми задаємо вага 3, словами в короткому описі - 2, в повному - 1. При повторі слова збільшуємо його вага.

В результаті у нас виходить масив $ word для одного товару, який має вигляд "Слово" => "Вага". Ці дані заносимо в таблицю `prod2search`:

Скрипт ще можливо поліпшити, наприклад, заборонити індексування певних прийменників; індексувати певні частини мови, наприклад, тільки іменники і прикметники; виключати пошук за словами, які присутні більш ніж в половині записів, наприклад слово "опис" входить в повний опис кожного товару і пошук по цьому слову і ніяк не характеризує конкретний товар.

Даний скрипт дозволяє з нуля проіндексувати весь контент, і служить лише додатковою утилітою. У реальному додатку необхідно індексувати текстовий контент тільки при додаванні або редагуванні конкретної записи. Для індексації контенту скористаємося наведеними вище скриптом, але тільки не будемо використовувати цикл і будемо працювати з одним записом. При редагуванні, до внесення індексів в таблицю `prod2search` або при видаленні товару - видаляти неактуальні записи:

де $ prod_id - ідентифікатор редагованого або видаляється товару.

Можна провести певну оптимізацію. Припустимо, крім найменування, короткого і повного опису товар володіє ще кількома характеристиками, і при редагуванні користувач змінює тільки ціну. В цьому випадку індексований контент не змінився і не потрібно його повторна індексація. Цього можна уникнути порівнявши хеши индексируемого контенту до і після редагування товару. Зіллємо індексовані поля в один рядок і отримаємо її хеш через md5 ($ str). далі вставимо отримане значення в форму і передавши дані на сервер порівняємо хеш до і після редагування. Якщо хеш-кодування співпадуть, значить контент не змінювався і не потрібно його переіндексація.

Після того як проіндексований весь контент і таблиця `prod2search` заповнена даними можна приступати до реалізації пошуку.

Розберемо запит. Ми знаходимо записи в таблиці `prod2search`. в яких слова зі списку слів в пошуковій фразі WHERE `s`.`word` IN (". implode ( ",", $ sql). "). При пошуку по кільком словами, кілька слів відносяться до одного і того ж товару, щоб товари в результаті не дублювати ми групуємо результат по товару GROUP BY `s`.`prod_id`. У цьому ж випадку ми підсумовуємо вага знайдених слів для одного товару SUM ( `weight`) AS` weight_sum`. Наприклад, ми шукаємо «червоні троянди» - у товару, у якого обидва слова зустрічаються в описі, вага буде більше, ніж у товару з одним словом. Результати вибірки ми ранжируємо за релевантністю, тобто вазі ORDER BY `weight_sum` DESC. Для того щоб при пошуку декількох слів в результаті ми отримали тільки ті товари, в описі яких зустрічаються ВСЕ потрапили в запит слова, ми враховуємо скільки слів для одного товару знайдено COUNT ( `s`.`prod_id`) AS` num` і ставимо умову HAVING `num` =" .count ($ sql). ". В даному випадку реалізується логічне «І», що з'єднують слова в пошуковій фразі, для логічного «АБО» умова слід упустити. У підсумку ми отримуємо список ідентифікаторів товарів, які підходять під критерій пошуку. Для наочності записи з товарами ми беремо вкладеним запитом, однак на практиці, можливо, найкращим рішенням буде взяти список товарів окремим запитом по їх ідентифікаторів. Варіант запиту з логічним «АБО» і лімітами буде працювати значно швидше.

Таким чином, на прикладі каталогу кольорів ми реалізували морфологічний пошук російською мовою з використанням PHP, MySQL і PhpMorphy. Даний метод можна застосувати до будь-яких розділах вашого сайту і зблизити відвідувача і вас, просто видавши користувачеві інформацію, яку він шукав.

Схожі статті