Рецепты программирования на PHP

  Главная   Учебник   Статьи   FAQ   Книги   Ссылки  

Собираем поисковый механизм на PHP/MySQL

Денис Рощин denis@komkon.org

"Основная проблема ЭВМ -
Вводишь мусор, получаешь мусор"

Дик Фейнман

Итак, у вас полностью динамический сайт, на котором находится большое количество различных данных (любого вида - форумы, статьи и т.п.). Преимущественно большие данные хранятся в BLOBах (чего и вам наверное не удалось избежать), следовательно невозможно сделать ничего полезного, используя стандартный вид запроса LIKE %searchword% так как вывод не будет соответствующим (то есть релевантным).

Должен быть другой путь. И он есть :-).

Шаг один: Редукция "посторонних" слов из blob'а

Первая проблема заключается в том, что данные переполнены посторонними словами (предлогами, междометиями..), такими как "как, где, а, и". Эти слова помогают нам, людям, общаться но не имеют ничего общего с нашей проблемой, когда нужно получить вывод по релевантности.

Ниже, в конце статьи, я приложил мой личный список таких, "посторонних", слов.

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

+-----+---------------+
| qid | word          |
+-----+---------------+
| 6   | links         |
| 5   | Fire          |
| 5   | topics        |
| 5   | related       |
| 5   | Shakespeare   |
| 4   | people        |
| 4   | Knowpost      |
| 3   | cuba          |
| 3   | cigar         |
+-----+---------------+

Так, давайте создадим собственно табличку:

CREATE TABLE search_table(
word VARCHAR(50),
qid INT)

Следующим шагом будет - обработать и переместить данные в нашу таблицу search_table.

<?php
$query = "SELECT blob,identifier FROM your_table";
$result = mysql_query($query);
$number = mysql_numrows($result);
$j = 0;
WHILE ($j < $number) {
/* Наш "blob" */
$body = mysql_result($result,$j,"blob");
/* Наш "identifier */
$qid = mysql_result($result,$j,"qid");
/* Открыть файл с посторонними словами в массив */
$noise_words = file("noisewords.txt");
$filtered = $body;
/* Помещаем пробел перед первым словом */
$filtered = ereg_replace("^"," ",$filtered);
/* Теперь мы избавились от ненужных слов и
можем поместить то, что осталось - в массив
*/
/* Пробегаем циклом и удаляем неправильные слова */
for ($i=0; $i < count($noise_words); $i++) {
$filterword = trim($noise_words[$i]);
$filtered =
eregi_replace(" $filterword "," ",$filtered);
}
$filtered = trim($filtered);
$filtered = addslashes($filtered);
$querywords = ereg_replace(",","",$filtered);
$querywords = ereg_replace(" ",",",$querywords);
$querywords = ereg_replace("\?","",$querywords);
$querywords = ereg_replace("\(","",$querywords);
$querywords = ereg_replace("\)","",$querywords);
$querywords = ereg_replace("\.","",$querywords);
$querywords = ereg_replace(",","','",$querywords);
$querywords = ereg_replace("^","'",$querywords);
$querywords = ereg_replace("$","'",$querywords);
/* Теперь мы должны иметь что-то типа
'Word1','Word2','Word3'
так что теперь мы можем загнать все в массив
*/
$eachword = explode(",", $querywords);
/* наконец-то мы можем пробежаться по
массиву и поместить каждое слово в базу данных,
вместе со счетчиком
*/
for ($k=0; $k < count($eachword); $k++) {
$inputword = "INSERT INTO search_table
VALUES($eachword[$k],$qid)";
mysql_query($inputword);
}
/* Пробежаться по циклу еще разок с новыми данными */
$j++;
}
?>

Этот скрипт обрабатывает ваши старые данные. Необходимо добавить подобную же обработку на добавление данных в вашу базу данных (когда её вводит пользователь, хозяин - или кто угодно) - так, чтобы база обновлялась в процессе.

Шаг два: Поиск в таблице

Теперь мы имеем таблицу с ключевыми словами и счетчиками. Как собрать запрос?

Во-первых, необходимо переформатировать (нет, не жесткий диск) - слова поиска в строку вида 'word1','word2','word3' и записать её в $querywords.

Далее использовать подобный ниже следующему запрос:

SELECT count(search_table.word) as score, search_table.qid,your_table.blob
FROM search_table,your_table
WHERE your_table.qid = search_table.qid AND search_table.word
IN($querywords)
GROUP BY search_table.qid
ORDER BY score DESC";

Вывод же может быть, например, таким:

<?php
$getresults = mysql_query($search);
$resultsnumber = mysql_numrows($getresults);
IF ($resultsnumber == 0) {
PRINT "Ничего не найдено. "
."Попробуйте использовать другие ключевые слова.";
} ELSEIF ($resultsnumber > 0) {
PRINT "Поиск вернул $resultsnumber результатов<BR>"
     ."Расположение по релевантности <BR><BR>";
for($count = 0; $count < $resultsnumber; $count++) {
$body = mysql_result($getresults,$count,"blob");
$qid = mysql_result($getresults,$count,"qid");
$body2print = substr($body, 0, 100);
$cnote = $count+1;
PRINT "$cnote. <a href=yourcontent.php3?qid=$qid> "
     ."<i>$body2print...</i></a><BR>";
}
}
?>

Итак, у вас есть механизм поиска по ключевым словам в вашей базе данных по релевантности (актуальности запросу).

Конечно, это не Yandex и не Google :-))).

Но у нас теперь есть небольшой поисковый механизм, который вполне быстро и грамотно работает и вполне подходит обычному пользователю (который не собирается использовать логические элементы и т.п.).

Вот лист моих "посторонних" слов:

noisewords.txt
--------------
a 
about 
after 
ago 
all 
almost 
along 
also 
am 
an 
and 
answer 
any 
anybody 
anywhere 
are 
aren't 
around 
as 
ask 
at 
bad 
be 
been 
before 
being 
best 
better 
between 
big
but 
by 
can 
can't 
come 
could 
couldn't 
day 
did 
didn't 
do 
does 
don't 
down 
each 
either 
else 
even 
ever 
every 
everybody 
everyone 
far 
find
for 
found 
from 
get 
go 
going 
gone 
good 
got 
had 
has 
have 
haven't 
having 
her 
here 
hers 
him 
his 
home 
how 
href 
I 
if 
in 
into 
is 
isn't 
it 
its
know 
large 
less 
like 
little 
looking 
look 
many 
me 
more 
most 
must 
my 
near 
never 
new 
news 
no 
none 
not 
nothing 
of 
off 
often
old 
on 
once 
only 
or 
other 
our 
ours 
out 
over 
page 
please 
question 
rather 
recent 
she 
should 
sites 
small 
so 
some 
something 
sometime 
somewhere 
than 
true 
thank 
that 
the 
their
theirs 
them 
then 
there 
these 
they 
this 
those 
though 
through 
thus 
time 
times
to 
too 
under 
until 
untrue 
up 
upon 
use 
users 
version 
very
via 
want 
was 
way 
web 
were 
what 
when 
where 
which 
who 
whom 
whose 
why 
wide 
will 
with 
within 
without 
world 
worse 
worst 
would 
www 
yes 
yet 
you 
your 
yours 
Hosted by uCoz