Этот урок связан с проектом Собственное поисковое ядро

Пишем собственное поисковое ядро. Часть 1.

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

Приступим.

1. Структура.

Для начала хочу показать в какой таблице у меня хранятся материалы:

Как видите у меня достаточно много полей, большинство я не стал показывать они предназначены чисто для технических моментов. Нас интересуют по сути первые 4 поля: id, title, keywords, text. Если вы как то по другому храните свои материалы, и у вас например есть еще поле description то необходимо учесть этот момент и соответственно внести необходимые корректировки.

Далее нас интересует какие файлы у нас будут в системе:

Тут тоже ничего сложного, самое поисковое ядро search_core.php и шаблон search_result.tpl. В чем суть? В том, что все отправляемые пользователем данные будут обрабатываться в поисковом ядре и формировать вывод из файла шаблона в нужное нам место на сайте.

Сам же файл search_core.php будет построен у нас на функциях, работе с массивами и регулярных функциях.

2. Особенности русского языка.

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

Определимся с тем какие у нас есть окончания у слов и напишем первую функцию в наше ядро, которая будет отрезать у слов запроса окончания для повышения уровня релевантности:

function dropBackWords($word) { //тут мы обрабатываем одно слово
	$reg = "/(ый|ой|ая|ое|ые|ому|а|о|у|е|ого|ему|и|ство|ых|ох|ия|ий|ь|я|он|ют|ат)$/i"; //данная регулярная функция будет искать совпадения окончаний
	$word = preg_replace($reg,'',$word); //убиваем окончания
	return $word;
}

Как видите в общем пока ничего сложного. Т.е. если пользователь введет например слово: "Песни", то система откинет букву "и" и оставит нам только "Песн", а это согласитесь позволит найти уже такие слова, как песня, песню, песнями и т.д. Т.е. семантика поиска у нас увеличилась.

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

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

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

function stopWords($query) { //тут мы обрабатываем весь поисковый запрос
	$reg = "/\s(под|много|что|когда|где|или|которые|поэтому|все|будем|как)\s/gim"; //данная регулярка отрежет все стоп-слова отбитые пробелами
	$query = preg_replace($reg,'',$query); //убиваем стоп-слова
	return $query;
}

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

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

Как вариант для обеих функций можно формировать массив таких значений и брать уже из него для сравнения, но не будем тут усложнять себе жизнь.

3. Обработка поискового запроса

Теперь нам надо наконец получить все поисковые слова из общего поискового запроса, который задал пользователь:

function explodeQuery($query) { 	//функция вызова поисковой строки
	$query = stopWords($query); 	//используем написанную нами ранее функцию для удаления стоп-слов
	$words = explode(" ",$query); 	//разбиваем поисковый запрос на слова через пробел и заносим все слова в массив
	$i = 0; 						//устанавливаем начало массива в 0, помним что нумерация в массивах начинается с 0
	$keywords = ""; 				//создаем пустой массив
	foreach ($words as $word) { 	//в цикле для массива words создаем элемент word
		$word = trim($word);		
		if (strlen($word)<6) {		//если слово короче 6 символов то убиваем его
			unset($word);
		}
		else {						//иначе выполняем следующее
			if (strlen($word)>8) {
				$keywords[$i]=dropBackWords($word);	//наша функция чистки окончаний для слов длинее 8 символов и занесение их в созданный нами массив
				$i++;								//наращиваем значение i для того чтобы перейти к следующему элементу
			}
			else {
                $keywords[$i]=$word; 				//если короче 8 символов то просто добавляем в массив
                $i++;
            }
		}
	}
	return $keywords; //возвращаем полученный массив
}

Думаю стоит объяснить почему мы слова короче 6 символов убиваем. Это правило распространяется на кодировку UTF-8, особенность которой в том, что каждый русский символ идет за два. Попробуйте ради интереса вывести на экран длину 1 русского символа и вы будете удивлены тем, что она равна 2. Таким образом если мы пишем короче 6 символов, то для русского это значит короче 3 символов. Вот такой нюанс, который следует помнить. Сейчас наверное каждый сразу вспомнил недавнее возмущение нашего правительства на тему, почему SMS-сообщения в русской раскладке в два раза короче, чем английские.

Таким образом если вывести наш массив то мы получим примерно следующее:

//поисковая фраза: как написать поисковый модуль своими силами
array (
	[0]=>написат,
	[1]=>поисков,
	[2]=>модул,
	[3]=>своим,
	[4]=>силам
)

Уже хорошо и практически то, что нам нужно. С этим мы уже можем спокойно работать и получить больше материалов из базы, чем если бы мы искали по целой фразе.

Напишем еще одну небольшую функцию чисто визуализационную и на этом закончим первую часть урока.

 

function colorSearchWord($word, $string, $color) {
	$replacement = "<span style='color:".$color."; border-bottom:1px dashed ".$color.";'>".$word."</span>";
	$result = str_replace($word, $replacement, $string);
	return $result;
}

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

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

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: www.ruseller.com
Автор: Павлов Александр
Урок создан: 10 Октября 2011
Просмотров: 71916
Правила перепечатки


5 последних уроков рубрики "PHP"

  • Фильтрация данных с помощью zend-filter

    Когда речь идёт о безопасности веб-сайта, то фраза "фильтруйте всё, экранируйте всё" всегда будет актуальна. Сегодня поговорим о фильтрации данных.

  • Контекстное экранирование с помощью zend-escaper

    Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

  • Подключение Zend модулей к Expressive

    Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.

  • Совет: отправка информации в Google Analytics через API

    Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.

  • Подборка PHP песочниц

    Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

или авторизуйтесь, чтобы добавлять комментарии, оценивать уроки и сохранять их в личном кабинете
  • 10 Октября 2011 08:41
    evgenics
    1. Если используем регулярку и кодировку UTF-8 лучшев регулярке добавить можификатор u
    $reg = "/тут паттерн регулярки/ui";
    2. Чем не устраивает mb_strlen вместо strlen ?
     if (mb_strlen ($word, 'UTF-8')<3)
    
    и все прекрасно.
    • 10 Октября 2011 09:38
      alexxhub
      Устраивает. Просто как нюанс указал. Тут уж хозяин барин, какую функцию удобнее найдете конечно почему бы не использовать. Кстати на счет модификатора. У меня беда какая-то с ним он выдал ошибку из-за него, поэтому не указывал, только i заработал.
  • 10 Октября 2011 13:45
    ntvsx193
    Статья хороша. Подробно объясняете
  • 10 Октября 2011 13:58
    teha
    Статья замечательная! Приятно читать! И самое главное, что написана человеческим языком и для людей. Спасибо!
    • 10 Октября 2011 17:24
      La5erdance
      Согласен.
  • 10 Октября 2011 16:22
    notbot
    Идея хорошая, но зачем удалять все слова короче 6 знаков!? "скачать фотки кошек" -> "" И ещё пара моментов: стоп-слова удаляются с обоими пробелами, вот пример: "скачать много файлов" -> "скачатьфайлов", так ведь? вместо первого пробела надо поставить (^|\s), а вместо второго ($|\s), надеюсь, не надо объяснять зачем.
    • 11 Октября 2011 01:26
      alexxhub
      Читайте внимательнее. Там указано почему 6 знаков.
  • 13 Октября 2011 09:17
    p1us
    Alexxhub, для чего нужен gim?
    • 14 Октября 2011 04:10
      alexxhub
      g - искать везде, i - игнорировать регистр, m - сложносоставные строки (черт ее знает как правильно перевести)
  • 13 Октября 2011 13:35
    p1us
    вы меня простите, если не в тему, но чему $string равна?
    • 14 Октября 2011 04:10
      alexxhub
      На место стринга вы подставляете переменную в которой нужно искать совпадение и окрашивать его. В следующей части используется эта функция и там это прекрасно видно.
      • 14 Октября 2011 06:05
        p1us
        что функция используется это да, но сама переменная $string вроде тоже должна иметь значение...или я путаю что-то? нигде нет значения этой переменной.. расскажите плз мне поподробнее. чайник - есть чайник
        • 17 Октября 2011 04:22
          alexxhub
          Нет не обязательно она должна иметь значение. Она получает его когда мы за место нее подставляем свою переменную.
  • 17 Октября 2011 18:08
    WI_Wind
    И правильно, что убрали всякие SQL-Инъекции, хоть чуть-чуть умом и ручками поработаем. А я это все в Kohan'у попытаюсь встроить.
  • 19 Октября 2011 18:33
    RusMikle
    В корне неправильный подход к поисковой системе и схеме её работы. То о чем вы пишете называется полнотекстовый поиск. Как правило такой поиск ведётся не только по общей информации об объекте но и по вспомогательной. Напр. Город, район, дата, какие то свои отличительные признаки которые хранятся в отдельных полях от названия и описания. Объекты могут быть разными и под каждый делать поиск под свой набор полей неразумно. Гораздо удобнее создать индекс документа на основе страницы его детализации с вырезанными html тегами и элементами общими для всех страниц на сайте (т.е. в итоге остаются только значимые тексты). Затем поиск условно разбивать на части по приоритетности. Напр. и в зависимости от этого расставлять индекс релевантности. Напр. если поисковые слова найдены в названии региона то приоритет 1, если в тексте страницы детализации (индексе) то в зависимости от релевантности полнотекстного поиска. И соотв. от релевантности сортировать результаты запроса. Опять же существует масса готовых продуктов в которых такой поиск реализован, и сделано там все намного умнее. Потому не изобретайте велосидед и не учите людей как ненадо это делать. Кстати, те стоп слова, которые Вы усиленно пытаетесь удалить из текстов, на самом деле, важная составляющая текста, позволяющая определить релевантность поисковой фразы. Ну и так далее. Остальным советую смотреть в сторону продуктов Sphinx или aceMySearch, есть много дугих. С уважением Михаил.
  • 20 Октября 2011 01:02
    MrNeutro
    Статья написана неплохо. Кроме одного НО: использовать MySql в таких системах большая ошибка; куда быстрее все будет работать на движке Mongo или Regis.
  • 27 Октября 2011 09:23
    Demon777
    Спасибо, пригодиться.
  • 3 Ноября 2011 00:02
    Orda_jan
    Я не понял! Скажите пожалуйста!! В этом уроке мы создаем 3 документ да!? 1.search_result.php <----- что напишем в нем 2.search_core.php <---- что напишем в нем 3.search_result.tpl <----- что напишем в нем вообще то я не создал search_core.php! Скажите пожалуйста!!!
  • 7 Ноября 2011 16:44
    maha
    класно
    
  • 10 Ноября 2011 14:24
    mishador
    Скиньте кто-нибудь ссылку на исходники.Пожалуйста.
  • 12 Ноября 2011 21:59
    snikers987
    эту регулярку(из первой функции)
     $reg = "/(ый|ой|ая|ое|ые|ому|а|о|у|е|ого|ему|и|ство|ых|ох|ия|ий|ь|я|он|ют|ат)$/i"; 
    я бы написал так<BR><PRE>
    $reg = "/(ый|ой|ая|ое|ые|ому|а|о|у|е|ого|ему|и|ство|ых|ох|ия|ий|ь|я|он|ют|ат)(\s|$)/i"
    ; так как так убираются вхождения не только из последнего слова<BR><BR>Во второй регулярке модификатор g уберите, такого вообще не существует.
    • 15 Ноября 2011 00:22
      RebelioUS
      Кстати да) даже не заметили) g то unknown)
  • 13 Ноября 2011 14:43
    snikers987
    Глупо выбирать все строки, а потом их анализировать нужно выбирать только нужные, тут нехватает еще одной функции которая "строит" запрос в базу
  • 14 Ноября 2011 21:26
    tlustenko
    ребята! не пойму, а где сами исходники с файлами???
  • 5 Декабря 2011 11:27
    limurec
    Вам не кажется что вместо $words = explode(" ",$query); лучше сделать так $words = preg_split("/[\s]+/", $query);?
  • 22 Января 2012 14:59
    xzoner
    Warning: preg_replace() [function.preg-replace]: Unknown modifier 'g' in W:\home\localhost\...\search_result.php on line 10 Fatal error: Function name must be a string in W:\home\localhost/...\search_result.php on line 54 Почему так?
    • 12 Февраля 2012 09:24
      GrKamil
      Fatal error: Function name must be a string in .. Посмотрите на line 54 у вас должно быть :
      if ($mysql_num_rows($result)!=0) { 
      , просто уберите перед mysql_num_rows знак доллара
  • 5 Марта 2012 19:37
    igor3310
    чето нечисти слова:(
  • 15 Июня 2012 10:51
    solonikov_alexandr
    Дайте ссылку на следующий урок.
  • 19 Июня 2012 13:35
    fabrigas201
    OK
  • 20 Июня 2012 06:35
    tsohg15
    было бы не плохо, если бы добавили исходники и написали что куда вставлять, а то честное слово, не всё понятно, написал какие файлы должны быть и всё, а дальше вставьте это, вставьте то, а куда не всем понятно будет.
  • 20 Июля 2012 23:57
    pas_s_s
    Fatal error: Function name must be a string in Z:\home\only-cs.com\www\search_result.php on line 10
    помогите!!!
  • 21 Июля 2012 12:26
    pas_s_s
    foreach ($keywords as $word)
    пишет: Warning: Invalid argument supplied for foreach() in Z:\home\only-cs.com\www\search_result.php on line 65 Warning: Invalid argument supplied for foreach() in Z:\home\only-cs.com\www\search_result.php on line 65 Warning: Invalid argument supplied for foreach() in Z:\home\only-cs.com\www\search_result.php on line 65
  • 28 Сентября 2012 17:34
    Вадим Сапрыкин
    Допустим, в UTF8 русские 3 символа это 6 для системы. А если кто то будет писать на английском? Можно сделать так:
    $regv = '/^([а-я0-9]+)$/i';
    $count = (preg_match($regv, $word)) ? 6 : 3;
    if (strlen($word)<$count) {
    ...
    Объясняю: если в слове присутствуют русские буквы и цифры - максимальный размер 6 символов. Иначе 3. P.s. Если с рег. выражениями или сокращением if-else что то не так - поправьте меня, ибо я не силен в них.
  • 22 Мая 2013 13:23
    GunaevVO
    Что писать, где писать, в каком файле делать не чего не написано!
  • 4 Августа 2013 15:38
    mux_kg
    помогите как создать регистрации и соц сеть на сайте
  • 23 Декабря 2014 13:31
    nur_oleg
    Было бы не плохо сделать видео-гайд по данной теме. Но все равно спасибо!
^ Наверх ^