Подтвердить что ты не робот

Поисковый запрос не является достаточно точным

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

  • Когда я ищу слова "что есть пища", и у меня есть "что есть пища" в базе данных, все результаты, содержащие одно из ключевых слов "что", "есть", "еда", показаны. Желаемое поведение - отображать результаты, содержащие точную фразу "что такое еда" (первая)

  • Только последнее слово в запросе выделено, и я хочу выделить все слова

Желаемое поведение: правильный ответ отображается вверху, независимо от его положения в базе данных.

Мой текущий код выглядит следующим образом:

if (isset($_GET["mainSearch"]))
{
  $condition = '';
  $mainSearch = SQLite3::escapeString($_GET['mainSearch']);
  $keyword = $_GET['mainSearch'];
  $query = explode(" ", $keyword);
  $perpageview=7;

  if ($_GET["pageno"])
  {
      $page=$_GET["pageno"];
  }
  else
  {
      $page=1;
  }

  $frompage = $page*$perpageview-$perpageview;

  foreach ($query as $text)
  {
      $condition .= "question LIKE '%".SQLite3::escapeString($text)."%' OR answer LIKE '%".SQLite3::escapeString($text)."%' OR ";
  }
  foreach ($query as $text_2)
  {
      $condition_2 .= "bname LIKE '%".SQLite3::escapeString($text_2)."%' OR bankreq LIKE '%".SQLite3::escapeString($text_2)."%' OR ";
  }

  $condition = substr($condition, 0, -4);
  $condition_2 = substr($condition_2, 0, -4);


  $order = " ORDER BY quiz_id DESC ";
  $order_2 = " ORDER BY id DESC ";
  $sql_query = "SELECT * FROM questions WHERE " . $condition . ' '. $order.' LIMIT '.$frompage.','.$perpageview;
  $sql_query_count = "SELECT COUNT(*) as count FROM questions WHERE " . $condition .' '. $order;
  //$mainAnswer = "SELECT * FROM questions WHERE question LIKE '%$mainSearch%' or answer LIKE '%$mainSearch%'";
  $bank_query = "SELECT * FROM banks WHERE " . $condition_2 . ' LIMIT 1';
  $result = $db->query($sql_query);
  $resultCount = $db->querySingle($sql_query_count);
  $bankret = $db->query($bank_query);
  //$mainAnsRet = $db->query($mainAnswer);
  $pagecount = ceil($resultCount/$perpageview);

  if ($resultCount > 0)
  {
  if ($result && $bankret)
  {
      while ($row = $result->fetchArray(SQLITE3_ASSOC))
      {

          $wording = str_replace($text, "<span style='font-weight: bold; color: #1a0dab;'>".$text."</span>", $row['answer']);

           echo '<div class="quesbox_3">
            <div class="questitle">
                <h2>'.$row["question"].'</h2>
            </div>
            <div class="quesanswer">'.$wording.'</div>
        </div>';
      }
      while ($brow = $bankret->fetchArray(SQLITE3_ASSOC))
      {
            $bname = $brow['bname'];
            $bankbrief = $brow['bankbrief'];
            $bankreq = $brow['bankreq'];
            $bankaddress = $brow['bankaddress'];
            $banklogo = $brow['banklogo'];
            $founded = $brow['founded'];
            $owner = $brow['owner'];
            $available = $brow['available'];


           echo '<div class="modulecontent">
            <div class="modulename">
                <div class="mname">'.$bname.'</div>
                <div class="mlogo"><img src="'.$banklogo.'"></div>
            </div>';

            if (strlen($bankreq) > 300)
            {
                $bankcut = substr($bankreq, 0, 300);

                $bankreq = substr($bankcut, 0, strrpos($bankcut, ' ')).'... <a href="bankprofile.php?bname='.$bname.'">Read More</a>';
                echo '<div class="modulebrief">'.$bankreq.'</div>';
            }
            echo '<div class="modulelinks">
                <div class="mfound">Founded: <span>'.$founded.'</span></div>
                <div class="mowned">Ownd By: <span>'.$owner.'</span></div>
            </div>
        </div>';

               // <div class="mavailable">Available for Export Loan: <span>'.$available.'</span></div>
      }
      ?>
      <div class="page_num">
      <?php
      for ($i=1; $i <= $pagecount; $i++) {
         echo '<a href="searchresult.php?mainSearch='.$mainSearch.'&pageno='.$i.'">'.$i.'</a>';
      }
      ?>
      </div>
      <?php
  }
  }
  else
  {
      $session_n = $_SESSION['log_id'];
      $sesdate = date('d/M/Y');
      echo "<div class='searchNone'><p>No results found</p></div>
      <div class='sendSearchQ'>
      <p>Please send us your question.</p>
      <form action='sendquestion.php' method='post' encytype='multipart/form-data'>
      <div class='searchQinputs'>
          <input type='text' name='searchQuestion' id='searchQuestion'placeholder='Whats your question'><br>
          <input type='submit' name='sendQuestion' id='sendQuestion' value='Send'>
          <input type='text' name='user' id='user' value='$session_n' style='display: none'>
          <input type='text' name='qDate' id='qDate' value='$sesdate' style='display: none'>
          <input type='text' name='status' id='status' value='0' style='display: none'>
          </div>
      </form>
      </div>";
  }
}
4b9b3361

Ответ 1

Добавить сортируемое поле в ваш запрос

Сначала нам нужно упростить вашу проблему

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

A Искать: some search text должен возвращать все результаты, содержащие любое из этих слов ['some', 'search', 'text'], с результатами в верхнем совпадении точно как указано "some search text".

Это означает, что вам нужно создать поле агрегата, которое позволяет сортировать результат на основе соответствия. Единственный способ, которым я это знаю, без рефакторинга данных и/или кода, - это выражение MySql Case.

Упрощенный запрос

SELECT *
FROM questions
WHERE
    question LIKE '%[word1]%' OR answer LIKE '%[word1]%'
    OR question LIKE '%[word2]%' OR answer LIKE '%[word2]%'
    OR question LIKE '%[word3]%' OR answer LIKE '%[word3]%'
ORDER BY quiz_id DESC

Случай [полный матч] и Сортировка

Нам нужно построить запрос, который выглядит примерно так:

SELECT *,
    (CASE WHEN
            question LIKE '%[full-search-query]%'
            OR answer LIKE '%[full-search-query]%'
        THEN 1 ELSE 0
    END) as fullmatch
FROM questions
WHERE
    question LIKE '%[word1]%' OR answer LIKE '%[word1]%'
    OR question LIKE '%[word2]%' OR answer LIKE '%[word2]%'
    OR question LIKE '%[word3]%' OR answer LIKE '%[word3]%'
ORDER BY fullmatch DESC, quiz_id DESC

Настройка кода

// your initial storage of the full search, before you split it on spaces
$keyword = $_GET['mainSearch'];

. . .

// build our sorting field
$sortFullMatch = "(CASE WHEN question LIKE '%".SQLite3::escapeString($keyword)."%' OR answer LIKE '%".SQLite3::escapeString($keyword)."%' THEN 1 ELSE 0 END) as fullmatch";

. . .

// adjust the query and sort
$order = " ORDER BY fullmatch DESC, quiz_id DESC ";
$sql_query = "SELECT *,". $sortFullMatch ." FROM questions WHERE ".$condition.' '.$order.' LIMIT '.$frompage.','.$perpageview;

Что это делает?

Мы добавили новое поле в инструкцию SELECT, fulltext. Если вопрос или ответ содержит точно полный поиск, это поле будет 1, иначе 0. Затем просто сортируйте в этом поле.

Выделение цветом

Что касается вашей проблемы с подсветкой, вы заменяете только на $text, которая устанавливается в цикле для каждого слова в mainSearch. Таким образом, это будет только последнее слово в множестве. Вместо этого вам нужно сделать аналогичный цикл.

Ваш код

$wording = str_replace($text, "<span style='font-weight: bold; color: #1a0dab;'>".$text."</span>", $row['answer']);

Скорректированная

foreach($query as $text) {
    $wording = str_replace($text, "<span style='font-weight: bold; color: #1a0dab;'>".$text."</span>", $row['answer']);
}

Ответ 2

Вы можете использовать Google NLP apis для обнаружения слов, которые являются только коннекторами и словами, которые являются нумерами и имеют значимость, так что запрос, который вы отправляете в базу данных, игнорирует слова типа "что" или "есть" и таким образом вы можете предоставить пользователям гораздо лучший опыт. есть также другие инструменты, такие как NLTK и др., которые также могут использоваться для более интеллектуальных запросов.

Ответ 3

По соображениям производительности вы должны использовать REGEXP вместо нескольких инструкций LIKE. Заказ можно выполнить с помощью простой инструкции ORDER BY, как показано ниже:

$keyword=$_GET['mainSearch'];
$pattern=join("|", array_filter(explode(" ", $keyword)));

$sql_query="SELECT * FROM questions
     WHERE (question REGEXP '$pattern') OR (answer REGEXP '$pattern')
     ORDER BY CONCAT(question, answer) LIKE '%$keyword%' DESC;";

Выделение можно легко выполнить с помощью preg_replace

 preg_replace("/($pattern)/",
       "<span style='font-weight: bold; color: #1a0dab;'>$1</span>",
       $row['answer']);

Ответ 4

Сначала запустите следующий запрос в вашей базе данных один раз.

ALTER TABLE  questions  ADD FULLTEXT(question, answer);

Затем используйте ниже запрос для поиска

if (isset($_GET["mainSearch"]))
{
  $condition = '';
  $mainSearch = SQLite3::escapeString($_GET['mainSearch']);
  $keyword = $_GET['mainSearch'];
  $query = explode(" ", $keyword);


  $puresearch=implode("*+*", $query);

  $myquery = "SELECT *,MATCH(question,answer) AGAINST('*".$keyword."*' IN BOOLEAN MODE) as relavance FROM questions WHERE MATCH(question,answer) AGAINST('*".$keyword."*' IN BOOLEAN MODE) ORDER BY ralavance DESC";

  $result = $db->query($myquery);

}

Это полный текстовый поиск, который дает результаты в соответствии с релевантностью и может быть упорядочен в порядке убывания, чтобы показать поиск в первой точке, которая более надежна для поиска.

Ответ 5

Учитывая эту таблицу test

create table test (
  tid integer PRIMARY KEY,
  ttext text NOT NULL
  );
  INSERT INTO test (
  tid,
  ttext
  ) VALUES (1,'what are you doing?'),(2,'this is your house?'),(3,'food chain'),(4,'what is food');

ваши конечные запросы должны выглядеть так:

select *,
(case
when ttext like '%food%' then 1
else 0
end+
case
when ttext like '%chain%' then 1
else 0
end) as tsum
from test where ttext like '%food%' or ttext like '%chain%' 
order by tsum desc;

id  text           sum
 3  food chain      2
 4  what is food    1    

select *,
(case
when ttext like '%what%' then 1
else 0
end+
case
when ttext like '%is%' then 1
else 0
end+
case
when ttext like '%food%' then 1
else 0
end) as tsum
from test where ttext like '%what%' or ttext like '%is%' or ttext like '%food%' 
order by tsum desc;

id  text                sum
 4  what is food         3
 1  what are you doing?  1
 2  this is your house?  1
 3  food chain           1

select *,
(case
when ttext like '%this%' then 1
else 0
end+
case
when ttext like '%is%' then 1
else 0
end) as tsum
from test where ttext like '%this%' or ttext like '%is%' 
order by tsum desc;

id  text                sum
 2  this is your house?  2
 4  what is food         1

select *,
(case
when ttext like '%what%' then 1
else 0
end+
case
when ttext like '%is%' then 1
else 0
end+
case
when ttext like '%house%' then 1
else 0
end) as tsum
from test where ttext like '%what%' or ttext like '%is%' or ttext like '%house%' 
order by tsum desc;

id  text                sum
 2  this is your house?  2
 4  what is food         2
 1  what are you doing?  1

Вы можете протестировать все вышеперечисленные запросы на DB Fiddle

Чтобы создать такой запрос и учитывая, что вы используете PDO, ваш код PHP должен выглядеть так.

...
// Sample code for 1st example query
$keywrd1 = '%food%';
$keywrd2 = '%chain%';
// Prepare the command
$sth = $dbh->prepare('select *,
(case
when ttext like :keyword1 then 1
else 0
end+
case
when ttext like :keyword2 then 1
else 0
end) as tsum
from test where ttext like :keyword1 or ttext like :keyword2 
order by tsum desc');
// Bind the parameters
$sth->bindParam(':keyword1', $keywrd1, PDO::PARAM_STR);
$sth->bindParam(':keyword2', $keywrd2, PDO::PARAM_STR);
$sth->execute();
...