介绍
全文搜索 (FTS)是搜索引擎用来在数据库中查找结果的技术。 您可以使用它来加强网站上的搜索结果,如商店,搜索引擎,报纸等。
更具体地说,FTS检索不完全符合搜索条件的文档。 文档是包含文本数据的数据库实体。 这意味着当用户搜索“猫和狗”时,例如由FTS支持的应用程序能够返回分别包含单词(仅“猫”或“狗”)的结果,以不同顺序包含单词(“狗和猫”),或包含单词(“猫”或“狗”)的变体。 这为应用程序提供了猜测用户意味着什么并且更快地返回更多相关结果的优势。
从技术上讲,像MySQL这样的数据库管理系统(DBMS)通常允许使用LIKE
子句进行部分文本查找。 但是,这些请求往往在大数据集上表现不佳。 它们也仅限于精确地匹配用户的输入,这意味着即使存在具有相关信息的文档,查询也可能不会产生结果。
使用FTS,您可以构建更强大的文本搜索引擎,而无需在更高级的工具上引入额外的依赖关系。 在本教程中,您将使用MySQL 5.6来使用全文搜索来查询数据库,然后通过与搜索输入的相关性对结果进行量化,并仅显示最佳匹配。
先决条件
在开始本教程之前,您需要:
- 一个Ubuntu 16.04服务器通过遵循Ubuntu 16.04指南的初始服务器设置来设置 ,其中包括一个sudo非root用户和一个防火墙。
- 按照Ubuntu 16.04指南中的“ 如何安装MySQL”安装MySQL 5.6或更高版本。
第1步 - 创建测试数据
为了尝试全文搜索,我们需要一些数据。 在这一步中,我们将创建一个名为testdb
的数据库,其中包含一个名为news
的表格,我们将使用表示来自虚构新闻聚合器网站的文章的一些示例数据来填充数据库。
注意 :如果您有自己想要使用文本数据的表格,则可以跳到第2步,并在后续进行适当的替换。
首先访问MySQL控制台。 系统会提示您输入安装MySQL时设置的root密码。
mysql -u root -p
一旦你连接,你的提示将改为mysql>
。
接下来,创建一个名为testdb
的新数据库。 这个数据库将包含测试数据。
CREATE DATABASE testdb;
切换到默认使用testdb
数据库,所以你不必指定数据库的名称来创建或更新它的东西。
USE testdb;
接下来,在数据库中创建一个名为news
表,用于示例新闻聚合器的文章。
CREATE TABLE news (
id INT NOT NULL AUTO_INCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
author TEXT NOT NULL,
PRIMARY KEY (id)
);
让我们来看看这个命令的作用:
-
CREATE TABLE
是一个CREATE TABLE
的SQL命令,与其他许多数据库类似。 -
news
是表的名字。 -
title
,content
和author
是无限长的文本列。 -
NOT NULL
是用于标记不能有空值的列的声明(尽管它们可能包含空字符串)。 -
id
是表的主索引,具有特殊类型的AUTO_INCREMENT
,它使用下一个可用ID自动填充ID字段。
现在添加一些示例数据到表中。
INSERT INTO news (id, title, content, author) VALUES
(1, 'Pacific Northwest high-speed rail line', 'Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.', 'Greg'),
(2, 'Hitting the beach was voted the best part of life in the region', 'Exploring tracks and trails was second most popular, followed by visiting the shops and then traveling to local parks.', 'Ethan'),
(3, 'Machine Learning from scratch', 'Bare bones implementations of some of the foundational models and algorithms.', 'Jo');
让我们来看看这个命令的作用:
-
INSERT
插入数据。 -
INTO
指定数据应该插入的位置。 在这种情况下,这是news
表。 -
(id, title, content, author) VALUES
指定应该存储每个条目数据值的列。 - 最后三行是我们添加到表中的三行数据。 每个包含一个新闻网站的例子文章,
title
,一些content
和author
的名字。
每个条目还有一个唯一的id
标识符,自动输入数据库索引。 数据库索引是提高数据检索操作性能的数据结构。 该索引与主数据分开存储。 它会更新表格内容的任何变化,但需要额外的写入和较少的存储空间。 它的小尺寸和定制的数据结构使得索引比使用主表空间选择查询更有效。
现在我们有了一些数据,我们可以开始编写查询来使用FTS来搜索这些数据。
第2步 - 创建FTS索引和使用FTS功能
让我们为我们的文本列做一个索引,这样我们就可以使用FTS。
为此,我们将使用一个名为FULLTEXT
的MySQL独占命令。 这个命令告诉MySQL把我们希望能够用FTS搜索的所有字段放到一个内部索引中。
ALTER TABLE news ADD FULLTEXT (title, content, author);
这是通过结合所有的文本列和消毒他们(例如去除标点符号和使大写字母小写)。 现在这个索引被创建了,它将被任何改变源表的内容的SQL查询更新。
接下来,尝试使用MATCH() AGAINST()
函数执行“西雅图海滩”的全文搜索。
SELECT * FROM news WHERE MATCH (title,content,author) AGAINST ('Seattle beach' IN NATURAL LANGUAGE MODE)\G
命令的MATCH()
部分指定使用FTS对哪一组列进行索引; 它必须匹配您用来创建索引的列列表。 在这个例子中, AGAINST()
部分指定我们正在执行全文搜索的单词,即“西雅图海滩”。
IN NATURAL LANGUAGE MODE
,搜索词是直接从用户输入提供的,不需要任何预处理。 MySQL默认采用自然语言模式,所以你不必明确地指定它。
注意:与自然语言模式相比, 单词词干是另一个有用的FTS技术,它使索引剥离单词的词缀,只存储根部分。 例如,“fit”和“fit”这两个词在FTS和词干方面是相同的。
不幸的是,MySQL不支持分词。 Stemming是在MySQL的工作日志中 ,但是没有时间框架来实现和释放它。 FTS仍然有用,因为它比LIKE
子句还要快得多。 如果你想使用词干,你可以调查整合与雪球库 。
上面的查询结束处的\G
使得输出中的每一列都打印在一行上。 这可以使较长的结果更容易阅读。 上述命令的输出如下所示:
Output*************************** 1. row ***************************
id: 1
title: Pacific Northwest high-speed rail line
content: Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.
author: Greg
*************************** 2. row ***************************
id: 2
title: Hitting the beach was voted the best part of life in the region
content: Exploring tracks and trails was second most popular, followed by visiting the shops and then traveling to local parks.
author: Ethan
2 rows in set (0.00 sec)
没有一个条目包含“西雅图海滩”这个短语,但是因为我们使用了全文搜索,所以我们还得到了两个结果:第一行只包含单词“Seattle”,而第二行只包含单词“海滩”。 您可以尝试更改关键字来查看结果。
既然您可以在SQL查询中使用FTS函数来查找与搜索输入相关的行,则可以使这些结果更相关。
第3步 - 提炼FTS结果
有两种技术可以帮助使全文搜索结果更相关。 一个是根据结果的相关性分数进行过滤,另一个是使用IN BOOLEAN
从结果中排除特定的单词,并指定搜索词之间的最大距离。
使用相关性分数
结果的相关性分数量化了搜索词匹配的好坏,其中0完全不相关。 相关性得分基于多个因素,包括在特定文档中找到术语的频率以及包含该术语的文档数量。 MySQL的全文搜索文档进入计算这个数字的数学。
根据“到公园旅游”的查询获取每行的相关性分数。
SELECT id, MATCH (title,content,author) AGAINST ('traveling to parks') as score FROM news;
该命令的as score
部分将输出中的第二列标记为score
。 否则,它将被标记为用于填充它的命令,在这种情况下是MATCH (title,content,author) AGAINST ('traveling to parks')
。
结果将如下所示:
Output+----+----------------------+
| id | score |
+----+----------------------+
| 1 | 0.031008131802082062 |
| 2 | 0.25865283608436584 |
| 2 | 0 |
+----+----------------------+
3 rows in set (0.00 sec)
第三行的相关性分数为0,因为没有任何搜索项出现在其中。 第一行包含“旅行”一词,但不包括“到”或“公园”,相关性得分很低,为0.03
。 包含所有单词的第二行的相关性得分最高为0.25
。
您可以使用这些分数来首先返回最相关的结果,或仅返回超出某个相关范围的结果。 相关性分数因数据集而异,所以选择一个分界点需要手动调整。
以下命令运行相同的查询,但添加了两件事:
它仅显示具有非零相关性分数的行,通过添加
WHERE MATCH (title,content,author) AGAINST (' traveling to parks ') > 0
它通过添加
ORDER BY score DESC
按相关性排序结果
SELECT id, MATCH (title,content,author) AGAINST ('traveling to parks') as score FROM news WHERE MATCH (title,content,author) AGAINST ('traveling to parks') > 0 ORDER BY score DESC;
您需要重复WHERE
子句中的MATCH() AGAINST()
函数,因为SQL子句中包含的内容有限制。
输出结果如下所示:
Output+----+----------------------+
| id | score |
+----+----------------------+
| 2 | 0.25865283608436584 |
| 1 | 0.031008131802082062 |
+----+----------------------+
2 rows in set (0.01 sec)
首先显示最相关的结果,第2行,然后是不太相关的第一行。第三行根本没有显示,因为它的相关性分数是0。
您可以更改截止点以继续微调您的结果。 例如,如果使用0.1
而不是0
作为截止点,则只会返回第2行。
使用IN BOOLEAN
在第2步中,在指定查询词语时使用了IN NATURAL LANGUAGE
的默认模式。 还有另一种模式, IN BOOLEAN
,它允许你从搜索中排除特定的单词,定义输入中单词相距多远的范围,等等。
要从查询中省略术语,请使用带IN BOOLEAN
的减号运算符。 以下命令将返回包含单词“travelling”但不包含单词“Seattle”的结果。
SELECT * FROM news WHERE MATCH (title,content,author) AGAINST ('traveling -Seattle' IN BOOLEAN MODE)\G
结果将只显示第2行:
Output*************************** 1. row ***************************
id: 2
title: Hitting the beach was voted the best part of life in the region
content: Exploring tracks and trails was second most popular, followed by visiting the shops and then traveling to local parks.
author: Ethan
1 row in set (0.01 sec)
这是有效的,因为减号运算符告诉DMS使用相关性得分为0的排除词标记任何文档。在此模式下仅显示具有非零相关性得分的结果。
您也可以使用IN BOOLEAN MODE
来指定搜索项之间的最大距离。 这个距离用文字来衡量,重要的是包括搜索条件。 例如,“猫和狗”一词的距离为3。
以下命令返回结果,其中“旅行”和“英里”字样出现在它们之间不超过2个字。
SELECT * FROM news WHERE MATCH (title,content,author) AGAINST ('"traveling miles" @4' IN BOOLEAN MODE)\G
你会看到一个结果,匹配第2行的content
traveling the 140 miles
。
Output*************************** 1. row ***************************
id: 1
title: Pacific Northwest high-speed rail line
content: Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.
author: Greg
1 row in set (0.00 sec)
如果在原始命令中将@4
更改为@3
,则不会看到任何结果。
当搜索具有不同词汇表的非常大的文档时,通过搜索词之间的距离来限制搜索结果会很有帮助。 查询术语之间的差距越小,结果就越精确,但微调距离将取决于您正在使用的文档集。 例如,一套科学论文可能运作良好,只有3的小字差距,但搜索论坛帖子可能表现更好,差距为8或更高,这取决于您想要的结果有多宽或多少。
结论
在本指南中,您使用了MySQL中的全文搜索功能。 您在为文档驱动的数据库构建数据库模式时创建了索引,然后使用特殊的运算符在查询时查找最相关的结果。
如果您想进一步探索MySQL的FTS功能,您可以阅读关于全文搜索的MySQL 5.6官方文档 。