MySQL - ngram 全文解析器
- ngram 全文解析器
- 配置 ngram 标记大小
- 使用 ngram 解析器创建全文索引
- ngram 解析器空间处理
- ngram 解析器停用词处理
- ngram 解析器短语搜索
- ngram 解析器词条搜索
- ngram 解析器通配符搜索
- 使用客户端程序的 ngram 全文解析器
通常在全文搜索中,内置的 MySQL 全文解析器会将单词之间的空格视为分隔符。这决定了单词的实际起始和结束位置,从而简化了搜索。然而,这仅适用于使用空格分隔单词的语言。
一些表意文字语言(例如中文、日语和韩语)不使用单词分隔符。为了支持此类语言的全文搜索,需要使用 ngram 解析器。 InnoDB 和 MyISAM 存储引擎均支持此解析器。
ngram 全文解析器
ngram 是来自给定文本序列的"n"个字符的连续序列。 ngram 解析器将文本序列划分为由 n 个字符组成的连续标记序列。
例如,考虑文本"Tutorial",并观察 ngram 解析器如何对其进行标记 -
n=1: 'T', 'u', 't', 'o', 'r', 'i', 'a', 'l' n=2: 'Tu', 'ut', 'to' 'or', 'ri', 'ia' 'al' n=3: 'Tut', 'uto', 'tor', 'ori', 'ria', 'ial' n=4: 'Tuto', 'utor', 'tori', 'oria', 'rial' n=5: 'Tutor', 'utori', 'toria', 'orial' n=6: 'Tutori', 'utoria', 'torial' n=7: 'Tutoria', 'utorial' n=8: 'Tutorial'
ngram 全文解析器是一个内置服务器插件。与其他内置服务器插件一样,它会在服务器启动时自动加载。
配置 ngram Token 大小
要更改 Token 大小(默认大小为 2),请使用 ngram_token_size 配置选项。ngram 值的范围是 1 到 10。但为了提高搜索查询速度,请使用较小的 Token 大小;因为较小的标记大小允许使用较小的全文搜索索引进行更快的搜索。
由于 ngram_token_size 是只读变量,因此您只能使用两个选项设置其值:
在启动字符串中设置 --ngram_token_size:
mysqld --ngram_token_size=1
在配置文件"my.cnf"中设置 ngram_token_size:
[mysqld] ngram_token_size=1
使用 ngram 解析器创建 FULLTEXT 索引
可以使用 FULLTEXT 关键字在表的列上创建 FULLTEXT 索引。它与 CREATE TABLE、ALTER TABLE 或 CREATE INDEX SQL 语句一起使用;您只需指定"WITH PARSER ngram"。语法如下:
CREATE TABLE table_name ( column_name1 datatype, column_name2 datatype, column_name3 datatype, ... FULLTEXT (column_name(s)) WITH PARSER NGRAM ) ENGINE=INNODB CHARACTER SET UTF8mb4;
示例
在此示例中,我们使用 CREATE TABLE 语句创建 FULLTEXT 索引,如下所示 -
CREATE TABLE blog ( ID INT AUTO_INCREMENT NOT NULL, TITLE VARCHAR(255), DESCRIPTION TEXT, FULLTEXT ( TITLE, DESCRIPTION ) WITH PARSER NGRAM, PRIMARY KEY(id) ) ENGINE=INNODB CHARACTER SET UTF8MB4; SET NAMES UTF8MB4;
现在,将数据(任何表意语言)插入到此创建的表中 -
插入博客值 (NULL, '', ''), (NULL, '', '');
要检查文本的标记方式,请执行以下语句 -
SET GLOBAL innodb_ft_aux_table = "customers/blog"; SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE ORDER BY doc_id,position;
ngram 解析器空间处理
ngram 解析器在解析时会消除任何空格字符。例如,考虑以下带有标记大小为 2 的 TEXT -
"ab cd"解析为"ab"、"cd"
"a bc"解析为"bc"
ngram 解析器停用词处理
除了空格字符外,MySQL 还有一个停用词列表,其中包含各种被视为停用词的词。如果解析器在文本中遇到停用词列表中的任何单词,则该单词将从索引中排除。
ngram 解析器短语搜索
普通短语搜索将转换为 ngram 短语搜索。例如,搜索短语"abc"将转换为"ab bc",返回包含"abc"和"ab bc"的文档;搜索短语"abc def"将转换为"ab bc de ef",返回包含"abc def"和"ab bc de ef"的文档。不会返回包含"abcdef"的文档。
ngram 解析器词条搜索
对于自然语言模式搜索,搜索词条将转换为 ngram 词条的并集。例如,字符串"abc"(假设 ngram_token_size=2)会转换为"ab bc"。给定两个文档,一个包含"ab",另一个包含"abc",搜索词"ab bc"会与这两个文档匹配。
对于布尔模式搜索,搜索词会转换为 ngram 短语搜索。例如,字符串"abc"(假设 ngram_token_size=2)会转换为""ab bc""。给定两个文档,一个包含"ab",另一个包含"abc",搜索短语""ab bc""仅与包含"abc"的文档匹配。
ngram 解析器通配符搜索
由于 ngram FULLTEXT 索引仅包含 ngram,不包含有关词条开头的信息,因此通配符搜索可能会返回意外结果。以下行为适用于使用 ngram 全文搜索索引的通配符搜索:
如果通配符搜索的前缀词短于 ngram 标记大小,则查询返回所有包含以该前缀词开头的 ngram 标记的索引行。例如,假设 ngram_token_size=2,搜索"a*"将返回所有以"a"开头的行。
如果通配符搜索的前缀词长于 ngram 标记大小,则前缀词将转换为 ngram 短语,并且通配符运算符将被忽略。例如,假设 ngram_token_size=2,通配符搜索"abc*"将转换为"ab bc"。
使用客户端程序的 ngram 全文解析器
我们也可以使用客户端程序执行 ngram 全文解析器操作。
语法
要通过 PHP 程序执行 ngram 全文解析器,我们需要使用 mysqli 函数 query() 执行"Create"语句,如下所示 -
$sql = "CREATE TABLE blog (ID INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), DECLARE TEXT, FULLTEXT ( title, DECLARE ) WITH PARSER NGRAM, PRIMARY KEY(id) )ENGINE=INNODB CHARACTER SET UTF8MB4"; $mysqli->query($sql);
要通过 JavaScript 程序执行 ngram 全文解析器,我们需要使用 mysql2 库的 query() 函数执行"Create"语句,如下所示 -
sql = `CREATE TABLE blog (ID INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), DECLARE TEXT, FULLTEXT ( title, DECLARE ) WITH PARSER NGRAM, PRIMARY KEY(id) )ENGINE=INNODB CHARACTER SET UTF8MB4`; con.query(sql);
要通过 Java 程序执行 ngram 全文解析器,我们需要使用 JDBC 函数 execute() 执行"Create"语句,如下所示 -
String sql = "CREATE TABLE blog (ID INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), description TEXT," + " FULLTEXT ( title, description ) WITH PARSER NGRAM, PRIMARY KEY(id) )ENGINE=INNODB CHARACTER SET UTF8MB4"; statement.execute(sql);
要通过 Python 程序执行 ngram 全文解析器,我们需要使用 MySQL Connector/Python 的 execute() 函数执行"Create"语句,如下所示 -
create_table_query = 'CREATE TABLE blog(ID INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), description TEXT, FULLTEXT (title, description) WITH PARSER NGRAM, PRIMARY KEY(id)) ENGINE=INNODB CHARACTER SET UTF8MB4' cursorObj.execute(queryexpansionfulltext_search)
示例
以下是程序 -
$dbhost = "localhost";
$dbuser = "root";
$dbpass = "password";
$dbname = "TUTORIALS";
$mysqli = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if ($mysqli->connect_errno) {
printf("Connect failed: %s
", $mysqli->connect_error);
exit();
}
// printf('Connected successfully.
');
/*CREATE Table*/
$sql = "CREATE TABLE blog (ID INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), description TEXT, FULLTEXT ( title, description ) WITH PARSER NGRAM, PRIMARY KEY(id) )ENGINE=INNODB CHARACTER SET UTF8MB4";
$result = $mysqli->query($sql);
if ($result) {
printf("Table created successfully...!
");
}
//插入数据
$q = "INSERT INTO blog (id, title, description) VALUES (NULL, '', ''), (NULL, '', '')";
if ($res = $mysqli->query($q)) {
printf("Data inserted successfully...!
");
}
//我们将使用下面的语句来查看 ngram 如何标记数据:
$setglobal = "SET GLOBAL innodb_ft_aux_table = 'TUTORIALS/blog'";
if ($mysqli->query($setglobal)) {
echo "global innodb_ft_aux_table set...!";
}
$s = "SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE ORDER BY doc_id, position ";
if ($r = $mysqli->query($s)) {
print_r($r);
}
//显示数据(ngram解析器短语搜索);
$query = "SELECT * FROM blog WHERE MATCH (title, description) AGAINST ('')";
if ($r = $mysqli->query($query)) {
printf("Table Records:
");
while ($row = $r->fetch_assoc()) {
printf(
"ID: %d, Title: %s, Descriptions: %s",
$row["id"],
$row["title"],
$row["description"]
);
printf("
");
}
} else {
printf("Failed");
}
$mysqli->close();
输出
获得的输出如下所示 -
global innodb_ft_aux_table set...!mysqli_result Object
(
[current_field] => 0
[field_count] => 6
[lengths] =>
[num_rows] => 62
[type] => 0
)
Table Records:
ID: 1, Title: , Descriptions:
ID: 3, Title: , Descriptions:
var mysql = require("mysql2");
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "password",
}); //连接到 MySQL
con.connect(function (err) {
if (err) throw err;
// console.log("Connected successfully...!");
// console.log("--------------------------");
sql = "USE TUTORIALS";
con.query(sql);
//创建一个表...
sql = `CREATE TABLE blog (ID INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), description TEXT, FULLTEXT ( title, description ) WITH PARSER NGRAM, PRIMARY KEY(id) )ENGINE=INNODB CHARACTER SET UTF8MB4`;
con.query(sql);
//插入数据
sql = `INSERT INTO blog (id, title, description) VALUES
(NULL, '', ''),
(NULL, '', '')`;
con.query(sql);
//我们将使用下面的语句来查看 ngram 如何标记数据:
sql = "SET GLOBAL innodb_ft_aux_table = 'TUTORIALS/blog'";
con.query(sql);
//显示表详细信息;
sql = `SELECT * FROM blog WHERE MATCH (title, description) AGAINST ('')`;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});
});
输出
获得的输出如下所示 -
[ { id: 1, title: '', description: '' } ]
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class NgRamFSearch {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/TUTORIALS";
String username = "root";
String password = "password";
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
System.out.println("Connected successfully...!");
//creating a table that takes fulltext column with parser ngram...!
String sql = "CREATE TABLE blog (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255), description TEXT," +
" FULLTEXT ( title, description ) WITH PARSER NGRAM, PRIMARY KEY(id) )ENGINE=INNODB CHARACTER SET UTF8MB4";
statement.execute(sql);
//System.out.println("Table created successfully...!");
//inserting data to the table
String insert = "INSERT INTO blog (id, title, description) VALUES (NULL, '', '')," +
" (NULL, '', '')";
statement.execute(insert);
//System.out.println("Data inserted successfully...!");
//我们将使用下面的语句来查看 ngram 如何标记数据:
String set_global = "SET GLOBAL innodb_ft_aux_table = 'TUTORIALS/blog'";
statement.execute(set_global);
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE ORDER BY doc_id, position ");
System.out.println("Information schema order by Id...!");
while (resultSet.next()){
System.out.println(resultSet.getString(1)+" "+resultSet.getString(2));
}
//displaying the data...!
String query = "SELECT * FROM blog WHERE MATCH (title, description) AGAINST ('')";
ResultSet resultSet1 = statement.executeQuery(query);
System.out.println("table records:");
while (resultSet1.next()){
System.out.println(resultSet1.getString(1)+" "+resultSet1.getString(2)+ " "+resultSet1.getString(3));
}
connection.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
输出
获得的输出如下所示 -
Connected successfully...! Information schema order by Id...! 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 3 3 3 3 3 3 3 3 table records: 1
import mysql.connector
# 建立连接
connection = mysql.connector.connect(
host='localhost',
user='root',
password='password',
database='tut'
)
# 创建游标对象
cursorObj = connection.cursor()
# Create the blog table with NGRAM full-text parser
create_table_query = '''
CREATE TABLE blog (
id INT AUTO_INCREMENT NOT NULL,
title VARCHAR(255),
description TEXT,
FULLTEXT (title, description) WITH PARSER NGRAM,
PRIMARY KEY(id)
) ENGINE=INNODB CHARACTER SET UTF8MB4;
'''
cursorObj.execute(create_table_query)
print("Table 'blog' is created successfully!")
# 将字符集设置为 UTF8MB4
set_charset_query = "SET NAMES UTF8MB4;"
cursorObj.execute(set_charset_query)
print("Character set is set to UTF8MB4.")
# 将数据插入 blog 表
data_to_insert = [
('', ''),
('', '')
]
insert_query = "INSERT INTO blog (title, description) VALUES (%s, %s)"
cursorObj.executemany(insert_query, data_to_insert)
connection.commit()
print("Data inserted into the 'blog' table.")
# 查询INNODB_FT_INDEX_CACHE表,获取全文索引信息
query_index_cache_query = "SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE ORDER BY doc_id, position;"
cursorObj.execute(query_index_cache_query)
results = cursorObj.fetchall()
print("Results of INNODB_FT_INDEX_CACHE table:")
for row in results:
print(row)
#关闭游标和连接
cursorObj.close()
connection.close()
输出
获得的输出如下所示 -
Table 'blog' is created successfully! Character set is set to UTF8MB4. Data inserted into the 'blog' table. Results of INNODB_FT_INDEX_CACHE table:

