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: