Отслеживание результата более 50% попаданий при FULLTEXT-поиске

Silex

unitecsys
Отслеживание результата более 50% попаданий при FULLTEXT-поиске

Насколько я понял, если при полнотекстовом поиске было обнаружено более 50% записей, удовлетворяющих критериям поиска, поиск ничего не возвращает. Можно ли отследить, был ли нулевой результат следствием этого события или следствием того, что действительно ничего не было найдено?
 

Апельсин

Оранжевое создание
> Можно ли отследить, был ли нулевой результат следствием этого события или следствием того, что действительно ничего не было найдено?

в общем случае - нельзя.
 

Silex

unitecsys
> в общем случае - нельзя
Т.е. есть некий вариант частного?

В мануале написано:
The 50% threshold is determined by the particular weighting scheme chosen. To disable it, change the following line in `myisam/ftdefs.h':
#define GWS_IN_USE GWS_PROB

To:
#define GWS_IN_USE GWS_FREQ
Я так понимаю, хостеры из-за одного меня менять это значение не будут. Вообще же у большинства хостеров какое значение? Просто первый раз столкнулся, а заказчик для тестирования начинает выбирать самые "очевидные" слова с нулевым результатом поиска :(
 

Апельсин

Оранжевое создание
> Т.е. есть некий вариант частного?
можно сделать извращение с поиском по LIKE '%keyword%', но это извращение и как по мне, то тогда уже и полнотекстовый поиск не нужен.

а так - нет.

> Просто первый раз столкнулся, а заказчик для тестирования начинает выбирать самые "очевидные" слова с нулевым результатом поиска

"очевидные слова" - это список stopwords, которые перечислены в ft_static.c (кажется), но в 4.0 там можно самому создавать свой файл со stopwords, поищи в документации, где-то про это было.
 

Silex

unitecsys
> но в 4.0
"Иногда зубная щетка бывает недоступна" (С) :(
Там 3.x какая-то...

>можно сделать извращение с поиском по LIKE '%keyword%',
Т.е. если ничего не нашли полнотекстовым способом, прогнать по LIKE еще раз, и если что-то найдем, сообщить, что найдено слишком много и нужно ввести кейвордсы поточнее? В принципе, у меня база максимум 1.5 - 2 метра будет, так что вариант. Спасибо.
 

Апельсин

Оранжевое создание
просто с вариантом LIKE нет смысла использовать полнотекстовый поиск как таковой. Т.е. использовать тогда только LIKE.
Тогда оно будет всегда тебе выдавать результаты если что-то найдет.
 

Silex

unitecsys
Да, это понятно. Но я немного не это имел ввиду.

При полнотекстовом поиске можно ранжировать результаты по релевантности. Это плюс. Минус - это вот те 50%, которые просто так не отследить. Так вот как "аварийный" вариант, когда полнотекстовый не дал результатов, разумно ли использовать LIKE, причем только для того, чтобы узнать, превысили результаты 50% или нет?

Фактически страдать будут те, кто мало того, что ничего не нашел по запросу, так еще и ждать обработку запроса больше должен :)
 

Апельсин

Оранжевое создание
ну .. при поиске с LIKE '%keyword%' индексы у тебя использоваться не будут, так что подтормаживать запросы будут.

кстати полнотекстовый поиск все-таки лучше с 4.0 использовать .. там и индекс быстрее строится и работает быстрее.
 

Yurik

/dev/null
если при полнотекстовом поиске было обнаружено более 50% записей, удовлетворяющих критериям поиска, поиск ничего не возвращает
Не совсем правильная трактовка мануала.

если при полнотекстовом поиске СЛОВО было обнаружено В более 50% записей оно попадает в список игнора. Поиск ничего не возвращает если ВСЕ слова попали в список игнора.

Fulltext features that are already implemented in the 4.0 tree:
Boolean search utilizes a more simplistic way of calculating the relevance, that does not have a 50% threshold.
Попробуй использовать BOOLEAN mode, он кроме того имеет операторы приоритета
 

Апельсин

Оранжевое создание
> Попробуй использовать BOOLEAN mode, он кроме того имеет операторы приоритета

ему это не подойдет.
у него версия 3.23
 

Silex

unitecsys
>Поиск ничего не возвращает если ВСЕ слова попали в список игнора

Да все верно.
 

ForJest

- свежая кровь
Могу придумать, возникни такая необходимость. Правда что является критерием релевантности в FULL TEXT я незнаю. Вопрос в другом - зачем нужен такой поиск? Я не понимаю.
 

Silex

unitecsys
Вопрос в другом - зачем нужен такой поиск?
Как зачем? Сначала ссылки на записи, содержащие наибольшее кол-во введенных слов для поиска, затем - по убыванию. На то и релевантность... Или ты не это имел ввиду?
 

ForJest

- свежая кровь
Я имел ввиду что ограничение в 50% и связанные с этим вышеописанные проблемы сводят на нет достоинства.
 

Апельсин

Оранжевое создание
если так нужно найти полное соответствие без этого 50% барьера, то кто мешает использовать полнотекстовый поиск с BOOLEAN MODE ?
 

slach

Новичок
;) в те далекие времена когда пользование FULLTEXT было физически невозможно (3.22.x), а потом оно приводило выпаданию 3.23.x в корку
умные люди из phorum.org
придумали пользоваться LIKE ;)

потом DJ Rabbit опубликовал статью
http://detail.phpclub.net/2001-12-09.htm

а я сюда еще кусочек кода запостю ОК ?

PHP:
// разбивка строки на массив токенов
  function search_get_tokens(&$query,&$match) {
   $query = (get_magic_quotes_gpc()) ? $query=StripSlashes(trim($query)) : trim($query);
   $match=intval($match);
   $tokNum = 0;
   $tokens = array();
   if ($query!='') {
     if($match!=3){
      //Build Tokens.  There's a gawd awful
      //way of doing this with a regex, but it's messy
      $InQuotedString = 0;
      $params = explode(" ", $query);
      $tokens[$tokNum] = "";
      for($i=0; $i<sizeof($params); $i++){
        if(!isset($tokens[$tokNum])){
          $tokens[$tokNum] = "";
        }
        $param = $params[$i];
        if(strpos($param,'"')===0 || preg_match('/^[+-]"/', $param)){
          $InQuotedString = 1;
        }
        if($InQuotedString == 1){
          $tokens[$tokNum] .= str_replace('"', '', $param) . " ";
        }else{
          $tokens[$tokNum++] = $param;
        }

        if(preg_match('/"$/', $param)){
          $InQuotedString = 0;
          $tokens[$tokNum] = chop($tokens[$tokNum]);
          if($tokens[$tokNum]==""){
            unset($tokens[$tokNum]);
          }else{
            $tokNum++;
          }
        }
      }
     }else{
       $tokens[$tokNum] = str_replace('"', '', chop($query));
     }
     if(count($tokens)==0) unset($tokens);
   }
   return $tokens;
  }

// генерация RELEVANCE части sql запроса по массиву токенов
  function search_get_sql_relevance($tokens,$match,$fields) {
   $good_words='';
   $bad_words='';
   $max_relevance=1;
   for($i=0; $i<count($tokens); $i++){
    if (strlen($tokens[$i])>2) {
     $token = trim($tokens[$i]);
     if(strstr($token,'+')==$token){
      $token=str_replace('"','',preg_replace('/^\+/','',$token));
      $good_words.=$token.' ';
      for($x=0; $x<count($fields); $x++){
	   $relevance[]="IF($fields[$x] LIKE '%$token%', 9, 0)";
	   $max_relevance+=9;
      }
     }elseif(strstr($token,'-')==$token){
      $token=str_replace('"','',preg_replace('/^\-/','',$token));
      $bad_words.=$token.' ';
      for($x=0; $x<count($fields); $x++){
  	   $relevance[]="IF($fields[$x] NOT LIKE '%$token%', 9, 0)";
	   $max_relevance+=9;
      }
     }elseif(trim($token)!=""){
      $token=str_replace('"','',$token);
      $good_words.=$token.' ';
      for($x=0; $x<count($fields); $x++){
	   $relevance[]="IF($fields[$x] LIKE '%$token%', 9, 0)";
	   $max_relevance+=9;
      }
     }
    }
   }
   //совпадение полной строки
   for($x=0; $x<count($fields); $x++){
	   $relevance[]="IF($fields[$x] LIKE '%$good_words%', ".strval(substr_count($good_words, ' ')*10).", 0)";
	   $max_relevance+=10*substr_count($good_words, ' ');
   }
   for($x=0; $x<count($fields); $x++){
	   $relevance[]="IF($fields[$x] NOT LIKE '%$bad_words%', ".strval(substr_count($bad_words, ' ')*10).", 0)";
	   $max_relevance+=10*substr_count($bad_words, ' ');
   }

   if (@is_array($relevance)) {
    return ' (0+('.implode('+',$relevance).'))*100/'.$max_relevance.' AS relevance ';
   }
   return '';
  }

// генерация WHERE части sql запроса по массиву токенов
  function search_get_sql_where($tokens,$match,$fields) {

   $SQLquery=' (';
   for($i=0; $i<count($tokens); $i++){
    if (strlen($tokens[$i])>2) {
     for($x=0; $x<count($fields); $x++){
      $token = trim($tokens[$i]);
      if(strstr($token,'+')==$token){
        $token = preg_replace('/^\+/', '', $token);
        $SQLquery .= "$fields[$x] LIKE '%$token%'";
        if($x<count($fields)-1){
           $SQLquery .= " OR ";
        }
      }elseif(strstr($token,'-')==$token){
        $token = preg_replace('/^\-/', "", $token);
        $SQLquery .= "$fields[$x] NOT LIKE '%$token%'";
        if($x<count($fields)-1){
          if($match==1){
            $SQLquery .= ') AND (';
          }else{
            $SQLquery .= ') OR (';
          }
        }
      }elseif(trim($token)!=""){
        $SQLquery .= "$fields[$x] LIKE '%$token%'";
        if($x<count($fields)-1){
          $SQLquery .= ' OR ';
        }
      }
     }
    }

    if($i<count($tokens)-1){
     if($match==1){
       $SQLquery .= ') AND (';
     }else{
       $SQLquery .= ') OR (';
     }
    }else{
     $SQLquery .= ') ';
    }
   }
   return $SQLquery;
  }

//пользуюсь примерно следующим образом:
if (!isset($query)) $query='';
$query = (get_magic_quotes_gpc()) ? $query=StripSlashes(trim($query)) : trim($query);

    if ($query!='') {
     $tokens=search_get_tokens($query,$match);
     if(@is_array($tokens)){
      $fields = array();
      switch($type) {
       case 'full':
        $fields[] = "t1.title";
        $fields[] = "t1.info";
        $fields[] = "t1.keywords";
        $fields[] = "t1.text";
        $SQLquery="SELECT t1.url AS url, DATE_FORMAT(t1.m_time,'%d %M %Y %H:%i') AS artikles_time, t1.divs_path, t1.divs_title, t1.title, t1.info, t1.keywords, ";
        $relevance=search_get_sql_relevance($tokens,$match,$fields);
        $SQLquery.=$relevance;
        $SQLquery.=" FROM search_index AS t1 WHERE (";
        break;
       case 'quick':
       default:
        $fields[] = "t2.title";
        $fields[] = "t2.info";
        $fields[] = "t1.title";
        $fields[] = "t1.info";
        $fields[] = "t1.keywords";
        $SQLquery="SELECT t1.id AS id, DATE_FORMAT(t1.c_time,'%d %M %Y %H:%i') AS artikles_time, CONCAT('$cfg[virtualhost]$cfg[rootdir]',t3.path) AS divs_path, t2.title AS divs_title, t1.title AS title, t1.info AS info, t1.keywords AS keywords FROM artikles AS t1 LEFT JOIN divs_info AS t2 ON (t2.divs_id=t1.divs_id AND t2.lang_id=$lang_id) LEFT JOIN divs AS t3 ON (t3.id=t1.divs_id) WHERE (";
        break;
      }

      $SQLquery.=search_get_sql_where($tokens,$match,$fields);

      switch($type) {
       case 'full':
        if ($relevance) $SQLquery .= ') ORDER BY relevance DESC, t1.m_time DESC';
        else $SQLquery .= ") ORDER BY t1.m_time DESC";
        break;
       case 'quick':
       default:
        $SQLquery .= ") ORDER BY t1.m_time DESC";
        break;
      }

     } // if is_array($tokens)

    } // end if $query!=''
    echo $SQLQuery
    $r=$db->Execute($SQLquery);
}
 

slach

Новичок
ищем по базе в 17 мегабайт вполне сносно
вкупе с кешированием, вообще проблем мало...
 
Сверху