Поисковый запрос не является достаточно точным
У меня есть поисковый запрос, сделанный мной, насколько мне известно в 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>";
}
}
Ответы
Ответ 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();
...