[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Релевантность в поиске
igor717
Хочу попросить, чтоб мне разъяснили как можно сделать релевантности в поиске?



Спустя 4 минуты, 50 секунд (31.03.2010 - 13:33) waldicom написал(а):
Одна из возможностей: полям, по которым ведется поиск, раздавать вес.

Спустя 5 минут, 40 секунд (31.03.2010 - 13:38) Архуша написал(а):
Ну один из простых способов:
1) Разделаеш запрос пользователя на слова (или словосочитания)
2) ищеш документ где эти слова (или словосочитания) встречаются чаще
3) Выводиш по порядку убывания, относительно количества найденых слов (или словосочитаний).

Спустя 5 минут, 29 секунд (31.03.2010 - 13:44) igor717 написал(а):
Я так в принцепе и делаю разбиваю фразу на слова и подставляю их в запрос к базе.
Только как узнать скока раз какое слова встретилось?

Спустя 4 минуты, 7 секунд (31.03.2010 - 13:48) igor717 написал(а):
waldicom, извени, а что ты имеешь в виду? И как вообще должен выглядить запрос - like, rlike или match () against()

Спустя 1 минута, 1 секунда (31.03.2010 - 13:49) Архуша написал(а):
да впринципе не сложно.

str_world_count()

еще вот хорошая функция:
count_chars();

Спустя 4 минуты, 25 секунд (31.03.2010 - 13:53) waldicom написал(а):
Цитата (igor717 @ 31.03.2010 - 12:48)
waldicom, извени, а что ты имеешь в виду? И как вообще должен выглядить запрос - like, rlike или match () against()

Не за что извиняться...
Допустим ты ведешь поиск по книгам. Значит у тебя могут быть примерно такие поля: "автор", "название", "издательство", "год выпуска", "содержание", "текст книги" и пр.
Так вот некоторым полям можно дать высокий вес, а другим ниже.

Как будет выглядеть код, тут я тебе не могу помочь. У нас для таких вещей используется Lucene


Спустя 1 минута, 6 секунд (31.03.2010 - 13:54) twin написал(а):
Архуша
Цитата
да впринципе не сложно.

str_world_count()

Этой функцией нужно уметь пользоваться еще. Софттайм схитрил - слова все английские. С кирилицей так не проканает. smile.gif

Спустя 1 минута, 12 секунд (31.03.2010 - 13:56) Архуша написал(а):
Цитата (twin @ 31.03.2010 - 10:54)
Архуша
Цитата
да впринципе не сложно.

str_world_count()

Этой функцией нужно уметь пользоваться еще. Софттайм схитрил - слова все английские. С кирилицей так не проканает. smile.gif

Правда? не знал, буду знать smile.gif спасибо.

Ну тогда остается вторая функция. Она с киррилицей дружит. Проверял сам.

Спустя 1 минута, 37 секунд (31.03.2010 - 13:57) igor717 написал(а):
А можно ли при обращении к базе данных подсчитать сколько каких слов встречалось в таблицах и исходя из этого отсортировать эти данные

Спустя 3 минуты, 46 секунд (31.03.2010 - 14:01) Архуша написал(а):
Вот еще: Допустим для страницы ты выставляеш определенную оценку (от 0 до 10). 0 вообще не авторитетная статья, 10 очень авторитетная и полезная статья.
Это типо как пейджранг только в очень сильной миниатюре smile.gif

И когда нашел страницу, скрипт сортирует поиск на основание оценки статей.

igor77, насколько я знаю в запросу SQL подсчитать колиечество найденных слов нельзя sad.gif

Спустя 5 минут, 57 секунд (31.03.2010 - 14:07) igor717 написал(а):
тогда я не совсем понимаю алгоритм:

допустим человек что-то ввел и это что-то встретилось в 10 различных статьях - это сформировался массив вывода БД

Дальше у меня идет функция вывода каждая статья попадает в эту функцию и там я подсвечиваю нужное слово и выдаю кусок текста с этим словом.

Соответственно нужно подкорректировать порядок попадания статей в функцию вывода.

Правильно я рассуждаю или нет?

Спустя 5 минут, 51 секунда (31.03.2010 - 14:13) Архуша написал(а):
Ну вот ты получил массив из 10 статей, где просто встретились эти слова.
Потом береш каждую и подсичтываеш в ней количество слов, запоминаеш количество и номер статьи. Из того что ты запомнил, делаеш некую формулу среднего значения повторения слов, получаеш усредненное значение релевантности по запросу, а дальше уже сортируеш на основание этого усредненного значения.


Спустя 11 минут, 51 секунда (31.03.2010 - 14:25) igor717 написал(а):
на словах все звучит довольна таки просто а вот как это сделать:

Если рассуждать то сначала надо тогда сформировать массив

$result7 = mysql_query ("select id, headline, content, search from framework where publish='1' and  reference='1' and ".implode(" or ", $zapros)." union select id, headline, content, search from categories where publish='1' and  reference='1' and ".implode(" or ", $zapros)." union select id, headline, content, search from articles where publish='1' and  reference='1' and ".implode(" or ", $zapros)." union select id, headline, content, search from news where publish='1' and ".implode(" or ", $zapros)."limit $start, $num",$db); 				  
$myrow7 = mysql_fetch_array ($result7);

do
{

/*здесь этот подсчет совпадений


/*и за тем наверно нужно формировать какой-то новый массив с результатами, вот только как это сделать если столько различных данных */
[s]
}
while ($myrow7 = mysql_fetch_array ($result7));


Спустя 5 минут, 36 секунд (31.03.2010 - 14:30) twin написал(а):
Первым делом изменить цикл с do... while на while

Спустя 1 минута, 53 секунды (31.03.2010 - 14:32) igor717 написал(а):
Извиняюсь, а почему?

Спустя 13 минут, 40 секунд (31.03.2010 - 14:46) igor717 написал(а):
короче не получится у меня наверно так сделать так как до этого куска скрипта у меня идет часть, которая осуществляет постраничный вывод данных, следовательно на 2,3 или 4 страницу может попасть часть результатов у который релевантность будет выше чем у 1!!!

Можно ли все таки в запросе к базе произвести сортировку по релевантности (чтоб в начале в result попадали те статьи где поисковый запрос встречается чаще)?

Спустя 2 часа, 32 минуты, 51 секунда (31.03.2010 - 17:19) igor717 написал(а):
В общем что касается релевантности и ее сортировки - все решается с помощью функции
MATCH (...) AGAINST (...) - она все делае автоматически, но тогда возникает вопрос поиска по части слова, конечно это можно решить с помощью MATCH (...) AGAINST (...IN BOOLEAN MODE), но тогда пропадает сортировка по релевантности...

Может кто знает как выйти из этого положения...

Спустя 8 минут, 1 секунда (31.03.2010 - 17:27) FatCat написал(а):
Цитата (waldicom @ 31.03.2010 - 14:33)
полям, по которым ведется поиск, раздавать вес
Цитата (igor717 @ 31.03.2010 - 14:44)
разбиваю фразу на слова и подставляю их в запрос к базе


А теперь объединяем:
  • Делаем базу слов.
  • Запрос разбиваем по словам, гоним по базе слов, получаем расширенный список слов.
  • Расширенным списком слов поиск по базе текстов.

Спустя 4 минуты, 19 секунд (31.03.2010 - 17:31) igor717 написал(а):
дело в том, что поиск ведется в 4 таблицах по одному полю...

А что значит делаем базу слов, можно по подробней...

Спустя 3 минуты, 19 секунд (31.03.2010 - 17:34) igor717 написал(а):
Просто у меня была мысль в админке при дабовлении контента сделать вспомогательное поле таблицы куда будут попадать уже порезанные слова (ну в смысле набор слов, который встречается в контенте) и поиск будет вестись именно по этому полю таблицы

Спустя 5 часов, 53 минуты, 37 секунд (31.03.2010 - 23:28) Архуша написал(а):
Цитата (igor717 @ 31.03.2010 - 14:34)
Просто у меня была мысль в админке при дабовлении контента сделать вспомогательное поле таблицы куда будут попадать уже порезанные слова (ну в смысле набор слов, который встречается в контенте) и поиск будет вестись именно по этому полю таблицы

Это так называемые тэги smile.gif

Вообще вполне возможно и так. Но 100 процентов это не даст.

Слушай. Я сам не пробывал, но по идее должно получиться. А ты попробуй парсить гугловский поиск. Тоесть отправляй в гугл запрос пользователя сформированный под твой сайт (указав на каком сайте искать), а потом тупо парсить то что он нашел...

Спустя 1 час, 22 минуты, 51 секунда (1.04.2010 - 00:51) igor717 написал(а):
Не знаю мой поиск меня вполне устраивает, и я если не поленюсь дописать к нему вот эту фигню: ну обрезать окончания у слов и занасить этот набор слов в отдельную графу таблицы к примеру search и уже потом производить поиск по ней. То при переходе с like на MATCH (...) AGAINST (...) получи сортировку по релевантности!!!

В принцепе сейчас в поиске я уже написал скрипт который обрезает окончания у прилагательных, глаголов и существительных, что довольно таки сильно увеличило гибкость поиска, поэтому это будет реализовать не долго!!!

Другое дело конечно еще подключить к поиску морфологический словарь - вот это дело.... wink.gif

Не посоветуешь? rolleyes.gif

Спустя 9 часов, 40 минут, 10 секунд (1.04.2010 - 10:31) Nikitian написал(а):
Вы видимо стеммер изобретаете
Стеммер Портера


<?php
/*
Скрипты в cp1251
*/

//Класс для выделения основы слова
/*
* PHP5 implementation of Martin Porter's stemming algorithm for Russian language.
*
* Written on a cold winter evening close to the end of 2005 by Dennis Kreminsky (etranger at etranger dot ru)
*
* Use the code freely, but don't hold me responsible if it breaks whatever it might break.
*
*
* Usage:
*
* $stem=stem::russian($word);
*
* All Russian characters are (originally) in UTF-8.
*
*/


define ('CHAR_LENGTH', '1'); // all Russian characters take 2 bytes in UTF-8, so instead of using (not supported by default) mb_*
// string functions, we use the standard ones with a dirty char-length trick.
// Should you want to use WIN-1251 (or any other charset), convert this source file to that encoding
// and then change CHAR_LENGTH to the proper value, which is likely to be '1' then.



class Stemmer2 {

var $_abc;
var $_ABC;

function Stemmer2() {
$this->_abc = 'абвгдеёжзийклмнопрстуфхцчшщъьэюя';
$this->_ABC = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ';
}

function rustolower($arg){
for($i=0;$i<strlen($this->_abc);$i++){
$arg = str_replace($this->_ABC{$i},$this->_abc{$i},$arg);
}
return $arg;
}

function stem($word)
{
// RUSSIAN DIRTY LOWERCASE:
$word = $this->rustolower($word);

$a=$this->rv($word);
$start=$a[0];
$rv=$a[1];
$rv=$this->step1($rv);
$rv=$this->step2($rv);
$rv=$this->step3($rv);
$rv=$this->step4($rv);
return $start.$rv;
}

function rv($word)
{
$vowels=array('а','е','и','о','у','ы','э','ю','я');
$flag=0;
$rv='';
$start='';
for ($i=0; $i<strlen($word); $i+=CHAR_LENGTH)
{
if ($flag==1)
$rv.=substr($word, $i, CHAR_LENGTH);
else
$start.=substr($word, $i, CHAR_LENGTH);
if (array_search(substr($word,$i,CHAR_LENGTH), $vowels)!==FALSE)
$flag=1;
}
return array($start,$rv);
}

function step1($word)
{
$perfective1=array('в', 'вши', 'вшись');
foreach ($perfective1 as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix && (substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='а' || substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='я'))
return substr($word, 0, strlen($word)-strlen($suffix));

$perfective2=array('ив','ивши','ившись','ывши','ывшись');
foreach ($perfective2 as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
return substr($word, 0, strlen($word)-strlen($suffix));

$reflexive=array('ся', 'сь');
foreach ($reflexive as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
$word=substr($word, 0, strlen($word)-strlen($suffix));

$adjective=array('ее','ие','ые','ое','ими','ыми','ей','ий','ый','ой','ем','им','ым','ом','его','ого','ему','ому','их','ых','ую','юю','ая','яя','ою','ею');
$participle2=array('ем','нн','вш','ющ','щ');
$participle1=array('ивш','ывш','ующ');
foreach ($adjective as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
{
$word=substr($word, 0, strlen($word)-strlen($suffix));
foreach ($participle1 as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix && (substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='а' || substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='я'))
$word=substr($word, 0, strlen($word)-strlen($suffix));
foreach ($participle2 as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
$word=substr($word, 0, strlen($word)-strlen($suffix));
return $word;
}

$verb1=array('ла','на','ете','йте','ли','й','л','ем','н','ло','но','ет','ют','ны','ть','ешь','нно');
foreach ($verb1 as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix && (substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='а' || substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='я'))
return substr($word, 0, strlen($word)-strlen($suffix));

$verb2=array('ила','ыла','ена','ейте','уйте','ите','или','ыли','ей','уй','ил','ыл','им','ым','ен','ило','ыло','ено','ят','ует','уют','ит','ыт','ены','ить','ыть','ишь','ую','ю');
foreach ($verb2 as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
return substr($word, 0, strlen($word)-strlen($suffix));

$noun=array('а','ев','ов','ие','ье','е','иями','ями','ами','еи','ии','и','ией','ей','ой','ий','й','иям','ям','ием','ем','ам','ом','о','у','ах','иях','ях','ы','ь','ию','ью','ю','ия','ья','я');
foreach ($noun as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
return substr($word, 0, strlen($word)-strlen($suffix));

return $word;
}

function step2($word)
{
if (substr($word,-CHAR_LENGTH,CHAR_LENGTH)=='и')
$word=substr($word, 0, strlen($word)-CHAR_LENGTH);
return $word;
}

function step3($word)
{
$vowels=array('а','е','и','о','у','ы','э','ю','я');
$flag=0;
$r1='';
$r2='';
for ($i=0; $i<strlen($word); $i+=CHAR_LENGTH)
{
if ($flag==2)
$r1.=substr($word, $i, CHAR_LENGTH);
if (array_search(substr($word,$i,CHAR_LENGTH), $vowels)!==FALSE)
$flag=1;
if ($flag=1 && array_search(substr($word,$i,CHAR_LENGTH), $vowels)===FALSE)
$flag=2;
}
$flag=0;
for ($i=0; $i<strlen($r1); $i+=CHAR_LENGTH)
{
if ($flag==2)
$r2.=substr($r1, $i, CHAR_LENGTH);
if (array_search(substr($r1,$i,CHAR_LENGTH), $vowels)!==FALSE)
$flag=1;
if ($flag=1 && array_search(substr($r1,$i,CHAR_LENGTH), $vowels)===FALSE)
$flag=2;
}
$derivational=array('ост', 'ость');
foreach ($derivational as $suffix)
if (substr($r2,-(strlen($suffix)))==$suffix)
$word=substr($word, 0, strlen($r2)-strlen($suffix));
return $word;
}

function step4($word)
{
if (substr($word,-CHAR_LENGTH*2)=='нн')
$word=substr($word, 0, strlen($word)-CHAR_LENGTH);
else
{
$superlative=array('ейш', 'ейше');
foreach ($superlative as $suffix)
if (substr($word,-(strlen($suffix)))==$suffix)
$word=substr($word, 0, strlen($word)-strlen($suffix));
if (substr($word,-CHAR_LENGTH*2)=='нн')
$word=substr($word, 0, strlen($word)-CHAR_LENGTH);
}
// should there be a guard flag? can't think of a russian word that ends with ейшь or ннь anyways, though the algorithm states this is an "otherwise" case
if (substr($word,-CHAR_LENGTH,CHAR_LENGTH)=='ь')
$word=substr($word, 0, strlen($word)-CHAR_LENGTH);
return $word;
}
}




class Stemmer1
{
var $VERSION = "0.02";
var $Stem_Caching = 0;
var $Stem_Cache = array();
var $VOWEL = '/аеиоуыэюя/';
var $PERFECTIVEGROUND = '/((ив|ивши|ившись|ыв|ывши|ывшись)|((?<=[ая])(в|вши|вшись)))$/';
var $REFLEXIVE = '/(с[яь])$/';
var $ADJECTIVE = '/(ее|ие|ые|ое|ими|ыми|ей|ий|ый|ой|ем|им|ым|ом|его|ого|ему|ому|их|ых|ую|юю|ая|яя|ою|ею)$/';
var $PARTICIPLE = '/((ивш|ывш|ующ)|((?<=[ая])(ем|нн|вш|ющ|щ)))$/';
var $VERB = '/((ила|ыла|ена|ейте|уйте|ите|или|ыли|ей|уй|ил|ыл|им|ым|ен|ило|ыло|ено|ят|ует|уют|ит|ыт|ены|ить|ыть|ишь|ую|ю)|((?<=[ая])(ла|на|ете|йте|ли|й|л|ем|н|ло|но|ет|ют|ны|ть|ешь|нно)))$/';
var $NOUN = '/(а|ев|ов|ие|ье|е|иями|ями|ами|еи|ии|и|ией|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|ию|ью|ю|ия|ья|я)$/';
var $RVRE = '/^(.*?[аеиоуыэюя])(.*)$/';
var $DERIVATIONAL = '/[^аеиоуыэюя][аеиоуыэюя]+[^аеиоуыэюя]+[аеиоуыэюя].*(?<=о)сть?$/';

function s(&$s, $re, $to)
{
$orig = $s;
$s = preg_replace($re, $to, $s);
return $orig !== $s;
}

function m($s, $re)
{
return preg_match($re, $s);
}

function stem($word)
{
//$word = mb_strtolower(mb_strtolower($word, 'CP1251'));
//$word = strtoupper_($word);

$word=strtolower_($word);
$word = str_replace('ё', 'е', $word);
if($this->Stem_Caching && isset($this->Stem_Cache[$word]))
{
return $this->Stem_Cache[$word];
}
$stem = $word;
do
{
if(!preg_match($this->RVRE, $word, $p)) break;
$start = $p[1];
$RV = $p[2];
if(!$RV) break;
if(!$this->s($RV, $this->PERFECTIVEGROUND, ''))
{
$this->s($RV, $this->REFLEXIVE, '');
if($this->s($RV, $this->ADJECTIVE, ''))
{
$this->s($RV, $this->PARTICIPLE, '');
}
else
{
if(!$this->s($RV, $this->VERB, ''))
$this->s($RV, $this->NOUN, '');
}
}

$this->s($RV, '/и$/', '');
if($this->m($RV, $this->DERIVATIONAL))
$this->s($RV, '/ость?$/', '');
if(!$this->s($RV, '/ь$/', ''))
{
$this->s($RV, '/ейше?/', '');
$this->s($RV, '/нн$/', 'н');
}
$stem = $start.$RV;
}
while(false);
if($this->Stem_Caching)$this->Stem_Cache[$word] = $stem;
return $stem;
}

function stem_caching($parm_ref)
{
$caching_level = @$parm_ref['-level'];
if($caching_level)
{
if(!$this->m($caching_level, '/^[012]$/'))
{
die(__CLASS__ . "::stem_caching() - Legal values are '0','1' or '2'. '$caching_level' is not a legal value");
}
$this->Stem_Caching = $caching_level;
}
return $this->Stem_Caching;
}

function clear_stem_cache()
{
$this->Stem_Cache = array();
}
}


Спустя 49 минут, 9 секунд (1.04.2010 - 11:20) igor717 написал(а):
Не про стемера я в курсе конечно - говно причем полное, слова портит сильно, слишком он много убирает лишнего. К примеру зачем убирать все возможные суффиксы? если человек ввел слово с суффиксом, то оно ему и нужно именно в этой форме, если убратть суффикс то зачастую слово полностю меняет смысл!!!
Поэтому я лично сделал свой вариант и заняло это полчаса работы с учебником по русскому. Конечно бывают у него тоже касяки, но это намного много меньше!!! -

Спустя 39 минут, 59 секунд (1.04.2010 - 12:00) FatCat написал(а):
Цитата (igor717 @ 31.03.2010 - 18:31)
что значит делаем базу слов, можно по подробней...

Сейчас как раз делаю нечто похожее: эмулятор работы яндекса. biggrin.gif
Слова из анализируемого текста сопоставляются сначала по таблице высокочастотников, таблица небольшая, тысячи полей.
Результаты выборки сопоставляются таблице низкочастотников (сотни тысяч полей), из которой выбираются только связанные с первой таблицей записи.

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

В действии можно посмотреть http://coda.su/index.php

Спустя 1 час, 30 минут, 36 секунд (1.04.2010 - 13:31) igor717 написал(а):
Прикольно rolleyes.gif (я про пример)

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

Так мы избавляемся от необходимости полного совпадения слов, и в то же время получаем сортировку по релевантности!!! При этом MATCH (...) AGAINST (...) работает быстрее, чем like, rlike. И тем более намного много быстрее чем IN BOOLEAN MODE, если его заставить сортировать по релевантности.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2025 Invision Power Services, Inc.