Поиск по сайту - статичный контент (Perl)

www.karman.com.ua
портал о хостинге в Украине
Хостинг + Украина = Karman.com.ua

Сайт от А до Б

/

Основы сайта

/

Интересные скрипты

/

Изучаем PHP

/

Как заработать на сайте

/

Раскрутка сайта

/

CMS


Поиск по сайту - статичный контент (Perl) 

Еще по теме:
  Opera создает необычный интернет-Поисковик МАМА
  QIP.ru странный Поисковик
  Новый студенческий Поисковик
  wwjd.ru - христианская Поисковая система
  Поиск веб-студий
  Скрытие части контента
  Борьба оптимизаторов и Поисковых систем
  Хэши в Perl

Поиск по сайту, не самый сложный элемент, но довольно муторный. Так не хочется его делать, а надо. Я не буду рассматривать возможности внедрения в сайт поисковых форм Яндекса или Google, про это можно почитать у них самих. Будем делать собственный поиск по сайту.

Итак, что у нас дано:

  • сайт состоящий из статичных страниц;
  • файлы страниц расположены в разных папках различного уровня (у меня CMS собирает ЧПУ);
  • база данных MySQL (не использовать базу данных в поисковой машине - странное занятие, тем более что сейчас базы данных уже не роскошь);

Для того что бы у нас осуществлялся поиск нужно будет собрать "поисковые индексы". Я использую для этого два способа (способов, на самом деле, гораздо больше): простой и немного сложнее. В первом я использую встроенные функции MySQL базы данных, во втором - собственный велосипед.

Определим алгоритм работы скрипта индексирования поисковой машины (основные подпрограммы):

Красным пунктиром выделены стандартные процедуры для обоих способов, процедуры выделенные синим радикально отличаются.

  • Процедура рекурсивного обхода директорий - процедура, последовательно проходящая по файлам и папкам нашего сайта и выбирающая нужные файлы;
  • Процедура обработки файла - процедура, обрабатывающая контент файла;
  • Процедура формирования данных - обработанный контент собирается в блок данных для переноса в базу данных;
  • Процедура обновления базы данных - сформированный блок данных заносится в базу данных;

Алгоритм работы скрипта вывода результатов поиска:

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

Какая информация нужна нам для вывода результатов запроса:

  • URL страницы - ссылка на найденную страницу;
  • название страницы - эту информацию мы будем брать из тега </I> страницы;</DIV> <LI> <DIV align=justify><I>краткое описание страницы</I> - эту информацию мы будем брать из мета-тега <I>description</I> страницы.</DIV></LI></UL> <P align=justify>В качестве "подопытного кролика" я выбрал портал <A href="http://www.alfakmv.ru/" target=_blank>АльфаКМВ</A>. Этот ресурс имеет в своем составе немногим более 3000 страниц разной вложенности в папках и можно спокойно оценить скорость работы нашей поисковой системы.</P> <H2>1. Способ первый: использование встроенных функций </H2> <P align=justify>Хоть MySQL считается не особо "навороченной" базой данных (хотя я лично так не считаю), у неё есть неоспоримые плюсы - это простота использования, а основной, в нашем случае, индекс FULLTEXT, который без особых сложностей организует нам прекрасный поиск. нужно просто приложить к этому небольшие усилия:</P> <P><B>1.1. Организация таблицы</B></P> <P align=justify>Индексная таблица состоит всего из четырех полей - ссылка на страницу (url), заголовок страницы (title), описание страницы (description) и текстовая часть (полнотекстовый индекс):</P> <DIV><FONT face="Courier New, Courier, mono"><B><FONT color=#9900ff>CREATE TABLE</FONT></B> <FONT color=#0000ff>`search`</FONT> (<BR> <FONT color=#0000ff>`url`</FONT> varchar(250) <B> <FONT color=#9900ff>NOT </FONT>NULL</B>,<BR> <FONT color=#0000ff>`title`</FONT> text <FONT color=#9900ff><B> NOT </B></FONT><B>NULL</B>,<BR> <FONT color=#0000ff>`description`</FONT> text <FONT color=#9900ff><B> NOT </B></FONT><B>NULL</B>,<BR> <FONT color=#0000ff>`search`</FONT> text <FONT color=#9900ff><B> NOT </B></FONT><B>NULL</B>,<BR> <B><FONT color=#9900ff>PRIMARY KEY</FONT></B> (<FONT color=#0000ff>`url`</FONT>),<BR> <B><FONT color=#9900ff>FULLTEXT KEY</FONT></B> <FONT color=#0000ff>`s`</FONT> (<FONT color=#0000ff>`search`</FONT>)<BR> ) <B><FONT color=#9900ff>TYPE</FONT></B>=MyISAM;</FONT></DIV> <P><B>1.2. Рекурсия </B></P> <P align=justify>Вторым этапом нам нужно пройтись по всем папкам и файлам сайта для индексации, для чего воспользуемся рекурсией.</P> <P align=justify><I>...<BR>Рекурсия - вызов функции или процедуры из неё же самой (обычно с другими значениями входных параметров), непосредственно или через другие функции (например, функция А вызывает функцию B, а функция B — функцию A). Количество вложенных вызовов функции или процедуры называется глубиной рекурсии. <BR>... <BR>Следует избегать избыточной глубины рекурсии, так как это может вызвать переполнение стека.<BR>...</I></P> <P align=justify>Задумчиво, но так как мы не знаем глубину папок в которых могут лежать файлы сайта, то прийдется использовать её, хотя можно поискать на <A href="http://search.cpan.org/" target=_blank>CPAN</A>, но мне кажется, это лишняя трата времени, быстрее написать самому.</P> <P align=justify>Создаем скрипт, который будет индексировать наш сайт, назовем его <EM>index.pl</EM>.</P> <DIV><FONT face="Courier New, Courier, mono"><FONT color=#66cc00><I>#!/usr/bin/perl<BR># Подключаем основные модули</I></FONT><BR><B><FONT color=#ff0000>use</FONT></B> strict;<BR><B><FONT color=#ff0000>use</FONT></B> warnings;<BR><B><FONT color=#ff0000>use</FONT></B> DBI;<BR><FONT color=#66cc00><I># "Локаль" - обязательно, т.к. кириллицу мы будем использовать и в регах </I></FONT><BR><B><FONT color=#ff0000>use</FONT></B> locale;<BR><B><FONT color=#ff0000>use</FONT></B> POSIX <FONT color=#0000ff>qw(locale_h)</FONT>;<BR> setlocale(LC_CTYPE, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR> setlocale(LC_ALL, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR><BR><I><FONT color=#66cc00># Обозначаем глобальные переменные</FONT></I><BR><FONT color=#ff0000><B>use</B></FONT> vars <FONT color=#0000ff>'$dbh'</FONT>, <FONT color=#0000ff>'$url_start'</FONT>, <FONT color=#0000ff>'$dir_start'</FONT>, <FONT color=#0000ff>'@dir_filter'</FONT>, <FONT color=#0000ff>'@file_type'</FONT>;<BR><I><FONT color=#66cc00># Директория DocumentsRoot сайта</FONT></I><BR> <FONT color=#cc6600>$dir_start</FONT> = <FONT color=#0000ff>'/var/www/</FONT><FONT face="Courier New, Courier, mono"><FONT color=#0000ff>my_sites</FONT></FONT><FONT color=#0000ff>/html'</FONT>;<BR><I><FONT color=#66cc00># Домен сайта</FONT></I><BR> <FONT color=#cc6600>$url_start</FONT> = <FONT color=#0000ff>'http://www.</FONT><FONT face="Courier New, Courier, mono"><FONT color=#0000ff>my_sites</FONT></FONT><FONT color=#0000ff>.ru'</FONT>;<BR><I><FONT color=#66cc00># Фильтр директорий (директории, которые исключаются из индексации)</FONT></I><BR> <FONT color=#cc9900>@dir_filter</FONT> = (<BR> <FONT color=#0000ff>'cgi-bin'</FONT>,<BR> <FONT color=#0000ff>'images'</FONT>,<BR> <FONT color=#0000ff>'temp'</FONT>,<BR> );<BR><I><FONT color=#66cc00># Фильтр файлов (какие расширения файлов индексировать)</FONT></I><BR> <FONT color=#cc9900>@file_type</FONT> = (<BR> <FONT color=#0000ff>'shtml'</FONT>,<BR> <FONT color=#0000ff>'html'</FONT>,<BR> <FONT color=#0000ff>'htm'</FONT>,<BR> );<BR><BR><I><FONT color=#66cc00># Сразу отправляем заголовок браузеру</FONT></I><BR> <B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>"Content-type: text/html; charset=windows-1251 "</FONT>;<BR><BR><I><FONT color=#66cc00># Открываем временный файл для хранения данных</FONT></I><BR> <B><FONT color=#ff0000>open</FONT> </B>(TMP, <FONT color=#0000ff>'>>'</FONT>, <FONT color=#0000ff>'/var/www/my_sites/cgi-bin/search/search.txt'</FONT>); <BR> <B><FONT color=#ff0000>flock </FONT></B>(TMP, 2);<BR><I><FONT color=#66cc00># Передаем управление процедуре рекурсии</FONT></I><BR> <B><FONT color=#ff00ff>&</FONT></B>recursion();<BR><I><FONT color=#66cc00># Закрываем временный файл</FONT></I><BR> <FONT color=#ff0000><B>close </B></FONT>TMP;<BR><BR> <FONT color=#ff00ff><B>&</B></FONT>update_db;<BR><BR> <B><FONT color=#ff0000>print </FONT></B><FONT color=#0000ff>'Индексация завершена!'</FONT>;<BR><B><FONT color=#ff0000>exit</FONT></B>;<BR><BR><B><FONT color=#ff0000>sub</FONT></B> recursion {<BR><I><FONT color=#66cc00># Получаем текущую директорию рекурсии относительно DocumentsRoot</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$postfix</FONT> = <B><FONT color=#ff0000>shift</FONT></B> || <B><FONT color=#ff0000>undef</FONT></B>;<BR><I><FONT color=#66cc00># Формируем абсолютный путь текущей директории</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$dir</FONT> = <FONT color=#cc6600>$dir_start</FONT>.(<FONT color=#cc6600>$postfix</FONT> || <FONT color=#0000ff>''</FONT>);<BR><I><FONT color=#66cc00># Объявляем локальным переменные FOLDER (в основном нам нужен дескриптор*)</FONT></I><BR> <B><FONT color=#ff0000>local</FONT></B> *FOLDER;<BR><I><FONT color=#66cc00># Открываем директорию</FONT></I><BR> <B><FONT color=#ff0000>opendir</FONT></B> (FOLDER, <FONT color=#cc6600>$dir</FONT>);<BR><I><FONT color=#66cc00># И последовательно считываем</FONT></I><BR> <B><FONT color=#ff0000>while</FONT></B> (<B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$item</FONT> = <B><FONT color=#ff0000>readdir</FONT></B> FOLDER) {<BR><I><FONT color=#66cc00># "отсекаем" элементы '.' и '..' что бы не "выскочить" на директорию выше</FONT></I><BR> <FONT color=#ff0000><B>next if</B></FONT> <FONT color=#cc6600>$item</FONT> <B><FONT color=#ff0000>eq</FONT></B> <FONT color=#0000ff>'.'</FONT> || <FONT color=#cc6600>$item</FONT> <B><FONT color=#ff0000>eq</FONT></B> <FONT color=#0000ff>'..'</FONT>;<BR><I><FONT color=#66cc00># Определяем относительный путь</FONT><FONT color=#009900><BR> </FONT></I><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$path</FONT> = (<FONT color=#cc6600>$postfix</FONT> || <FONT color=#0000ff>''</FONT>).<FONT color=#0000ff>'/'</FONT>.<FONT color=#cc6600>$item</FONT>;<FONT color=#009900><I><BR><FONT color=#66cc00># Если элемент списка - директория, то порождаем процедуру вглубь рекурсии</FONT><BR></I></FONT> <B><FONT color=#ff00ff>&</FONT></B>recursion(<FONT color=#cc6600>$path</FONT>) <FONT color=#ff0000><B>if -d</B></FONT> <FONT color=#cc6600>$dir</FONT>.<FONT color=#0000ff>'/'</FONT>.<FONT color=#cc6600>$item</FONT> && !<FONT color=#ff0000><B>map</B></FONT> {<FONT color=#cc6600>$path</FONT> =<FONT color=#009999>~ /^/$_/</FONT>} <FONT color=#cc9900>@dir_filter</FONT>;<FONT color=#009900><I><BR><FONT color=#66cc00># Если элемент списка - файл, то передаем относительный путь к нему в процедуру обработки</FONT></I></FONT><BR> <B><FONT color=#ff00ff>&</FONT></B>file_parse(<FONT color=#cc6600>$path</FONT>) <FONT color=#ff0000><B>if -f</B></FONT> <FONT color=#cc6600>$dir</FONT>.<FONT color=#0000ff>'/'</FONT>.<FONT color=#cc6600>$item</FONT> && <FONT color=#ff0000><B>map</B></FONT> {<FONT color=#cc6600>$path</FONT> =<FONT color=#009999>~ /.$_$/</FONT>} <FONT color=#cc9900>@file_type</FONT>;<BR> }<BR><I><FONT color=#66cc00># Закрываем директорию </FONT></I><BR> <B><FONT color=#ff0000>close</FONT></B> FOLDER;<BR><I><FONT color=#66cc00># ... и возвращаемся </FONT></I><BR> <B><FONT color=#ff0000>return</FONT></B> 1;<BR>}</FONT></DIV> <P align=justify>Как видно - никаких сложностей. Однако хочу заметить, что в глубь рекурсии мы уходим только для директорий, а не символьных ссылок, причем, я бы и не рекомендовал использовать символьные ссылки, чтобы рекурсия не зациклилась во время обработки.</P> <P><B>1.3. Предварительное формирование данных или просто формирование данных </B></P> <P align=justify>Третий этап - подготовка файла и индексации. Так как очень часто на страницах сайта используются SSI внедрения, то их нужно будет включить в основное тело страницы.</P> <P align=justify><I>...<BR>в одном разделе сайта дизайнер внедрил красивый заголовок через SSI, когда поисковая система проиндексировала страницы, то ключевые слова заголовка были пропущены, и поиск осуществлялся "криво"<BR>...</I></P> <DIV><FONT face="Courier New, Courier, mono"><FONT color=#ff0000><B>sub</B></FONT> file_parse {<BR><I><FONT color=#66cc00># Получаем относительный путь к файлу </FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$file</FONT> = <B><FONT color=#ff0000>shift</FONT></B>;<BR><I><FONT color=#66cc00># Открываем файл страницы </FONT></I><BR> <B><FONT color=#ff0000>open</FONT></B> (FILE, <FONT color=#0000ff>"$dir_start$file"</FONT>);<BR><I><FONT color=#66cc00># Объявляем переменную в которой бодем собирать контент </FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$content</FONT>; <BR><I><FONT color=#66cc00># Построчно производим обработку</FONT></I><BR> <B><FONT color=#ff0000>while</FONT></B> (<FILE>) {<BR> <FONT color=#cc6600>$_</FONT> =<FONT color=#009999>~s /<!--#include virtual="(.*?)"-->/&_include_ssi($file ,$1)/eg</FONT>;<BR> <B><FONT color=#ff0000>if</FONT></B> (<FONT color=#cc6600>$content</FONT>) {<FONT color=#cc6600>$content</FONT> .= <FONT color=#cc6600>$_</FONT>} <B><FONT color=#ff0000>else</FONT></B> {<FONT color=#cc6600>$content</FONT> = <FONT color=#cc6600>$_</FONT>}<BR> }<BR><I><FONT color=#66cc00># Закрываем файл страницы </FONT></I><BR> <B><FONT color=#ff0000>close</FONT></B> FILE;<BR><I><FONT color=#66cc00># Обработка контента<BR># Убираем "жесткие" пробелы и пробельные символы</FONT></I><BR> <FONT color=#cc6600>$content</FONT> =<FONT color=#009999>~s / / /gi</FONT>;<BR> <FONT color=#cc6600>$content</FONT> =<FONT color=#009999>~s /[s ]/ /gi</FONT>; <BR><I><FONT color=#66cc00># Выбираем заголовок и описание страницы</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> (<FONT color=#cc6600>$title</FONT>) = <FONT color=#cc6600>$content</FONT> =<FONT color=#009999>~ /<title>(.*)<\title>/i</FONT>;<BR> <B><FONT color=#ff0000>my</FONT></B> (<FONT color=#cc6600>$description</FONT>) = <FONT color=#cc6600>$content</FONT> =<FONT color=#009999>~ /<meta.*description.*content=(.*?)>/i</FONT>;<BR><I><FONT color=#66cc00># Производим "чиску" контента оставляя только символы </FONT></I><BR> <FONT color=#cc6600>$content</FONT> =<FONT color=#009999>~s /[^w-s]/ /g</FONT>;<BR> <FONT color=#cc6600>$content</FONT> =<FONT color=#009999>~s /s{2,}/ /g</FONT>;<BR><BR><I><FONT color=#66cc00># Отправляем обработанный контент, путь, заголовок и описание в процедуру обновления БД</FONT></I><BR> <B><FONT color=#ff00ff>&</FONT></B>update_data(<FONT color=#cc6600>$content</FONT>, <FONT color=#cc6600>$title</FONT>, <FONT color=#cc6600>$description</FONT>, <FONT color=#cc6600>$file</FONT>);<BR><BR> <B><FONT color=#ff0000>return</FONT></B> 1;<BR>}<BR><BR><B><FONT color=#ff0000>sub</FONT></B> _include_ssi {<BR><I><FONT color=#66cc00># Получаем имя HTML файла и имя файла SSI</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> (<FONT color=#cc6600>$file</FONT>, <FONT color=#cc6600>$ssi</FONT>) = <FONT color=#cc9900>@_</FONT>;<BR><I><FONT color=#66cc00># Объявляем переменную - путь к файлу внедряемому через SSI</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$path</FONT>;<BR><I><FONT color=#66cc00># Если файл берется из корня</FONT></I><BR> <B><FONT color=#ff0000>if</FONT></B> (<FONT color=#cc6600>$ssi</FONT> =<FONT color=#009999>~ /^[\/]{2}/</FONT>) {<BR><BR> <FONT color=#cc6600>$path</FONT> = <FONT color=#cc6600>$dir_start</FONT>.<FONT color=#cc6600>$ssi</FONT>;<BR><FONT color=#66cc00><I># "Чистим" двойные "слеши" </I></FONT><BR> <FONT color=#cc6600>$path</FONT> =<FONT color=#009999>~s /([\/]){2,}/$1/g</FONT>;<BR><BR><I><FONT color=#66cc00># Иначе </FONT></I><BR> } <B><FONT color=#ff0000>else</FONT></B> { <BR><I><FONT color=#66cc00># Определяем директорию основного файла </FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> (<FONT color=#cc6600>$path</FONT>) = <FONT color=#cc6600>$file</FONT> =<FONT color=#009999>~ /(.*)[/\].*?/</FONT>;<BR> <B><FONT color=#ff0000>if</FONT></B> (<FONT color=#cc6600>$path</FONT>) {<FONT color=#cc6600>$path</FONT> .= <FONT color=#cc6600>$ssi</FONT>} <B><FONT color=#ff0000>else</FONT></B> {<FONT color=#cc6600>$path</FONT> = <FONT color=#cc6600>$ssi</FONT>}<BR><I><FONT color=#66cc00># "Чистим" двойные "слеши"</FONT></I><BR> <FONT color=#cc6600>$path</FONT> =<FONT color=#009999>~s /([\/]){2,}/$1/g</FONT>;<BR><BR> }<BR><I><FONT color=#66cc00># считываем контент файла</FONT></I><BR> <B><FONT color=#ff0000>open</FONT></B> (SSI, <FONT color=#cc6600>$path</FONT>);<BR><B><FONT color=#ff0000> my</FONT></B> <FONT color=#cc6600>$content</FONT> = <B><FONT color=#ff0000>join</FONT></B>(<FONT color=#0000ff>''</FONT>, <SSI>);<BR> <B><FONT color=#ff0000>close</FONT></B> SSI;<BR><FONT color=#66cc00><I># Возвращаем контент файла</I></FONT><BR> <B><FONT color=#ff0000>return</FONT></B> <FONT color=#cc6600>$content</FONT><BR>}</FONT></DIV> <P align=justify>В данной процедуре, производится обработка контента файла. Хочу заметить, что <a href=http://karman.com.ua/forum/topic10089.html><b>SSI</b></a> я обрабатываю только для директивы <I>include virtual</I>, при этом не проверяю внедряемый файл, если же через <I>include virtual </I>внедряются скрипты или используются дополнительные директивы, то данный код нужно будет соответственно доработать. Так же может возникнуть вопрос, почему я разбиваю скрипт на такие маленькие процедуры, когда, по большому счету, достаточно было бы описать это в одной процедуре - все это только лишь для того что бы облегчить понимание предмета, а последнее вынесение процедуры <I>update_data </I>- потому что дальше способы индексации разнятся между собой. </P> <P><B>1.4. Обновление блока данных </B></P> <P align=justify>В общем, в эту процедуру мы передаем уже практически готовые данные для вставки в базу данных, поэтому: </P> <P>Для варианта с <I>LOAD DATA</I>: </P> <DIV><FONT face="Courier New, Courier, mono"><B><FONT color=#ff0000>sub</FONT></B> update_data {<BR><I><FONT color=#66cc00># Получаем данные </FONT></I><BR><FONT color=#ff0000> <B>my</B></FONT> (<FONT color=#cc6600>$content</FONT>, <FONT color=#cc6600>$title</FONT>, <FONT color=#cc6600>$description</FONT>, <FONT color=#cc6600>$file</FONT>) = <FONT color=#cc9900>@_</FONT>;<BR><I><FONT color=#66cc00># Формируем строку </FONT></I><BR><FONT color=#ff0000> <B>my</B></FONT> <FONT color=#cc6600>$line</FONT> = <FONT color=#cc6600>$url_start</FONT>.<FONT color=#cc6600>$file</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$title</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$description</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$$content</FONT>.<FONT color=#0000ff>" "</FONT>;<BR><FONT color=#66cc00><I># Записываем строку во временный файл </I></FONT><BR><FONT color=#ff0000> <B>print</B></FONT> TMP <FONT color=#cc6600>$line</FONT>;<BR><FONT color=#ff0000> </FONT><FONT face="Courier New, Courier, mono"><B><FONT color=#ff0000>return</FONT></B></FONT> 1;<BR>}</FONT></DIV> <P>для варианта с <I>INSERT INTO</I>: </P> <DIV><FONT face="Courier New, Courier, mono"><B><FONT color=#ff0000>sub</FONT></B> update_data {</FONT> <FONT face="Courier New, Courier, mono"><BR><I><FONT color=#66cc00># Получаем данные </FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> (<FONT color=#cc6600>$content</FONT>, <FONT color=#cc6600>$title</FONT>, <FONT color=#cc6600>$description</FONT>, <FONT color=#cc6600>$file</FONT>) = <FONT color=#cc9900>@_</FONT>;<BR><I><FONT color=#66cc00># Формируем запрос </FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$update</FONT> = <FONT color=#0000ff>"INSERT INTO wm5_search_one <BR> SET<BR> url = '$url_start$file',<BR> title = '$title',<BR> description = '$description',<BR> search = '$$content'<BR> "</FONT>;<BR><I><FONT color=#66cc00># Выполняем запрос к БД </FONT></I><BR> <FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#cc6600>$update</FONT>);<BR><BR> <B><FONT color=#ff0000>return</FONT></B> 1;<BR><BR>}</FONT></DIV> <P align=justify>Правда, во втором варианте нужно не забыть предварительно подключится к базе данных. </P> <P><B>1.5. Обновление базы данных </B></P> <P align=justify>Можно рассмотреть два варианта обновления данных:</P> <UL> <LI> <DIV align=justify>сформировать временный файл с данными для последующей выгрузки в базу данных с помощью команды <I>LOAD DATA</I>;</DIV> <LI> <DIV align=justify>последовательно вставлять записи с помощью команды <I>INSERT</I>;</DIV></LI></UL> <P align=justify>Если мы обновлять данные будем с помощью <I>LOAD DATA</I>. Информация уже сформирована и требуется только обновить базу данных:</P> <DIV><FONT face="Courier New, Courier, mono"><B><FONT color=#ff0000>sub</FONT></B> update_db {<BR><I><FONT color=#66cc00># Подключаемся к базе данных</FONT></I><BR> <FONT color=#cc6600>$dbh</FONT> = <FONT color=#0000ff>'DBI'</FONT>-><B><FONT color=#ff0000>connect</FONT></B>(<FONT color=#0000ff>'DBI:mysql:database=search;host=localhost;port=3306'</FONT>, <FONT color=#0000ff>'user'</FONT>, <FONT color=#0000ff>'password'</FONT>)<BR> <FONT color=#ff0000><B>or die</B></FONT> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Обнуляем таблицу </FONT></I><BR> <FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#0000ff>'DELETE FROM search;'</FONT>) <FONT color=#ff0000><B><BR> or die</B></FONT> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Загружаем данные</FONT></I><BR> <FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#0000ff>'LOAD DATA INFILE "/var/www/my_sites/cgi-bin/search/search.txt" INTO TABLE search;'</FONT>) <FONT color=#ff0000><B><BR> or print </B></FONT><FONT color=#0000ff>"ERROR!!! $DBI::errstr <br> "</FONT>;<BR><FONT color=#66cc00><I># Отключаемся от базы данных</I></FONT><BR> <FONT color=#cc6600>$dbh</FONT>->disconnect();<BR><FONT color=#66cc00><I># Удаляем временный файл</I></FONT><BR> <B><FONT color=#ff0000>unlink</FONT> </B><FONT color=#0000ff>'/var/www/my_sites/cgi-bin/search/search.txt'</FONT>;<BR> 1;<BR>} </FONT></DIV> <P align=justify>Хочу обратить внимание на то что я указываю абсолютные пути к временному файлу. Это условие обязательное, так как скрипт рассчитывается на запуск с помощью <I>cron</I>.</P> <P align=justify>Теперь рассмотрим особенности обновления данных, с помощью <I>INSERT</I>, и с помощью<I> LOAD DATA</I>. Довольно противоречивое мнение у меня сложилось по поводу выбора способа обновления. С одной стороны команда <I>INSERT</I> очень медленная, но с другой, тратится меньше ресурсов. Я протестировал оба варианта, благо изменения скриптов для этого не большие (вместо дописывания данных во временный файл вставляем запись в таблицу, а процедуру обновления базы данных опускаем). Итак, что получилось:</P> <P align=justify>Тестирование производилось, на одном и том же сайте но на разных серверах (более и менее мощном), сайт все тот же ~3000 статичных страниц:</P> <P align=justify><B><I>Более мощный сервер</I></B> - P4 2.8 (HyperThreading), 800 Mhz FSB, память двухканальная 400 Mhz Kingston 512 MB, Promise UltraDMA133, 2 х 40Gb (Seagate Barracuda) зеркало (на нем сайт) и еще 120 Gb (Maxtor) SATA (на нем ядро SuSE 9.2, MySQL 4.0.18).</P> <UL> <LI> <DIV align=justify>При использовании <I>INSERT</I> индексация производилась в течение 130-140 секунд объем данных таблицы 105'961'780 байт;</DIV> <LI> <DIV align=justify>При использовании<I> LOAD DATA</I> индексация производилась в течение 110-120 секунд (40-50 секунд - формирование временного файла, 60-70 секунд обновление базы данных), размер временного файла - 106'216'954, объем данных таблицы - тот же;</DIV></LI></UL> <P align=justify>Прирост производительности не большой - 15%, но во время обновления базы данных сама база находилась "в трансе", т.е. другие обращения к базе данных происходили с большой задержкой. Отсюда можно сказать - быстрее, но не рациональнее.</P> <P align=justify><B><I>Более слабый сервер</I></B> - P4 2.4, 533 Mhz FSB, память 333 Mhz 1024 MB, 20 Gb (Samsung 7200) на нем ядро Red Hat 7.3, MySQL 4.0.18 и сайт.</P> <UL> <LI> <DIV align=justify>При использовании <I>INSERT</I> индексация производилась в течение 600-900(!) секунд объем данных таблицы 105'961'780 байт;</DIV> <LI> <DIV align=justify>При использовании <I>LOAD DATA</I> индексация производилась в течение 250-300 секунд (100-120 секунд - формирование временного файла, 150-200 секунд обновление базы данных), размер временного файла - 106'216'954, объем данных таблицы - тот же;</DIV></LI></UL> <P align=justify>Конечно, большой разброс по времени дало количество текущих процессов (видимо сказалось отсутствие <I>HyperThreading</I>), но результат показывает, что прирост производительности составил, как минимум 100%, и хотя база данных была дольше "в трансе", но не в таком глубоком (почему - сложно сказать конфиги MySQL идентичны, может большее количество оперативной памяти сказалось).</P> <P align=justify>Итак - решать Вам по какому пити идти, все зависит от сервера, его возможностей и ограничений.</P> <P><B>1.6. Скрипт вывода результатов поиска</B></P> <P align=justify>Вот теперь самое интересное, зачем мы собственно делали столько манипуляций. Я не буду особо расписывать данный скрипт: формировать постраничный вывод, "наводить красоту" и так далее... просто сделаю скелет:</P> <DIV><FONT face="Courier New, Courier, mono"><I><FONT color=#66cc00>#!/usr/bin/perl</FONT></I><FONT color=#66cc00><BR><I># Подключаем основные модули</I></FONT><BR><B><FONT color=#ff0000>use</FONT></B> strict;<BR><B><FONT color=#ff0000>use</FONT> </B>warnings;<BR><B><FONT color=#ff0000>use</FONT></B> DBI;<BR><B><FONT color=#ff0000>use</FONT></B> CGI <FONT color=#0000ff>qw(param)</FONT>;<BR><B><FONT color=#ff0000>use</FONT></B> locale;<BR><B> <FONT color=#ff0000>use</FONT></B> POSIX <FONT color=#0000ff>qw(locale_h)</FONT>;<BR> setlocale(LC_CTYPE, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR> setlocale(LC_ALL, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR><I><FONT color=#66cc00># Получаем поисковый запрос</FONT></I><BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$search</FONT> = param(<FONT color=#0000ff>'search'</FONT>) || <B><FONT color=#ff0000>undef</FONT></B>;<BR><BR><I><FONT color=#66cc00># Сразу отправляем заголовок браузеру</FONT></I><BR><B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>"Content-type: text/html; charset=windows-1251 "</FONT>;<BR><I><FONT color=#66cc00># Форма запроса</FONT></I><BR><B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'<form action='' method=get>'</FONT>;<BR><B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'<input type=text name=search value="'</FONT>.(<FONT color=#cc6600>$search</FONT> || <FONT color=#0000ff>''</FONT>).<FONT color=#0000ff>'">'</FONT>;<BR><B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'<input type=submit value=search>'</FONT>;<BR><B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'</form>'</FONT>;<BR><I><FONT color=#66cc00></FONT></I><I><FONT color=#66cc00># Если запрос пустой, то останавливаем скрипт</FONT></I><BR><B><FONT color=#ff0000>unless</FONT></B> (<FONT color=#cc6600>$search</FONT>) {<B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'Результатов запроса - 0'</FONT>; <B><FONT color=#ff0000>exit</FONT></B>}<BR><I><FONT color=#66cc00># На всякий случай "чистим" полученные данные</FONT></I><BR><FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /[^ws-]/ /g</FONT>;<BR><I><FONT color=#66cc00># "Сжимаем" пробельные символы</FONT></I><BR><FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /s+/ /g</FONT>;<BR><I><FONT color=#66cc00># Подключаемся к базе данных </FONT></I><BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$dbh</FONT> = <FONT color=#0000ff>'DBI'</FONT>-><B><FONT color=#ff0000>connect</FONT></B>(<FONT color=#0000ff>'DBI:mysql:database=search;host=localhost;port=3306'</FONT>, <FONT color=#0000ff>'user'</FONT>, <FONT color=#0000ff>'password'</FONT>)<BR> || <FONT color=#ff0000><B>die</B></FONT> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Формируем запрос </FONT></I><BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sql</FONT> = <FONT color=#0000ff>"SELECT<BR> url, title, description,<BR> MATCH (search) AGAINST ('$search') AS score<BR> FROM search<BR> WHERE MATCH (search) AGAINST ('$search')<BR> LIMIT 50"</FONT>;<BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sth</FONT> = <FONT color=#cc6600>$dbh</FONT>->prepare(<FONT color=#cc6600>$sql</FONT>);<BR><FONT color=#cc6600>$sth</FONT>->execute() || <B><FONT color=#ff0000>die</FONT></B> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Устанавливаем счетчик </FONT></I><BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$i</FONT> = 1;<BR><B><FONT color=#ff0000>while</FONT></B> (<B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$row</FONT> = <FONT color=#cc6600>$sth</FONT>->fetchrow_hashref()) {<BR><I><FONT color=#66cc00># Печатаем строку результата </FONT></I><BR> <B><FONT color=#ff0000>print</FONT></B> <FONT color=#cc6600>$i</FONT>, <FONT color=#0000ff>' - <a href="'</FONT>, <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'url'</FONT>}, <FONT color=#0000ff>'">'</FONT>, <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'title'</FONT>}, <FONT color=#0000ff>'<a><br>'</FONT>,<BR> <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'description'</FONT>}, <FONT color=#0000ff>'<br><br>'</FONT>;<BR> <FONT color=#cc6600>$i</FONT>++<BR>}<BR><FONT color=#cc6600>$sth</FONT>->finish();<BR><FONT color=#66cc00><I># Отключаемся от базы данных</I></FONT><BR><FONT color=#cc6600>$dbh</FONT>->disconnect();<BR><BR><B><FONT color=#ff0000>if</FONT></B> (<FONT color=#cc6600>$i</FONT> == 1) {<B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'Результатов запроса - 0'</FONT>}<BR><B><FONT color=#ff0000>else</FONT></B> {<B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'Результатов запроса - '</FONT>, <FONT color=#cc6600>$i</FONT> - 1}<BR><BR><B><FONT color=#ff0000>exit</FONT></B>;</FONT></DIV> <P align=justify>Практически наш скрипт готов. Он прекрасно отрабатывает полнотекстовый поиск, при этом без особых сложностей.</P> <P><B>1.7. Дополнительные возможности</B></P> <P align=justify>Для начала, (хотя это нужно было сделать в самом начале) ознакомимся с документацией MySQL - <6.8. Полнотекстовый поиск в MySQL> в данном документе сказано, что существует возможность усложнения поискового запроса по индексу, то есть определить "вес" слов, а так же использовать их "усечение". Доработав немного скрипт поиска можно создать "сносную" поисковую машину, которая учитывает морфологию. Для этого сделаем следующее:</P> <P><B><I>а). в переменной поискового запроса заменим два-три последних символа в каждом слове, а так же добавим разрешенные символы:</I></B></P> <DIV><FONT face="Courier New, Courier, mono">...<BR><FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /[^wds-"+~<>]/ /g</FONT>;<BR><FONT color=#66cc00><I># "Сжимаем" пробельные символы</I></FONT><BR><FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /s+/ /g</FONT>;<BR><FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /([wd-]+)[wd-]{2}/$1*/g</FONT>;<BR><FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /s*s/ /g</FONT>;<BR>...</FONT></DIV> <P><B><I>б). в запросе к базе данных укажем IN BOOLEAN MODE:</I></B></P> <DIV><FONT face="Courier New, Courier, mono">...<BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sql</FONT> = <FONT color=#0000ff>"SELECT<BR><FONT face="Courier New, Courier, mono"> </FONT> url, title, description,<BR><FONT face="Courier New, Courier, mono"> </FONT> MATCH (search) AGAINST ('$search' IN BOOLEAN MODE) AS score<BR><FONT face="Courier New, Courier, mono"> </FONT> FROM search<BR><FONT face="Courier New, Courier, mono"> </FONT> WHERE MATCH (search) AGAINST ('$search' IN BOOLEAN MODE)<BR><FONT face="Courier New, Courier, mono"> </FONT> LIMIT 50"</FONT>;<BR>...</FONT></DIV> <P align=justify>Но, хочу сразу оговорится: при использовании <I>BOOLEAN MODE</I> на редкость плохо считается релевантность<B>*</B> и результаты запроса не сортируются, поэтому использовать эту функцию - IMHO не стоит. И на помощь приходит "солдатская смекалка", что нам мешает во время индексации формировать двойной контент с полными словами и с "обрезанными" и так же расширить подобным образом запрос?</P> <P><B><I>а). в скрипте индексации, после "чистки" контента файла:</I></B></P> <DIV><FONT face="Courier New, Courier, mono">...<BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$content2</FONT> = <FONT color=#cc6600>$content</FONT>;<BR><FONT color=#cc6600>$content2</FONT> =<FONT color=#009999>~s /([w-]+)[w-]{2}/$1/g</FONT>;<BR><FONT color=#cc6600>$content2</FONT> =<FONT color=#009999>~s /s[w-]s/ /g</FONT>;<BR><FONT color=#cc6600>$content</FONT> .= <FONT color=#cc6600>$content2</FONT>;<BR>... </FONT></DIV> <P><I><B>б). в поисковом скрипте, после "передачи" формы:</B></I></P> <DIV><FONT face="Courier New, Courier, mono">...<BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$search2</FONT> = <FONT color=#cc6600>$search</FONT>;<BR><FONT color=#cc6600>$search2</FONT> =<FONT color=#009999>~s /([w-]+)[w-]{2}/$1/g</FONT>;<BR><FONT color=#cc6600>$search2</FONT> =<FONT color=#009999>~s /s[w-]s/ /g</FONT>;<BR><FONT color=#cc6600>$search</FONT> .= <FONT color=#0000ff>' '</FONT>.<FONT color=#cc6600>$search2</FONT>;<BR>... </FONT></DIV> <P align=justify>И <I>"о чудо!!!"</I> скрипт начал искать, то что раньше его было не заставить. Конечно имеет смысл еще "поиграть" с количеством обрезаемых символов в словах и формировать не "двойной" поисковый запрос, а "тройной" и более... но это дело техники...</P> <P align=justify>Так же, не нужно забывать о том, что именно ищут пользователи на Вашем сайте. Для того, что бы это определись, достаточно сохранять поисковые запросы пользователей, а потом анализировать их. В соответствии с анализом корректировать контент на страницах для более "правильного" поиска (например: пришлось включить в контент страниц слово <I>"ане<B>г</B>дот"</I>, потому как половина пользователей искала именно его). </P> <P align=justify><FONT color=#666666>*ПРИМЕЧАНИЕ: Это не написано в официальной документации, но как показывает практика, при использовании IN BOOLEAN MODE, отключается критерий поиска фразы, то есть, если в поисковом запросе несколько слов, то они ищутся не как фраза, а каждое слово отдельно, при этом совпадение слова определяется как 1, коэффициент релевантности в итоге получается целое число, варьировать дробной частью которого возможно только "весом" слов, что не приемлемо для большинства пользователей.</FONT> </P> <P><B>1.8. "Грабли", "подводные камни" и немного об оптимизации </B></P> <P>К сожалению, рассказать обо всех нюансах я просто не в состоянии, на это не хватит времени, но какие-то основные описать могу:</P> <UL> <LI> <DIV align=justify>кодировка в базе данных, по умолчанию у меня стоит всегда cp1251, возможно поэтому я не испытывал особо трудностей во время поисков, менять на другую кодировку ради проверки - я не стал;</DIV> <LI> <DIV align=justify>кодировка (локализация) в скрипте CP1251, не всегда эта кодировка по умолчанию установлена на сервере, если будет наблюдаться не адекватное поведение, то её требуется проверить (вообще, насчет кодировки cp1251 - это не панацея, просто я использую её); </DIV> <LI> <DIV align=justify>полнотекстовый индекс по умолчанию индексирует слова размером более 3-х символов (слова из 3-х символов - <I>не индексируются</I>). Если требуется индексировать слова менее 4-х символов, то нужно будет настроить конфиг MySQL, как это сделано, прекрасно описано в главе <<A href="http://dev.mysql.com/doc/mysql/ru/fulltext-fine-tuning.html">6.8.2. Тонкая настройка полнотекстового поиска в MySQL</A>>. В связи с этим можно так же исключать из поля search таблицы слова размер которых менее индексного, для экономии места;</DIV></LI></UL> <P>Вот, собственно, и все, просто и компактно. Пора заняться настоящим "весельем"... :-) </P> <H2>2. Способ второй: "Изобретаем велосипед" или "Пляски с бубнами"</H2> <P align=right><I>... А в PostrgeSQL FULLTEXT нету :(... (Цитата из ЖЖ) </I></P> <P>Достаем из тумбочки старый любимый бубен, разжигаем костер и начинаем готовится к пляске. </P> <P>Сразу хочу сказать, что данное решение мне нравится больше: </P> <UL> <LI>во-первых - используются стандартные инструменты, что позволяет сделать поисковую систему максимально кроссплатформенной; <LI>во-вторых - возможность более "тонкой" настройки поисковой системы в целом и в частности. </LI></UL> <P>По каким критериям производится поиск по сайту:</P> <UL> <LI>совпадение слова - это само собой; <LI>"вес" слова на страницы, то есть количество повторов слова на странице. </LI></UL> <P>При этом я совершенно не учитываю расположение слова на странице и то, находятся ли поисковые слова рядом, или же в разных частях документа. С одной стороны - это плохо, но с другой - мы же не пишем поисковую систему Google, нам нужно найти что-либо в пределах одного сайта, поэтому излишние критерии релевантности - ни к чему, только лишняя головная боль и бесполезная трата ресурсов. </P> <P>Для нашей поисковой системы нужно будет создать три таблицы:</P> <UL> <LI> <DIV align=justify>слова (<I>search_main</I>) - таблица в которой хранятся (<I>раздельно!</I>) все поисковые слова сайта, страница к которой они относятся и их вес;</DIV> <LI> <DIV align=justify>страницы (<I>search_page</I>) - URL, заголовки и описания страницы. Хотя возможно эти данные хранить применительно к каждому поисковому слову, но это тоже лишняя трата ресурсов;</DIV> <LI> <DIV align=justify>фильтр (<I>search_filter</I>) - список слов не включаемых в поисковые - это имена стилей, некоторые теги, операторы JavaScript; в общем, те слова, которые не требуются для поиска.</DIV></LI></UL> <P><B>2.1. Организация таблиц</B></P> <P>Структура таблиц и связей выглядит так:</P> <P><IMG src="http://www.woweb.ru/catalog/116/1077329020/img/3.gif" border=0></P> <P>Команды на создание таблиц:</P> <P><FONT face="Courier New, Courier, mono"><B><FONT color=#9900ff>CREATE TABLE</FONT></B> <FONT color=#0000ff>`search_filter`</FONT> (<BR> <FONT color=#0000ff>`word`</FONT> varchar(100) <B><FONT color=#9900ff>NOT</FONT> NULL</B>,<BR> <FONT color=#0000ff>`note`</FONT> varchar(100) <B>NULL</B>,<BR> <B><FONT color=#9900ff>PRIMARY KEY</FONT></B> (<FONT color=#0000ff>`word`</FONT>)<BR>) <B><FONT color=#9900ff>TYPE</FONT></B>=MyISAM;</FONT></P> <P><FONT face="Courier New, Courier, mono"><B><FONT color=#9900ff>CREATE TABLE</FONT></B> <FONT color=#0000ff>`search_main`</FONT> (<BR> <FONT color=#0000ff>`word`</FONT> varchar(100) <B><FONT color=#9900ff> NOT</FONT> NULL</B> default <FONT color=#0000ff>''</FONT>,<BR> <FONT color=#0000ff>`page`</FONT> int(11) <B><FONT color=#9900ff> NOT</FONT> NULL</B> default <FONT color=#0000ff>'0'</FONT>,<BR> <FONT color=#0000ff>`relevance`</FONT> int(11) <B><FONT color=#9900ff> NOT</FONT> NULL</B> default <FONT color=#0000ff>'0'</FONT>,<BR> <B><FONT color=#9900ff>KEY</FONT></B> <FONT color=#0000ff>`word`</FONT> (<FONT color=#0000ff>`word`</FONT>,<FONT color=#0000ff>`page`</FONT>)<BR>) <B><FONT color=#9900ff>TYPE</FONT></B>=MyISAM;</FONT></P> <P><FONT face="Courier New, Courier, mono"><B><FONT color=#9900ff>CREATE TABLE</FONT></B> <FONT color=#0000ff>`search_page`</FONT> (<BR> <FONT color=#0000ff>`id`</FONT> int(11) <B><FONT face="Courier New, Courier, mono"> </FONT></B> <B><FONT color=#9900ff>NOT</FONT> NULL</B>,<BR> <FONT color=#0000ff>`url`</FONT> varchar(200) <B><FONT color=#9900ff> NOT</FONT> NULL</B> default <FONT color=#0000ff>''</FONT>,<BR> <FONT color=#0000ff>`title`</FONT> varchar(200) <B><FONT color=#9900ff> NOT</FONT> NULL</B> default <FONT color=#0000ff>''</FONT>,<BR> <FONT color=#0000ff>`description`</FONT> text <B><FONT face="Courier New, Courier, mono"> </FONT></B> <B><FONT color=#9900ff>NOT</FONT> NULL</B>,<BR> <B><FONT color=#9900ff>PRIMARY KEY</FONT></B> (<FONT color=#0000ff>`id`</FONT>)<BR>) <B><FONT color=#9900ff>TYPE</FONT></B>=MyISAM;</FONT></P> <P><B>2.2. Предварительное формирование данных или просто формирование данных</B></P> <P align=justify>Не будем возвращаться к рекурсии и обработке файла, так как они идентичны (о чем было сказано выше).</P> <P align=justify>Итак, что мы должны сделать в этой процедуре. Контент практически подготовлен, нужно сформировать 2 блока (файла) данных. Для этого в самом начале <a href=http://karman.com.ua/forum/topic5092.html><b>скрипта</b></a> откроем для последовательной записи (если они не были заранее очищены, то их очищаем) и выберем слова исключения (<I>search_filter</I>). Так же в начале скрипта мы определяем глобальную переменную <I>$i =1 </I>которая будет у нас идентификатором страницы, вот почему мы не указали при создании таблиц автоматических счетчиков. Объясняю почему: </P> <UL> <LI> <DIV align=justify>во-первых, данные вставляются в базу данных не сразу, а после обработки всей информации, а нам нужно будет сразу определять связь слово->страница;</DIV> <LI> <DIV align=justify>во-вторых, даже при последовательном внесении информации в базу данных, прийдется делать дополнительный запрос для определения последнего идентификатора страницы;</DIV> <LI> <DIV align=justify>в-третьих, таблица базы данных пустая, и за уникальность идентификаторов можно не волноваться.</DIV></LI></UL> <DIV><FONT face="Courier New, Courier, mono"><I><FONT color=#66cc00>#!/usr/bin/perl<BR># Подключаем основные модули</FONT></I><BR><B><FONT color=#ff0000>use</FONT></B> strict;<BR><B><FONT color=#ff0000>use</FONT></B> warnings;<BR><B><FONT color=#ff0000>use</FONT></B> DBI;<BR><B><FONT color=#ff0000>use</FONT></B> locale;<BR><B><FONT color=#ff0000>use</FONT></B> POSIX <FONT color=#0000ff>qw (locale_h)</FONT>;<BR> setlocale(LC_CTYPE, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR> setlocale(LC_ALL, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR><I><FONT color=#66cc00># Обозначаем глобальные переменные</FONT></I><BR><B><FONT color=#ff0000>use</FONT></B> vars <FONT color=#0000ff>'$dbh'</FONT>, <FONT color=#0000ff>'$url_start'</FONT>, <FONT color=#0000ff>'$dir_start'</FONT>, <FONT color=#0000ff>'@dir_filter'</FONT>, <FONT color=#0000ff>'@file_type'</FONT>, <FONT color=#0000ff>'$i'</FONT>, <FONT color=#0000ff>'%filter'</FONT>;<BR><FONT color=#66cc00><I># Инициализируем идентификатор страниц</I></FONT><BR><FONT color=#cc6600>$i</FONT> = 1;<BR><I><FONT color=#66cc00># Директория DocumentsRoot сайта</FONT></I><BR><FONT color=#cc6600>$dir_start</FONT> = <FONT color=#0000ff>'/var/www/sites/alfakmv/html'</FONT>;<BR><I><FONT color=#66cc00># Домен сайта</FONT></I><BR><FONT color=#cc6600>$url_start</FONT> = <FONT color=#0000ff>'http://www.alfakmv.ru'</FONT>;<BR><I><FONT color=#66cc00># Фильтр директорий (директории, которые исключаются из индексации)</FONT></I><BR><FONT color=#cc9900>@dir_filter</FONT> = (<BR> <FONT color=#0000ff>'cgi-bin'</FONT>,<BR> <FONT color=#0000ff>'images'</FONT>,<BR> <FONT color=#0000ff>'temp'</FONT>,<BR> );<BR><I><FONT color=#66cc00># Фильтр файлов (какие расширения файлов индексировать)</FONT></I><BR><FONT color=#cc9900>@file_type</FONT> = (<BR> <FONT color=#0000ff>'shtml'</FONT>,<BR> <FONT color=#0000ff>'html'</FONT>,<BR> <FONT color=#0000ff>'htm'</FONT>,<BR> );<BR><I><FONT color=#66cc00># Коннектимся</FONT></I><BR><FONT color=#cc6600>$dbh</FONT> = <FONT color=#0000ff>'DBI'</FONT>-><B><FONT color=#ff0000>connect</FONT></B>(<FONT color=#0000ff>'DBI:mysql:database=search;host=localhost;port=3306'</FONT>, <FONT color=#0000ff>'user'</FONT>, <FONT color=#0000ff>'pass'</FONT>)<BR> || <FONT color=#ff0000><B>die</B></FONT> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Выбираем слова - исключения </FONT></I><BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sql</FONT> = <FONT color=#0000ff>'SELECT word FROM search_filter'</FONT>;<BR><B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sth</FONT> = <FONT color=#cc6600>$dbh</FONT>->prepare(<FONT color=#cc6600>$sql</FONT>);<BR> <FONT color=#cc6600>$sth</FONT>->execute() || <B><FONT color=#ff0000>die</FONT> </B><FONT color=#cc6600>$DBI::errstr</FONT>;<BR> <B><FONT color=#ff0000>while</FONT></B> (<FONT color=#ff0000><B>my</B></FONT> <FONT color=#cc6600>$row</FONT> = <FONT color=#cc6600>$sth</FONT>->fetchrow_hashref()) {<FONT color=#cc6600>$filter</FONT>{<FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'word'</FONT>}} = 1}<BR> <FONT color=#cc6600>$sth</FONT>->finish();<BR><BR><I><FONT color=#66cc00># Очищаем таблицы базы данных</FONT></I><BR><FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#0000ff>'DELETE FROM search_main'</FONT>);<BR><FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#0000ff>'DELETE FROM search_page'</FONT>); <BR><BR><I><FONT color=#66cc00># Сразу отправляем заголовок браузеру</FONT></I><BR><B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>"Content-type: text/html; charset=windows-1251 "</FONT>;<BR><BR><B><FONT color=#ff0000>open</FONT></B> (WORDS, <FONT color=#0000ff>'>>'</FONT>, <FONT color=#0000ff>'/var/www/my_sites/cgi-bin/search/words.txt'</FONT>);<BR> <B><FONT color=#ff0000>flock</FONT></B> WORDS, 2;<BR><BR><B><FONT color=#ff0000>open</FONT></B> (PAGES, <FONT color=#0000ff>'>>'</FONT>, <FONT color=#0000ff>'/var/www/my_sites/cgi-bin/search/words.txt'</FONT>);<BR> <B><FONT color=#ff0000>flock</FONT></B> PAGES, 2;<BR><BR><FONT color=#66cc00><I># Передаем управление процедуре рекурсии</I></FONT><BR> <B><FONT color=#ff00ff>&</FONT></B>recursion();<BR><BR><B><FONT color=#ff0000>close</FONT></B> PAGES;<BR><B><FONT color=#ff0000>close</FONT></B> WORDS;<BR><BR><B><FONT color=#ff00ff>&</FONT></B>update_db;<BR><BR><B><FONT color=#ff0000>exit</FONT></B>; </FONT></DIV> <P><B>2.3. Обновление блока данных </B></P> <P align=justify>Определим основные действия процедуры:</P> <UL> <LI>сформировать строку для блока данных страниц, и записать её в файл; <LI>обработать контент страницы, подсчитать вес слов и сформировать список; <LI>дописать список в блок данных (файл) слов; </LI></UL> <DIV><FONT face="Courier New, Courier, mono"><B><FONT color=#ff0000>sub</FONT></B> update_data {<BR><I><FONT color=#66cc00># Получаем данные</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> (<FONT color=#cc6600>$content</FONT>, <FONT color=#cc6600>$title</FONT>, <FONT color=#cc6600>$description</FONT>, <FONT color=#cc6600>$file</FONT>) = @_;<BR><I><FONT color=#66cc00># Формируем строку блока данных страниц и записываем её в файл</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$line</FONT> = <FONT color=#cc6600>$i</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$url_start</FONT>.<FONT color=#cc6600>$file</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$title</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$description</FONT>;<BR> <B><FONT color=#ff0000>print</FONT></B> PAGES <FONT color=#cc6600>$line</FONT>, <FONT color=#0000ff>" "</FONT>;<BR><I><FONT color=#66cc00># Переводим текст, контент страницы, в нижний регистр</FONT></I><BR> <FONT color=#cc6600>$$content</FONT> =<FONT color=#009999>~tr /A-ZxA8xC0-xDF/a-zxB8xE0-xFF/</FONT>;<BR><I><FONT color=#66cc00># Определяем хеш для подстчета веса слов</FONT></I><BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cccc00>%words</FONT>;<BR> <FONT color=#ff0000><B>foreach my</B></FONT> <FONT color=#cc6600>$word</FONT> (<B><FONT color=#ff0000>split</FONT></B>(<FONT color=#0000ff>' '</FONT>, <FONT color=#cc6600>$$content</FONT>)) {<BR><I><FONT color=#66cc00># Фильтрация слов</FONT></I><BR> <FONT color=#ff0000><B>next if length</B></FONT> <FONT color=#cc6600>$word</FONT> < 3; <FONT color=#66cc00><I># Примечание*</I></FONT> <BR> <FONT color=#ff0000><B>next if exists</B></FONT> <FONT color=#cc6600>$filter</FONT>{<FONT color=#cc6600>$word</FONT>};<BR><I><FONT color=#66cc00># Формируем хеш слов и их вес</FONT></I><BR> <B><FONT color=#ff0000>if</FONT></B> (<B><FONT color=#ff0000>exists</FONT></B> <FONT color=#cc6600>$words</FONT>{<FONT color=#cc6600>$word</FONT>}) {<FONT color=#cc6600>$words</FONT>{<FONT color=#cc6600>$word</FONT>}++} <B><FONT color=#ff0000>else</FONT></B> {<FONT color=#cc6600>$words</FONT>{<FONT color=#cc6600>$word</FONT>} = 1}<BR> }<BR><I><FONT color=#66cc00># Формируем строки блока данных слов</FONT></I><BR> <FONT color=#ff0000><B>foreach my</B></FONT> <FONT color=#cc6600>$word</FONT> (<B><FONT color=#ff0000>keys</FONT></B> <FONT color=#cccc00>%words</FONT>) {<BR> <B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$line</FONT> = <FONT color=#cc6600>$word</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$i</FONT>.<FONT color=#0000ff>" "</FONT>.<FONT color=#cc6600>$words</FONT>{<FONT color=#cc6600>$word</FONT>};<BR> <B><FONT color=#ff0000>print</FONT></B> WORDS <FONT color=#cc6600>$line</FONT>, <FONT color=#0000ff>" "</FONT>;<BR> }<BR><FONT color=#66cc00><I># Обновляем идентификатор страницы </I></FONT><BR> <FONT color=#cc6600>$i</FONT>++;<BR> <B><FONT color=#ff0000>return</FONT></B> 1;<BR>}</FONT></DIV> <P align=justify><FONT color=#666666>*ПРИМЕЧАНИЕ: Цифра 3 как раз и отвечает за размер слова, которые разрешены для индексации </FONT></P> <P><B>2.4. Обновление базы данных </B></P> <P align=justify>Данная процедура просто выгружает в базу данных наши два файла, после чего их удаляет</P> <P><FONT face="Courier New, Courier, mono"><B><FONT color=#ff0000>sub</FONT></B> update_db {<BR><FONT color=#66cc00><I># Загружаем данные</I></FONT><BR> <FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#0000ff>"LOAD DATA INFILE "/var/www/sites/alfakmv/cgi-bin/search2/words.txt" INTO TABLE search_main;"</FONT>) <BR> || <B><FONT color=#ff0000>die</FONT></B> <FONT color=#0000ff>"ERROR!!! $DBI::errstr <br>"</FONT>;<BR> <FONT color=#cc6600>$dbh</FONT>-><B><FONT color=#ff0000>do</FONT></B>(<FONT color=#0000ff>"LOAD DATA INFILE "/var/www/sites/alfakmv/cgi-bin/search2/pages.txt" INTO TABLE search_page;"</FONT>) <BR> || <B><FONT color=#ff0000>die</FONT></B> <FONT color=#0000ff>"ERROR!!! $DBI::errstr <br>"</FONT>; <BR><I><FONT color=#66cc00># Удаляем временные файлы</FONT></I><BR> <B><FONT color=#ff0000>unlink</FONT></B> <FONT color=#0000ff>'/var/www/sites/alfakmv/cgi-bin/search2/words.txt'</FONT>;<BR> <B><FONT color=#ff0000>unlink</FONT></B> <FONT color=#0000ff>'/var/www/sites/alfakmv/cgi-bin/search2/pages.txt'</FONT>;<BR><BR> <B><FONT color=#ff0000>return</FONT></B> 1;<BR>}</FONT></P> <P align=justify>Правда еще хотел оговориться, что при индексации формируются файлы по объему соразмерные с объемом текстовой части сайта, поэтому могут возникнуть проблемы с лимитом дискового пространства на хостинге.</P> <P align=justify>На этом, с индексацией все. Я даже не рассматриваю варианты обновления данных с помощью команд LOAD DATA и INSERT так как, в таблицу слов вставляется записей не на один порядок больше чем в первом варианте с FULLTEXT (~3000 против ~2000000), а таблицу страниц - ровно такое же количество, правда в гораздо меньшем объеме. </P> <P align=justify><B>2.5. Скрипт вывода результатов поиска</B></P> <P align=justify>В данный скрипт особо не отличается от скрипта первого варианта, единственное радикальное различие - запрос к базе данных, он более сложный, так как выборка производится из двух таблиц, условие основано на списке слов поискового запроса и прочие мелочи...</P> <P align=justify><FONT face="Courier New, Courier, mono"><I><FONT color=#66cc00>#!/usr/bin/perl<BR># Подключаем основные модули</FONT></I><BR><B><FONT color=#ff0000>use</FONT></B> strict;<BR><B><FONT color=#ff0000>use</FONT></B> warnings;<BR><B><FONT color=#ff0000>use</FONT></B> DBI;<BR><B><FONT color=#ff0000>use</FONT></B> CGI <FONT color=#0000ff>qw(param)</FONT>;<BR><B><FONT color=#ff0000>use</FONT></B> locale;<BR><B><FONT color=#ff0000>use</FONT></B> POSIX <FONT color=#0000ff>qw(locale_h)</FONT>;<BR> setlocale(LC_CTYPE, <FONT color=#0000ff>'ru_RU.CP1251'</FONT>);<BR> setlocale(LC_ALL, <FONT color=#0000ff>"ru_RU.CP1251"</FONT>);<BR><I><FONT color=#66cc00># Получаем поисковый запрос</FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$search</FONT> = param(<FONT color=#0000ff>'search'</FONT>) || <B><FONT color=#ff0000>undef</FONT></B>;<BR><I><FONT color=#66cc00># Сразу отправляем заголовок браузеру</FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>"Content-type: text/html; charset=windows-1251 "</FONT>;<BR><I><FONT color=#66cc00># Форма запроса</FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'<form action='' method=get>'</FONT>;<BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'<input type=text name=search value="'</FONT>.(<FONT color=#cc6600>$search</FONT> || <FONT color=#0000ff>''</FONT>).<FONT color=#0000ff>'">'</FONT>;<BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'<input type=submit value=search>'</FONT>;<BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'</form>'</FONT>;<BR><I><FONT color=#66cc00># Если запрос пустой, то останавливаем скрипт</FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>unless</FONT></B> (<FONT color=#cc6600>$search</FONT>) {<B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'Результатов запроса - 0'</FONT>; <B><FONT color=#ff0000>exit</FONT></B>}<BR><I><FONT color=#66cc00># На всякий случай "чистим" полученные данные</FONT></I><BR> <FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /[^ws-]/ /g</FONT>;<BR><I><FONT color=#66cc00># "Сжимаем" пробельные символы</FONT></I><BR> <FONT color=#cc6600>$search</FONT> =<FONT color=#009999>~s /s+/ /g</FONT>;<BR><I><FONT color=#66cc00># Подключаемся к базе данных </FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$dbh</FONT> = <FONT color=#0000ff>'DBI'</FONT>-><B><FONT color=#ff0000>connect</FONT></B>(<FONT color=#0000ff>'DBI:mysql:database=search;host=localhost;port=3306'</FONT>, <FONT color=#0000ff>'root'</FONT>, <FONT color=#0000ff>'dfkmrbhbz'</FONT>)<BR> || <B><FONT color=#ff0000>die</FONT></B> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Формируем запрос</FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>my</FONT></B> <FONT color=#cc9900>@search</FONT> = <B><FONT color=#ff0000>split</FONT></B>(<FONT color=#0000ff>' '</FONT>, <FONT color=#cc6600>$search</FONT>);<BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sql</FONT> = <FONT color=#0000ff>"SELECT<BR> t2.url, t2.title, t2.description, SUM(t1.relevance) AS score<BR> FROM search_main AS t1, search_page AS t2<BR> WHERE t1.word IN ('"</FONT>.<FONT color=#ff0000><B>join</B></FONT>(<FONT color=#0000ff>"','"</FONT>,<FONT color=#cc9900>@search</FONT>).<FONT color=#0000ff>"') AND t1.page = t2.id<BR> GROUP BY t1.id<BR> ORDER BY score DESC<BR> LIMIT 50"</FONT>;<BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$sth</FONT> = <FONT color=#cc6600>$dbh</FONT>->prepare(<FONT color=#cc6600>$sql</FONT>);<BR> <FONT color=#cc6600>$sth</FONT>->execute() || <B><FONT color=#ff0000>die</FONT></B> <FONT color=#cc6600>$DBI::errstr</FONT>;<BR><I><FONT color=#66cc00># Устанавливаем счетчик </FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$i</FONT> = 1;<BR><B><FONT face="Courier New, Courier, mono"> </FONT><FONT color=#ff0000>while</FONT></B> (<B><FONT color=#ff0000>my</FONT></B> <FONT color=#cc6600>$row</FONT> = <FONT color=#cc6600>$sth</FONT>->fetchrow_hashref()) {<BR><I><FONT color=#66cc00># Печатаем строку результата </FONT></I><BR><B><FONT face="Courier New, Courier, mono"> </FONT></B> <B><FONT color=#ff0000>print</FONT></B> <FONT color=#cc6600>$i</FONT>, <FONT color=#0000ff>' - <a href="'</FONT>, <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'url'</FONT>}, <FONT color=#0000ff>'">'</FONT>, <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'title'</FONT>}, <FONT color=#0000ff>'<a><br>'</FONT>,<BR> <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'description'</FONT>}, <FONT color=#0000ff>' - '</FONT>, <FONT color=#cc6600>$$row</FONT>{<FONT color=#0000ff>'score'</FONT>},<FONT color=#0000ff>'<br><br>'</FONT>;<BR> <FONT color=#cc6600>$i</FONT>++<BR> }<BR> <FONT color=#cc6600>$sth</FONT>->finish();<BR><FONT color=#66cc00><I># Отключаемся от базы данных</I></FONT><BR><FONT color=#cc6600>$dbh</FONT>->disconnect();<BR><B><FONT face="Courier New, Courier, mono"><BR></FONT><FONT color=#ff0000>if</FONT></B> (<FONT color=#cc6600>$i</FONT> == 1) {<B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'Результатов запроса - 0'</FONT>}<BR><B><FONT color=#ff0000>else</FONT></B> {<B><FONT color=#ff0000>print</FONT></B> <FONT color=#0000ff>'Результатов запроса - '</FONT>, <FONT color=#cc6600>$i</FONT> - 1}<BR><B><FONT face="Courier New, Courier, mono"><BR></FONT><FONT color=#ff0000>exit</FONT></B>;</FONT></P> <P align=justify>Вот и все, совсем все, осталось сравнить эти 2 способа.</P> <H2>3. Сравнение</H2> <P align=justify>Сравнение проводилось на одном и том же сервере, индексировался один и тот же сайт. </P> <TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=1> <TBODY> <TR> <TD align=right width="40%"> </TD> <TD align=middle width="30%">С использованием FULLTEXT </TD> <TD align=middle width="30%">Ручная обработка </TD></TR> <TR> <TD align=right>Объем занимаемых данных (относительно друг друга) </TD> <TD align=middle>1</TD> <TD align=middle>0,43</TD></TR> <TR> <TD align=right>Скорость индексации<BR>(относительно друг друга, средняя величина, индексация с помощью команды FULLTEXT) </TD> <TD align=middle>1</TD> <TD align=middle>0,97</TD></TR> <TR> <TD align=right>Скорость поискового запроса к базе данных </TD> <TD align=middle>0,02 сек<BR>(~3 300 записей)</TD> <TD align=middle>0,31 сек<BR>(~2 300 000 записей)</TD></TR></TBODY></TABLE> <P align=justify>В итоге мы видим, что несмотря на то что объем данных во втором способе гораздо меньше (индекс FULLTEXT довольно объемный), скорость индексации отличается незначительно (если совсем не отличается), а вот запрос для выборки результатов гораздо медленнее. Это связано с гораздо большим количеством записей, и более сложным запросом из двух таблиц. Можно, конечно, во втором способе данные о странице хранить в основной таблице, но при этом объем данных увеличивается в 5-6 раз, а скорость запроса убыстряется всего на 10-15 %, что, впрочем, не актуально. Впрочем, для небольших сайтов оба варианта будут одинаково приемлемы, так как все таки тестирование проводилось на сайте, имеющем более 3000 статичных страниц. </P> <P align=justify>При этом результаты выполнения запроса практически идентичны в обоих случаях, различие было только в порядке вывода (релевантности), что никак не сказывалось на правильности поиска.</P> <H2>Заключение</H2> <P align=justify>Итак мы рассмотрели 2 способа организации поиска по сайту. Следует иметь в ввиду, что поиск осуществляется по статичным страницам сайта и никак не предназначен для динамичных сайтов. Естественно были рассмотрены практически идеальные варианты построения сайта:</P> <UL> <LI>не учтена возможность существования символьных ссылок; <LI>не учтены вариации <a href=http://karman.com.ua/forum/topic10165.html><b>мета тегов</b></a> <I>title</I> и <I>description</I>; <LI>не учтены вариации использования <I>SSI</I>; <LI>не учтена возможность создания фильтров для определенных файлов (кроме как по расширению); <LI>не учтена возможность создания фильтров для определенных вложенных папок (кроме как корневых); <LI>может что-то еще не учтено :)... </LI></UL> <P>но, впрочем, скелет дан, нарастить мясо - на совести программиста... </P><br>Чтобы <b>обсудить это</b> в форуме, <a href="http://karman.com.ua/forum/ipb.html?act=post&do=new_post&f=581">нажмите здесь</a>.</td></tr><!--links--><tr><td colspan=2><br><div class="bkmz"> <span>Добавить в закладки:</span> <noindex> <ul> <li><a class="google" rel="nofollow" title="В закладки на Google" href="http://www.google.com/bookmarks/mark?op=edit&bkmk=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&title=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>google.com</span></a></li> <li><a class="yandex" rel="nofollow" title="В закладки на Яндексе" href="http://zakladki.yandex.ru/userarea/links/addfromfav.asp?bAddLink_x=1&lurl=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&lname=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>yandex.ru</span></a></li> <li><a class="bobr" rel="nofollow" title="Забобрить" href="http://bobrdobr.ru/addext.html?url=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&title=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>bobrdobr.ru</span></a></li> <li><a class="memori" rel="nofollow" title="В Memori.ru" href="http://memori.ru/link/?sm=1&u_data[url]=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&u_data[name]=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>memori.ru</span></a></li> <li><a class="moemesto" rel="nofollow" title="Добавить в Мое Место" href="http://moemesto.ru/post.php?url=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&title=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>moemesto.ru</span></a></li> <li><a class="linkstore" rel="nofollow" title="В закладки на Linkstore.ru" href="http://www.linkstore.ru/servlet/LinkStore?a=add&url=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&title=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>linkstore.ru</span></a></li> <li><a class="rumarkz" rel="nofollow" title="В закладки на Rumarkz.ru" href="http://rumarkz.ru/bookmarks/?action=add&popup=1&address=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&title=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>rumarkz.ru</span></a></li> <li><a class="stozakl" title="Добавить в 100закладок" href="http://www.100zakladok.ru/save/?bmurl=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&bmtitle=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>100zakladok.ru</span></a></li> <li><a class="delicous" rel="nofollow" title="В закладки на Del.icio.us" href="http://del.icio.us/post?url=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html&title=%CF%EE%E8%F1%EA%20%EF%EE%20%F1%E0%E9%F2%F3%20-%20%F1%F2%E0%F2%E8%F7%ED%FB%E9%20%EA%EE%ED%F2%E5%ED%F2%20%28Perl%29%20-%20%D5%EE%F1%F2%E8%ED%E3%20%E2%20%D3%EA%F0%E0%E8%ED%E5" target=_blank><span>del.icio.us</span></a></li> <li><a class="techno" rel="nofollow" title="В закладки на Technorati.com" href="http://www.technorati.com/faves?add=http://karman.com.ua/_poisk_po_sajtu_statichnyj_0_0_0_1220_1.html" target=_blank><span>technorati.com</span></a></li> </ul> </noindex> </div> <br><a href="http://karman.com.ua" target=_blank title="Хостинг в Украине">Хостинг</a>-источник: <a href="http://karman.com.ua" target=_blank title="Хостинг в Украине">http://karman.com.ua</a></td></tr></table><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9700.html>GoGo <span class=sw>Поис</span>ковик</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9654.html>Максимально удобный мониторинг <span class=sw>Поис</span>ковой выдачи</a><br> <!-- Яндекс.Директ --> <script type="text/javascript"> //<![CDATA[ yandex_partner_id = 18534; yandex_site_bg_color = 'FFFFFF'; yandex_site_charset = 'windows-1251'; yandex_ad_format = 'direct'; yandex_font_size = 1.2; yandex_direct_type = 'flat'; yandex_direct_border_type = 'ad'; yandex_direct_limit = 2; yandex_direct_bg_color = 'FABC02'; yandex_direct_border_color = 'BBBBBB'; yandex_direct_title_color = '0000CC'; yandex_direct_url_color = '000000'; yandex_direct_all_color = '0000CC'; yandex_direct_text_color = '000000'; yandex_direct_hover_color = '0066FF'; document.write('<sc'+'ript type="text/javascript" src="http://an.yandex.ru/resource/context.js?rnd=' + Math.round(Math.random() * 100000) + '"></sc'+'ript>'); //]]> </script> </td></table></td><td><IMG src="p.gif" width=10 height=1 border=0></td><td valign=top width=1%><table cellspacing=0 cellpadding=0 border=0><tr><td><img src=p.gif width=160 height=10></td><tr><td style="font-size: 15px;">Есть <strong>вопросы о хостинге</strong> и о сайтах?</td><form action='http://karman.com.ua/forum/ipb.html' method=get><input type=hidden name="act" value="post"><input type=hidden name="do" value="new_post"><input type=hidden name="f" value="581"><tr><td><input type=submit style="width: 160px;" value="Спроси прямо сейчас!"> и получи ответ от профессионалов, которые обожают помогать людям :).</td></form></tr><tr><td><img src=/p.gif width=1 height=15></td></tr><tr><td align="right"><nobr><script language="javascript">//<!-- document.write('<a href="/c.php?id=29" target=_blank>'); document.write('<img src="http://www.karman.com.ua/files/6/prostohosting.gif" alt="ВИРТУАЛЬНЫЙ ХОСТИНГ, ХОСТИНГ РЕСЕЛЛЕРАМ, ВИРТУАЛЬНЫЙ СЕРВЕР [VPS,VDS], Выделенный Сервер, Регистрация доменов" border=0 width=98 height=11>'); document.write('</a>'); //--></script></nobr></td><tr><td><img src=/p.gif width=1 height=15></td></tr><tr><td valign=top>Еще по теме:<br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9877.html>Информация о SEO – оптимизаци под <span class=sw>Поис</span>ковые системы.</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9871.html>SEO для начинающих: роботы <span class=sw>Поис</span>ковых систем – это люди</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9850.html>Различие между оптимизацией под <span class=sw>Поис</span>ковые системы (SEO) и <span class=sw>Поис</span>ковым маркетингом (SEM)</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9832.html>Способы улучшения позиций в <span class=sw>Поис</span>ковых системах</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9804.html><span class=sw>Поис</span>ковый маркетинг – сделайте так, чтоб он давал вам преимущества</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9803.html>История <span class=sw>Поис</span>кового маркетинга</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9799.html>Позиции в <span class=sw>Поис</span>ковых системах, улучшите их</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9796.html>Каталоги и <span class=sw>Поис</span>ковые системы – сравнительный анализ.</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9768.html>Чем занимается писатель-фрилансер SEO <span class=sw>контен</span>та</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9752.html><span class=sw>Поис</span>ковый маркетинг и подсказки для оптимизации вашего сайта</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9738.html>Сравнение естественного и платного <span class=sw>Поис</span>ка: изучение авторитетных источников</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9714.html>Запуск Находки – бета версии нового <span class=sw>Поис</span>ка</a><br><IMG src="g/doc.gif" border=0 align="absmiddle">  <a href=http://karman.com.ua/forum/topic9701.html>Новая реализация формы <span class=sw>Поис</span>ка от Webalta</a><br></td></table></td></table></td></table><div align=center><script type='text/javascript'><!--//<![CDATA[ var m3_u = (location.protocol=='https:'?'https://62.149.18.48/openads/www/delivery/ajs.php':'http://62.149.18.48/openads/www/delivery/ajs.php'); var m3_r = Math.floor(Math.random()*99999999999); if (!document.MAX_used) document.MAX_used = ','; document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u); document.write ("?zoneid=21"); document.write ('&cb=' + m3_r); if (document.MAX_used != ',') document.write ("&exclude=" + document.MAX_used); document.write ("&loc=" + escape(window.location)); if (document.referrer) document.write ("&referer=" + escape(document.referrer)); if (document.context) document.write ("&context=" + escape(document.context)); if (document.mmm_fo) document.write ("&mmm_fo=1"); document.write ("'><\/scr"+"ipt>"); //]]>--></script><noscript><a href='http://62.149.18.48/openads/www/delivery/ck.php?n=af763be6&cb=INSERT_RANDOM_NUMBER_HERE' target='_blank'><img src='http://62.149.18.48/openads/www/delivery/avw.php?zoneid=21&cb=INSERT_RANDOM_NUMBER_HERE&n=af763be6' border='0' alt='' /></a></noscript> </div><hr><table cellspacing=0 cellpadding=0 border=0 width=100%><tr><td bgcolor="ff6600" width=200><IMG src="p.gif" width=200 height=13 border=0></td><td colspan=5 align="right" valign="top" width=100%><IMG src="g/sq.gif" border=0></td><tr><td colspan=6 bgcolor="ff6600" width=100%><IMG src="p.gif" width=1 height=1 border=0></td><tr><td colspan=6 bgcolor="ffffff"><IMG src="p.gif" width=200 height=1 border=0></td><tr><td bgcolor="ffffff"><IMG src="p.gif" width=200 height=1 border=0></td><td width=100% class=mini>© <b><a href="index.php?content_id=76&lang_id=1" class="mini">СПД Праведно-Счастливый Аладдин Ярославович</a></b>, 2004-2008. Все права защищены. При цитировании материалов ссылка на www.karman.com.ua обязательна. Редакция "Кармана" может не разделять точку зрения авторов статей, сообщений и ответственности за их содержание не несет.<td bgcolor="ffffff"><IMG src="p.gif" width=10 border=0></td><td></td><td bgcolor="ffffff"><IMG src="p.gif" width=1 border=0></td><tr><td colspan=6 bgcolor="ffffff"><IMG src="p.gif" width=1 height=10 border=0></td></table><hr> <div class=l> Быстрый переход к содержимому сайта Karman.com.ua: <br>Новости, советы, углубленные знания, знания для новичков, законодательство, интересные скрипты, фотогалереи, отчеты, <strong>статьи о хостинге</strong>: <a href="l_0_0_0_0_0.html">0</a>, <a href="l_0_0_0_0_1.html">1</a>, <a href="l_0_0_0_0_2.html">2</a>, <a href="l_0_0_0_0_3.html">3</a>, <a href="l_0_0_0_0_4.html">4</a>, <a href="l_0_0_0_0_5.html">5</a>, <a href="l_0_0_0_0_6.html">6</a>, <a href="l_0_0_0_0_7.html">7</a>, <a href="l_0_0_0_0_8.html">8</a>, <a href="l_0_0_0_0_9.html">9</a>, <a href="l_0_0_0_0_10.html">10</a>, <a href="l_0_0_0_0_11.html">11</a>, <a href="l_0_0_0_0_12.html">12</a>, <a href="l_0_0_0_0_13.html">13</a>, <a href="l_0_0_0_0_14.html">14</a>, <a href="l_0_0_0_0_15.html">15</a>, <a href="l_0_0_0_0_16.html">16</a>, <a href="l_0_0_0_0_17.html">17</a>, <a href="l_0_0_0_0_18.html">18</a>, <a href="l_0_0_0_0_19.html">19</a>, <a href="l_0_0_0_0_20.html">20</a>, <a href="l_0_0_0_0_21.html">21</a>, <a href="l_0_0_0_0_22.html">22</a>, <a href="l_0_0_0_0_23.html">23</a>, <a href="l_0_0_0_0_24.html">24</a> <br>Часто задаваемые общие <strong>вопросы о хостинге</strong>, про FTP, PHPMyAdmin и MySQL, CPanel, Предустановленные скрипты, WHM, Cron, .htaccess, SSH, Паролирование директорий, О доменах, о работе с сайтом, о Раскрутке сайта, об Электронной почте, про Основы web-программирования: <a href="l_0_faq_0_0_0.html">0</a>, <a href="l_0_faq_0_0_1.html">1</a> <br>Энциклопедия основных <strong>терминов хостинга</strong>, программного обеспечения, железной стороны хостинга, технологий, электронной почты и доменов: <a href="l_0_encyclopedia_0_0_0.html">0</a>, <a href="l_0_encyclopedia_0_0_1.html">1</a>, <a href="l_0_encyclopedia_0_0_2.html">2</a>, <a href="l_0_encyclopedia_0_0_3.html">3</a>, <a href="l_0_encyclopedia_0_0_4.html">4</a>, <a href="l_0_encyclopedia_0_0_5.html">5</a>, <a href="l_0_encyclopedia_0_0_6.html">6</a> <br><strong>Сайты о хостинге</strong> (форумы, хостинг-провайдеры, студии веб-дизайна, домен-регистраторы, инструментарии в помощь вебмастеру): <a href="l_0_site_0_0_0.html">0</a>, <a href="l_0_site_0_0_1.html">1</a>, <a href="l_0_site_0_0_2.html">2</a> </div><hr> <center><noindex><nofollow><!-- bigmir)net TOP 100 --><a href="http://www.bigmir.net/" target=_blank onClick='img = new Image();img.src="http://www.bigmir.net/?cl=77067";' ><script language="javascript"><!-- bmQ='<img src=http://c.bigmir.net/?s77067&t23' bmD=document bmD.cookie="b=b" if(bmD.cookie)bmQ+='&c1' //--></script><script language="javascript1.2"><!-- bmS=screen;bmQ+='&d'+(bmS.colorDepth?bmS.colorDepth:bmS.pixelDepth)+"&r"+bmS.width; //--></script><script language="javascript"><!-- bmF = bmD.referrer.slice(7); ((bmI=bmF.indexOf('/'))!=-1)?(bmF=bmF.substring(0,bmI)):(bmI=bmF.length); if(bmF!=window.location.href.substring(7,7+bmI))bmQ+='&f'+escape(bmD.referrer); bmD.write(bmQ+" border=0 width=88 height=63 alt='bigmir TOP100'>"); //--></script></a> <!--begin of Rambler's Top100 code --> <a href="http://top100.rambler.ru/top100/"> <img src="http://counter.rambler.ru/top100.cnt?1261108" alt="" width=1 height=1 border=0></a> <!--end of Top100 code--> <!--begin of Top100 logo--> <a href="http://top100.rambler.ru/top100/"> <img src="http://top100-images.rambler.ru/top100/banner-88x31-rambler-orange2.gif" alt="Rambler's Top100" width=88 height=31 border=0></a> <!--end of Top100 logo --> </noindex></nofollow></center><script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5238934-1"); pageTracker._trackPageview(); </script> </td></tr></table><br><br><br><br><br><br><br><br></body></html>