我多次重访这个问题,而且我从未真正找到合适的答案。
是否可以执行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
之类的单词。
我想要实现的基本顺序是:
所以我的问题是,如何通过跨多个单词的MySQL搜索为用户获取明智的排序建议列表?
答案 0 :(得分:8)
您可以使用字符串函数,例如:
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 = true,0 = false,因此按顺序排序会先给出真的。
答案 1 :(得分:5)
对于其他登陆的人(就像我一样):根据我的经验,为了获得最佳效果,您可以根据搜索词的数量使用条件。如果只有一个单词使用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);
}
答案 2 :(得分:2)
这是我使用上述答案组合得到的最佳结果:
$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
,这正是您想要的!
答案 3 :(得分:1)
我根据您描述的顺序尝试了这个。
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的出现次数Count the number of occurrences of a string in a VARCHAR field?
答案 4 :(得分:0)
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;