Наконец-то заработал поиск

Статья более не актуальна, так как относилась к старому, самописному движку.

Вот я и сделал наконец поиск по сайту, вообще еще много чего надо сделать, а то я даже страницы добавляю через MySQL. Сейчас поиск ищет по заголовку, описанию и собственно телу страницы, самое приятное в нем то, что в нем есть ранжирование результатов. Если кому-то интересно как такое сделать то читайте дальше.

MATCH() … AGAINST() первое, что приходит в голову, когда говорят о поиске с релевантностью, забудьте об этом бреде!!! Во-первых, это накладывает очень два ограничений: первое и главное — надо делать полнотекстовые индексы, без них никак, второе — индексы можно сделать только на таблицах MyISAM. Во вторых это просто глючно, потому что при поиске небольших фраз по большой базе их релевантность близка к абсолютному нулю, или же если слово присутствует в более 50% строк, то оно не учитывается.


SELECT 
MATCH('Content') AGAINST ('keyword1 keyword2') as Relevance 
FROM table 
WHERE MATCH ('Content') AGAINST('+keyword1 +keyword2' IN BOOLEAN MODE) 
HAVING Relevance > 0.2 
ORDER BY Relevance DESC

Это так сказать классический способ поиска, взятый с сайта самого мускула, но ИМХО это большая ошибка, тут первый MATCH выдает релевантность, а второй который IN BOOLEAN MODE используется для поиска обоих слов сразу в одной строке, если есть только одно то он выдаст 0. Если слов больше двух то это начинает давать сильную погрешность, выдавая только те ряды, где есть все слова фразы. Более того, куча MATCH сильно тормозит запрос.

Поэтому лучше ввести весовые коэффициенты в запрос. Коэффициенты вы раздаете сами, поэтому их общее число всегда может быть в пределах 100 и к нему можно смело добавлять знак %. Итак у нас есть три колонки title, description и text. Если в каждом из трех полей есть искомое словосочетание то релевантность 100%, при этом каждое поле имеет свой вес: заголовок — самый большой(40%), потом описание(20%) а потом уже текст(10%). Я считаю, что полное словосочетание это 70% релевантности и если есть все слова из него, то это еще 30%. немного запутал, сейчас объясню. Есть словосочетание «мама мыла раму», если оно дословно есть во всех полях то это 70%, а если в каждом из них есть «раму мыла мама», то это только 30%. В результате мы получаем либо 100% (70% все вместе и 30% каждое по отдельности) совпадение со строкой либо только 30% (каждое по отдельности) совпадение, ну это лично мое мнение.

Итак алгоритм будет выгладить следующим образом:


$text = "мама мыла раму";
$query = "SELECT *, 
IF (title like '%".$text."%', 40, 0) + IF (title LIKE '%".str_replace(" ", "%', 5.71, 0) + IF (title LIKE '%", $text)."%', 5.71, 0) + 
IF (description like '%".$text."%', 20, 0) + IF (description LIKE '%".str_replace(" ", "%', 2.86, 0) + IF (description LIKE '%", $text)."%', 2.86, 0) +
IF (text like '%".$text."%', 10, 0) + IF (text LIKE '%".str_replace(" ", "%', 1.43, 0) + IF (text LIKE '%", $text)."%', 1.43, 0) AS rel
FROM pages 
WHERE 
(title LIKE '%".str_replace(" ", "%' OR title LIKE '%", $text)."%') OR
(description LIKE '%".str_replace(" ", "%' OR description LIKE '%", $text)."%') OR
(text LIKE '%".str_replace(" ", "%' OR text LIKE '%", $text)."%') 
ORDER BY rel DESC";

Если подставить значения будет выглядеть менее запутано но все равно непонятно


SELECT *, 
IF (title LIKE '%мама мыла раму%', 40, 0) + IF (title LIKE '%мама%', 5.71, 0) + IF (title LIKE '%мыла%', 5.71, 0) + IF (title LIKE '%раму%', 5.71, 0)  + 
IF (description LIKE '%мама мыла раму%', 20, 0) + IF (description LIKE '%мама%', 2.86, 0) + IF (description LIKE '%мыла%', 2.86, 0) + IF (description LIKE '%раму%', 2.86, 0)  + 
IF (text LIKE '%мама мыла раму%', 10, 0) + IF (text LIKE '%мама%', 1.43, 0) + IF (text LIKE '%мыла%', 1.43, 0) + IF (text LIKE '%раму%', 1.43, 0) AS rel
FROM pages 
WHERE 
(title LIKE '%мама%' OR title LIKE '%мыла%' OR title LIKE '%раму%') OR
(description LIKE '%мама%' OR description LIKE '%мыла%' OR description LIKE '%раму%') OR
(text LIKE '%мама%' OR text LIKE '%мыла%' OR text LIKE '%раму%')
ORDER BY rel DESC

Вот такой немаленький запросик получился))) Теперь попробуем разобрать что значат все эти непонятные коэффициенты 5.71, 2.86 и 1.43. Для этого я нарисую маленькую табличку.

\ Все мама мыла раму
title 40 5.71 5.71 5.71
description 20 2.86 2.86 2.86
text 10 1.43 1.43 1.43

Сумма первого столбика получается как раз 70%, сумма всех остальных ячеек 30% в результате искомые 100% релевантности. Сумма каждого следующего столбика 10%. А сумма каждой ячейки высчитывается по формуле (вес поля) * (общее число процентов 30) / (общее число коэффициентов 7) / (количество слов), если подставить поле title то выйдет 4*30/7/3 = 5.71. В общем, я думаю, поняли, кто не понял, возьмите калькулятор!

В том что такой запрос отрабатывает весьма шустро вы можете перейдя на страницу поиска, там выводиться время потраченное на запрос.

Кстати в моем поиске еще наложена логика отключения полей, если отключено одно поле то коэффициенты становятся 3 и 4 , а если отключено два поля то оставшееся имеет коэффициент 7. И еще не забудьте удалить пробелы в начале и конце строки, а также двойные пробелы в середине. Все!

2 Комментарии “Наконец-то заработал поиск

Комментарии закрыты