簡體   English   中英

MySQL - 如何獲得具有准確相關性的搜索結果

[英]MySQL - How to get search results with accurate relevance

我已經多次重新審視這個問題,但我從未真正找到正確的答案。

是否可以執行 MySQL 搜索,該搜索返回 ACTUAL 按相關性准確排序的結果?

我正在嘗試創建一個 ajax 搜索表單,它在用戶輸入輸入字段時提出建議,並且僅使用純 MySQL 查詢沒有找到合適的解決方案。 我知道有可用的搜索服務器,例如 ElasticSearch,我想知道如何僅使用原始 MySQL 查詢來做到這一點。


我有一張學校科目表。 少於 1200 行,這永遠不會改變。 讓我們執行一個基本的 FULLTEXT 搜索,用戶開始輸入“Bio”。

查詢(“生物...”) - 全文布爾模式

SELECT name, MATCH(name) AGAINST('bio*' IN BOOLEAN MODE) AS relevance
FROM subjects
WHERE MATCH(name) AGAINST('bio*' IN BOOLEAN MODE)
ORDER BY relevance DESC
LIMIT 10

結果

name                                  |  relevance
--------------------------------------------------------
Biomechanics, Biomaterials and Prosthetics  |  1
Applied Biology                             |  1
Behavioural Biology                         |  1
Cell Biology                                |  1
Applied Cell Biology                        |  1
Developmental/Reproductive Biology          |  1
Developmental Biology                       |  1
Reproductive Biology                        |  1
Environmental Biology                       |  1
Marine/Freshwater Biology                   |  1

為了顯示這些結果有多糟糕,這里是一個簡單的LIKE查詢的比較,它顯示了所有未顯示的更相關的結果:

查詢(“生物...”) - 喜歡

SELECT id, name
WHERE name LIKE 'bio%'
ORDER BY name

結果

name                                  |  relevance
--------------------------------------------------------
Bio-organic Chemistry                       |  1
Biochemical Engineering                     |  1
Biodiversity                                |  1
Bioengineering                              |  1
Biogeography                                |  1
Biological Chemistry                        |  1
Biological Sciences                         |  1
Biology                                     |  1
Biomechanics, Biomaterials and Prosthetics  |  1
Biometry                                    |  1

而且您已經看到有多少主題沒有被建議,即使這些主題更有可能是用戶正在尋找的。

然而,使用LIKE的問題是如何像FULLTEXT那樣在多個單詞和單詞中間進行搜索。

我想要實現的基本排序是這樣的:

  1. 以搜索詞開頭的第一個詞
  2. 以搜索詞開頭的第二個詞
  3. 詞條不在詞首的詞
  4. 如果沒有進一步相關,所有內容通常按字母順序排列

所以我的問題是,如何通過跨多個單詞的 MySQL 搜索為用戶獲取合理排序的建議列表?

您可以使用字符串函數,例如:

select id, name
from subjects
where name like concat('%', @search, '%')
order by 
  name like concat(@search, '%') desc,
  ifnull(nullif(instr(name, concat(' ', @search)), 0), 99999),
  ifnull(nullif(instr(name, @search), 0), 99999),
  name;

這將為您獲取包含@search 的所有條目。 首先是在開頭有它的那些,然后是在空白之后有它的那些,然后是出現的位置,然后是字母。

name like concat(@search, '%') desc順便使用了 MySQL 的布爾邏輯。 1 = 真,0 = 假,所以按降序排序首先給你真。

SQL小提琴: http ://sqlfiddle.com/#!9/c6321a/1

對於其他登陸這里的人(就像我一樣):根據我的經驗,為了獲得最佳結果,您可以根據搜索詞的數量使用條件。 如果只有一個詞使用LIKE '%word%' ,否則使用布爾全文搜索,如下所示:

if(sizeof($keywords) > 1){
   $query = "SELECT *,
             MATCH (col1) AGAINST ('+word1* +word2*' IN BOOLEAN MODE) 
             AS relevance1,
             MATCH (col2) AGAINST ('+word1* +word2*' IN BOOLEAN MODE) 
             AS relevance2
             FROM table1 c
             LEFT JOIN table2 p ON p.id = c.id
             WHERE MATCH(col1, col2) 
             AGAINST ('+word1* +word2*' IN BOOLEAN MODE) 
             HAVING (relevance1 + relevance2) > 0
             ORDER BY relevance1 DESC;";
    $execute_query = $this->conn->prepare($query);
}else{          
   $query = "SELECT * FROM table1_description c
             LEFT JOIN table2 p ON p.product_id = c.product_id
             WHERE colum1 LIKE ? AND column2 LIKE ?;";
        // sanitize
        $execute_query = $this->conn->prepare($query);
        $word=htmlspecialchars(strip_tags($keywords[0]));
        $word = "%{$word}%";
        $execute_query->bindParam(1, $word);
        $execute_query->bindParam(2, $word);
    }

這是使用上述答案的組合我可以獲得的最佳結果:

$searchTerm = 'John';
// $searchTerm = 'John Smit';
if (substr_count($searchTerm, ' ') <= 1)
    $sql = "SELECT id, name
    FROM people
    WHERE name like '%{$searchTerm}%')
    ORDER BY
      name LIKE '{$searchTerm}%') DESC,
      ifnull(nullif(instr(name, ' {$searchTerm}'), 0), 99999),
      ifnull(nullif(instr(name, '{$searchTerm}'), 0), 99999),
      name
    LIMIT 10";
}
else {
$searchTerm = '+' . str_replace(' ', ' +', $searchTerm) . '*';
$sql = "SELECT id,name, MATCH(lead.name) AGAINST('{$searchTerm}' IN BOOLEAN MODE) AS SCORE
        FROM lead
    WHERE MATCH(lead.name) AGAINST('{$searchTerm}' IN BOOLEAN MODE)
    ORDER BY `SCORE` DESC
    LIMIT 10";

確保在列上設置全文索引(如果最終使用的是多列)並使用OPTIMIZE table_name重置索引。

最好的一點是,如果您輸入Jo ,那么名字為Jo的人的排名將高於John ,這正是您想要的!

我根據您描述的順序嘗試了這個。

SET @src := 'bio';
SELECT name,
name LIKE (CONCAT(@src,'%')),
         LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(name,' ',2),' ',-1),LENGTH(@src)) = @src,
         name LIKE (CONCAT('%',@src,'%'))
FROM subjects
ORDER BY name LIKE (CONCAT(@src,'%')) DESC,
         LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(name,' ',2),' ',-1),LENGTH(@src)) = @src DESC,
         name LIKE (CONCAT('%',@src,'%')) DESC,
         name

http://sqlfiddle.com/#!9/6bffa/1

我想也許您甚至可能還想包括@src 的出現次數 計算 VARCHAR 字段中字符串的出現次數?

MATCH(s.name) AGAINST('"Applied Bio"' IN BOOLEAN MODE)

以上語句將搜索確切的搜索詞,意味着這兩個詞必須存在於每條記錄中。

ORDER BY s.name like concat("Applied Bio", '%') desc,
ifnull(nullif(instr(s.name, concat(' ', "Applied Bio")), 0), 99999),
ifnull(nullif(instr(s.name, "Applied Bio"), 0), 99999),
s.name

按以搜索詞開頭的第一個單詞排序。

完整的 SQL 語句:

SELECT SQL_NO_CACHE 
s.id, s.name
FROM subjects s use index(name_fulltext) 
WHERE 
MATCH(s.name) AGAINST('"Applied Bio"' IN BOOLEAN MODE) 
GROUP BY s.id 
ORDER BY 
s.name like concat("Applied Bio", '%') desc,
ifnull(nullif(instr(s.name, concat(' ', "Applied Bio")), 0), 99999),
ifnull(nullif(instr(s.name, "Applied Bio"), 0), 99999),
s.name
LIMIT 100;

為了得到你想要的,你可以看看將幾個“case when…”語句與 mysql 的正則表達式結合起來,這將根據你的要求為你提供每行的准確分數。 正則表達式可能是您缺少的拼圖的一部分:請參閱https://dev.mysql.com/doc/refman/5.6/en/regexp.html (在我的手機上回答,因此很難格式化答案或給出示例)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM