Подходящо търсене с морфология (без индексиране)

Често уеб разработчиците са изправени пред задачата да внедрят търсене в сайта. Нека се опитаме да проектираме "велосипед" със следните характеристики:

  1. Липса на индексна таблица (индексите са тема на отделна статия);
  2. Работа с UTF-8 кодировка;
  3. Премахване на кратки и стоп думи;
  4. Морфологична трансформация на български словоформи;
  5. Сортирайте резултатите според тяхната уместност;
  6. Страниране на резултатите.

Имаме нужда от работещ уеб сървър с PHP поддръжка. За морфологичен анализ ще използваме Porter's Bulgarian stemmer. Освен това се нуждаем от списък със стоп думи и някои функции от проекта PHP UTF-8. Тъй като се съгласихме, че няма да използваме индексиране, тогава в базата данни е достатъчна една таблица с приблизително следната форма:

Ще търсим страници по тяхното съдържание ( content ) и ще показваме заглавието ( title ) и описанието ( description ) във фрагменти. Въпреки това, ако е необходимо, можете лесно да разширите търсенето до няколко полета и дори да вземете това предвид при изчисляване на уместността.

Предварителна обработка на низове

Първо инициализираме променливите с низа на заявката, номера на текущата страница и броя на записите, показани на една страница. Последните две променливи са необходими за организиране на страниране на резултатите с подходяща навигация.

Ще намалим заявката за търсене до 64 знака (тази дължина е напълно достатъчна) и ще я преведем в малки букви. За да направим това, ще използваме функциите от проекта PHP UTF-8, споменат по-горе, архивът, с който разопаковахме в директорията utf8. Ако вашият производствен сървър поддържа mbstring, можете да използвате подобни функции от там.

За коректна работа на щуцера е необходима подмянабукви от "йо" до "е". След това премахваме всички HTML обекти и специални знаци от низа и заменяме последователните интервали с единични екземпляри. Така получаваме готов низ от думи, разделени с интервали.

Морфологичен анализатор

В българския език морфологичната вариативност на думите е изразена (например „дърво, дървета, дървени“), следователно, за да се подобри качеството на търсенето, има смисъл да се направи морфологичен анализ на заявките за търсене. Целта на такъв анализ ще бъде да се получи от думата нейната основа или лема. Търсенето по леми увеличава пълнотата на резултатите, получени поради документи, в които се срещат други словоформи със същата основа. Морфологичният анализ също има положителен ефект върху релевантността (точността) на търсенето, тъй като използването на леми дава гъвкавост на механизма за търсене и освобождава потребителя от необходимостта да посочи формата на думата в желания случай и т.н.

Има два основни подхода към морфологичния анализ. Първият и най-точен е прилагането на алгоритми, използващи речници. Примерите включват Ispell и AOT. Недостатъкът на този подход е по-голямото натоварване на сървъра, тъй като обемът на речниците може да достигне десетки мегабайти.

Вторият и много по-икономичен подход е да се използват лематизатори или стемери, алгоритми, които определят лемите чрез суфикси и окончания. Тяхната точност е с порядък по-ниска, но за малки сайтове, където търсенето не е основна необходимост, това е повече от достатъчно.

Като stemmer ще използваме имплементацията на алгоритъма на Porter в PHP. Можете обаче да използвате пакета PECL::stem или какъвто и да е друг пакет. Стемерът на Портър изглежда така:

Ще ни трябва и масив от стоп думи, които не носятзначително семантично натоварване и подлежи на премахване. Не включва думи, по-кратки от три букви, тъй като така или иначе ще ги изтрием (значително ще намалим точността на търсенето).

Изграждане на SQL заявка

Преди да получим лемите и да изградим SQL заявка от тях, нека си спомним за уместността. Логично е страницата да отговаря на заявката толкова повече, колкото повече са заявените думи (леми) в нейния текст. SQL ви позволява да определите броя на срещанията на дума в низ (текст на документ), като използвате следната конструкция:

Разбира се, би било по-правилно броят на повторенията да се нормализира според дължината на текста, тъй като има разлика между десет повторения в цяла книга и пет в един параграф (вторият случай е по-уместен, въпреки по-малко съвпадения). Но при използване на SQL без използване на запомнени процедури е невъзможно да се определи общият брой думи в низ. Следователно ще трябва да пренебрегнем амплитудата на обема на страниците, като ги считаме за приблизително еднакви.

И така, като начало, нека формираме частите от заявката, съдържащи клаузата WHERE и броя на релевантността. Последният се дефинира като сбор от случаите на всяка от необходимите леми. Ако искате да приложите търсене в множество полета на таблица, тогава трябва да промените съответно тези части на заявката. В същото време можете да увеличите теглото на някои полета, като добавите коефициент за тях към формулата за изчисляване на релевантността.

Първо ще отсеем кратки и стоп думи, а за останалите ще дефинираме леми, които ще търсим. За да се предпазим допълнително от твърде „тежки“ заявки, ще се ограничим до първите пет думи.

Тъй като възнамеряваме да получаваме резултати страница по страница, ще ни трябват две SQL заявки. От първия ще разберем общия брой страници за търсене, а с помощта на втория ще изберем необходимите за показване(в количеството, посочено в $rpp), сортирайки ги в низходящ ред по уместност.

Извеждане на резултатите

Остава само да се покаже формата за въвеждане на заявка заедно с резултатите от търсенето:

И накрая, нека добавим връзки за навигация между страниците с резултати:

Изключете тагове и атрибути

Сериозен недостатък на внедряването без индексиране е търсенето в целия документ, включително етикетите и техните атрибути. Например, ако възнамерявате да намерите страници с думата "статия", ще намерите и всички документи със същия етикет. Това не е голяма работа, ако имате български сайт за цветя или автомобили. Още по-лошо, ако вашите потребители наистина често търсят думи като „статия“, „раздел“, „скрипт“ и т.н. MySQL съхранена процедура, която премахва всички тагове от низ, може да помогне в този случай. Изглежда така:

Съхранените процедури се поддържат в InnoDB, но не и в MyISAM. Следователно използването на тази функция е по ваша преценка. В следващата статия ще разгледаме търсенето с индексиране, както и формирането на фрагменти въз основа на заявка за търсене с подчертаване на намерените думи.

От 5.6 Mysql може да използва FULLTEXT индекс за InnoDB.

Sphinx все още е най-доброто решение за търсене по морфологична релевантност и т.н. Чрез персонализиране на формулите за класиране

работи така връща стъпка4(стъпка3(стъпка2(стъпка1($a[0]))));

Можете ли да ми кажете какво прави функцията rv($word)? В него променливата $flag == 1 само на последната буква от думата, съответно последната итерация на цикъла, функцията винаги връща $rv = '' (празно)

HTML 5 кратък курс

Този курс запознава читателя с основите на HTML 5 и ви кара да работите за нула време.