SELECT * FROM articles
WHERE MATCH (title, body) AGAINST ('database' IN NATURAL LANGUAGE MODE);
-- 顯示相關度分數並排序
SELECT id, body,
MATCH (title, body) AGAINST ('Security implications of running MySQL as root') AS score
FROM articles
WHERE MATCH (title, body) AGAINST ('Security implications of running MySQL as root')
ORDER BY score DESC;
| 模式 | 特點 |
|---|---|
| NATURAL LANGUAGE MODE | 預設,計算相關度,需 FULLTEXT 索引,忽略停用詞、短詞(預設 <4 字元) |
| IN BOOLEAN MODE | 支援 + - * "" 運算子,不計算相關度,可不走索引 |
| WITH QUERY EXPANSION | 盲目擴展:先搜尋,再用高相關文檔關鍵詞二次搜尋 |
ft_min_word_len(預設 4),中文單字多為 1-2 字,**原生全文搜尋不適合中文**。ft_stopword_file 可自訂。MySQL 內建不支援 CJK(中日韓)分詞。解決路徑:應用層分詞 → 建立倒排索引表 → 搜尋時同樣分詞匹配。
function chineseSegment($str, $encoding = 'utf-8') {
$stopWords = ['的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一個', '上', '也', '很', '到', '說', '要', '去', '你', '會', '著', '看', '好', '自己', '這'];
// 移除 HTML 標籤
$str = preg_replace('/<[^>]+>/', '', $str);
// 移除停用詞
$str = str_replace($stopWords, ' ', $str);
$len = mb_strlen($str, $encoding);
$terms = [];
for ($i = 0; $i < $len - 1; $i++) {
$bigram = mb_substr($str, $i, 2, $encoding);
// 過濾純標點/空白
if (preg_match('/^[\p{Han}\p{L}\p{N}]{2}$/u', $bigram)) {
$terms[] = $bigram;
}
}
// 英文/數字單獨提取
preg_match_all('/[a-zA-Z0-9]+/', $str, $enMatches);
$terms = array_merge($terms, $enMatches[0]);
return array_unique($terms);
}
// 寫入全文索引表
$terms = chineseSegment($articleTitle . ' ' . $articleBody);
$indexStr = implode(' ', $terms);
$db->query("UPDATE article_ft SET keywords='$indexStr' WHERE article_id=$id");
$qTerms = chineseSegment($userQuery);
$where = '';
foreach ($qTerms as $t) {
$where .= "keywords LIKE '%$t%' OR ";
}
$where = rtrim($where, ' OR ');
$sql = "SELECT * FROM article_ft WHERE $where";
FULLTEXT(keywords) 加速 MATCH AGAINST(需 MyISAM 或 5.6+ InnoDB)。