Skip to content

MySQL - N-gram 全文解析器

MySQL - ngram 全文解析器 (用于 CJK 语言)

Section titled “MySQL - ngram 全文解析器 (用于 CJK 语言)”

MySQL 中的标准全文搜索 (full-text search) 依赖于空格和标点符号等分隔符来分隔单词(分词/tokens)。这对于英语等语言效果很好,但对于汉字表意语言(如中文、日语和韩语 (CJK))则无效,这些语言通常不使用空格分隔单词。为了在这些语言中实现有效的搜索,MySQL 提供了 ngram 全文解析器 (full-text parser)。

ngram 是文本字符串中连续的“n”个字符序列。ngram 解析器 (parser) 通过在文本上滑动“n”个字符的窗口来对文本进行分词 (tokenize)。“n”的值是可配置的。

例如,使用不同的“n”值对日语单词“東京” (Tokyo) 进行分词:

n=1: '東', '京' (一元模型)
n=2: '東京' (二元模型)

对中文短语“搜索引擎”进行分词:

n=2 (二元模型): '搜索', '索引', '引擎'

分词大小由 ngram_token_size 服务器变量控制。默认值为 2(二元模型),可设置为 1 到 10。较小的分词大小会导致索引更小,搜索更快,但可能会产生较少的相关结果。此变量在运行时是只读的,必须在您的 MySQL 配置文件 (my.cnf 或 my.ini) 中设置。

[mysqld]
ngram_token_size=2

您必须重启 MySQL 服务器才能使此更改生效。更改 ngram_token_size 后,您必须重建所有使用 ngram 解析器的 FULLTEXT 索引。

使用 ngram 解析器创建 FULLTEXT 索引

Section titled “使用 ngram 解析器创建 FULLTEXT 索引”

要使用此解析器,您在创建 FULLTEXT 索引时需要指定 WITH PARSER ngram。至关重要的是,您的表和列应使用像 utf8mb4 这样的 Unicode 字符集。

CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
FULLTEXT (title, content) WITH PARSER ngram
) ENGINE=InnoDB CHARACTER SET=utf8mb4;

让我们插入一些日语示例文章。

INSERT INTO articles (title, content) VALUES
('日本の首都', '日本の首都は東京です。'),
('京都の観光', '京都には多くの有名な寺院があります。');

当您执行搜索时,搜索查询本身也会被分词为 ngram。

搜索“東京”(在 ngram_token_size=2 的情况下)会变成搜索分词 '東京'。这将匹配第一篇文章。

SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('東京' IN NATURAL LANGUAGE MODE);

布尔模式 (Boolean mode) 提供更多控制。对短语的搜索会转换为对 ngram 序列的搜索。例如,搜索 "京都 寺院"(京都 寺庙)会变为搜索包含 ngram '京都'、'都 '、' 寺'、'寺院' 且彼此邻近的文档。

SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('"京都の寺院"' IN BOOLEAN MODE);

这很可能匹配第二篇文章,因为它包含该字符序列。

  • 空格处理: ngram 解析器将空格视为分隔符。它会移除空格,并且不会生成跨越空格的 ngram。例如,"ab cd" 会被解析为 "ab" 和 "cd" 的 ngram,分别处理。
  • 停用词处理: 常见词(停用词/stopwords)在分词前会被移除。如果一个分词在停用词列表中,它将不被索引。如果您定义了相关的停用词列表,这也适用于 CJK 语言。
  • 通配符搜索: 通配符搜索 (*) 可能不太直观。如果前缀短于 ngram_token_size,它可能会返回大量结果。如果前缀更长,通配符运算符通常会被忽略,并将该术语转换为 ngram 短语搜索。

在客户端程序中使用 ngram 解析器 (Node.js 示例)

Section titled “在客户端程序中使用 ngram 解析器 (Node.js 示例)”

以下是您如何在 Node.js 应用程序中使用 mysql2 实现搜索功能。

// searchService.js
const pool = require('./db'); // 假设已配置 mysql2 Promise 连接池
async function searchArticles(query) {
// 使用 BOOLEAN MODE 进行更精确的短语搜索。
// 添加通配符以允许末尾的部分匹配。
const searchQuery = `"${query}*"`;
const sql = `
SELECT id, title, content
FROM articles
WHERE MATCH(title, content) AGAINST(? IN BOOLEAN MODE);`;
try {
const [rows] = await pool.execute(sql, [searchQuery]);
console.log(`Found ${rows.length} results for '${query}':`);
console.log(rows);
return rows;
} catch (error) {
console.error('Full-text search failed:', error);
throw error;
}
}
// 示例用法:
// searchArticles('東京');
// searchArticles('京都 観光');