Хотелось бы самому написать небольшой скрипт поиска по БД. Все это хорошо реализовать с помощью LIKE %abc%, но как быть с релевантностью? Чтобы полное совпадение было на первом месте, все остальные - на втором. Совет нужен в одном - для этого используется 2 запроса ? Один с точным значением, второй - с LIKE? Или можно как-то все это объединить в 1 запрос?
Для таких целей, лучше бы использовать что-то вроде Sphinx-а, вместо LIKE - MATCH AGAINST (зависит от определенных условий). Да и статей по этой теме уже достаточно. Вот, например, "Релевантный поиск на php"
Я такой велик делал. Ход работы:
Под «подготовить таблицу к поиску» я подразумеваю вынос всего контента, по которому мы будем искать, в отдельное поле и правильную расстановку индексов. К примеру, у записи есть заголовок и содержание. Новый столбец обзовем searchable
. Значит, в новом поле будет храниться CONCAT_WS(" ", "заголовок", "содержание")
:
`title` : "Трое в лодке, не считая собаки"
`content` : "Представляет собой отчет о лодочной поездке по реке Темзе между Кингстоном и Оксфордом..."
->
`searchable` : "Трое в лодке, не считая собаки Представляет собой отчет о лодочной поездке по реке Темзе между Кингстоном и Оксфордом..."
Это нужно для оптимизации процесса сортировки.
Преобразование поисковой фразы в слова я делал таким образом, чтобы предлоги и частицы (1-3 буквы; в зависимости от языка) сливались со следующим словом. Это, на мой взгляд, лучший вариант, так как в тексте предлоги и частицы в основном относятся с последующему за ними слову. Но и проблема тоже есть: союз «и», к примеру, нельзя отнести ни к предыдущему, ни к последующему слову. Поэтому, я вырезал все «пустые» слова из поисковой фразы (для каждого языка). Ну и, конечно, знаки препинания — тоже вырезал. Получал, такие «слова»: "Трое в лодке, не считая собаки" -> "Трое | в лодке | не считая | собаки". Или "mysql search in table" -> "mysql | search | in table".
Еще очень важно для нормального поиска — знать, какая часть слова не имеет важности: это приставка и окончание. Суффикс тоже можно убрать. То есть, для нормального-ala-гугол-поиска желательно брать только корни слов. Для этого понадобится какой-то скрипт, который бы смог выдергивать морфемы. И это, хотя и достаточно сложно, но гуглится.
Искать проще всего:
<?
$words = search_query_get_words('Трое в лодке, не считая собаки');
// $words : array('Трое', 'в лодке', 'не считая', 'собаки');
$query = 'SELECT * FROM `table` WHERE ';
$query .= '`searchable` LIKE "%' . implode('%" OR `searchable` LIKE "%', $words) . '%"';
?>
Ищем по одному только полю — сгенерированному ранее <code>searchable</code>.
Хитрость заключается в том что, если уже предусмотрен морфологический разбор слов, то колонка searchable
должна хранить не тупую склейку заголовка и содержания, а только уникальные корни слов из полей title
и content
— это сэкономит и объем физической памяти, занимаемой таблицей, и, что важнее — скорость выполнения запроса.
Морфологический разбор слов текста нужно будет производить значительно реже — при редактировании или добавлении новой статьи. Это значительно проще, чем при каждом поиске пытаться адаптировать каждое запрошенное слово с учетом всех склонений-падежей.
Самое главное — правильно отсортировать. Я прикинул, что самым важным в определении того, насколько результат соответствует запросу, является расстояние в символах между найденными словами. Но это оказалось и самым сложным, поэтому я опустил это. Вторым по важности я считаю количество слов поисковой строки, присутствующих в записи. А вот количество вхождений одного и того же слова в записи — наименее значимо.
Кроме того, очень важно, где именно найдены слова: в заголовке или в содержании.
Итак, есть 2 фактора:
Получить количество несложно:
<?
$words_in_field = '';
foreach ($words as $word)
$words_in_field .= ' + IF(`searchable` LIKE "%'.$word.'%", 1, 0)';
$words_in_field = substr($words_in_field, 3);
$query = 'SELECT * FROM `table` WHERE '. $words_in_field .' > 0 ORDER BY '. $words_in_field. ' DESC';
?>
Получить количество вхождений можно вот как:
mysql> SELECT (LENGTH("Шел-шел, да упал") - LENGTH(REPLACE(LOWER("Шел-шел, да упал"), "шел", ""))) / LENGTH("шел")
Итак, есть результаты по двум «факторам» и есть 2 «настоящих» колонки title
и content
. Нужно определиться, какое поле важнее. Я считаю, что заголовок — самое важное. Предположим, у нас есть 2 записи (статьи). При запросе, состоящем из 4 слов найдется:
В этом случае в начале выдачи нужно показать «Вторую статью», а ниже — «Первую статью». Значит, колонкам нужно указать веса. Пусть это будут 1 и 0.5 для title
и content
соответственно. При подсчете индекса соответствия нужно умножать количество найденных слов на вес данной колонки.
Конечно, этим не обойтись. Нужно предусмотреть и упростить процесс поиска одного-единственного слова. Рано или поздно встанет вопрос о том, как же сортировать одинаковые по релевантности результаты (ага: PR, тИЦ) или как сделать так, чтобы таких ситуаций не возникало...
Конечно, для огромного объема данных этот вариант очень слаб. Но для внутреннего поиска на средненьком сайте этого вполне достаточно.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Здравствуйте! Помогите, пожалуйста, с запросомЕсть таблица Orders и Locations, между ними связь many-to-one
Подскажите как работать в C# с MysqlЧто подключать, как подключатся к mysql и как искать в бд