【问题标题】:Fulltext search - Query equivalent to like全文搜索 - 相当于 like 的查询
【发布时间】:2021-07-27 13:48:35
【问题描述】:

我正在尝试改进在非索引列上使用 like 运算符的查询。我四处寻找并提出全文搜索选项。

我正在关注这个例子 https://dev.mysql.com/doc/refman/8.0/en/fulltext-stopwords.html#fulltext-stopwords-stopwords-for-innodb-search-indexes

这里采取的步骤:

CREATE TABLE `test.my_stopwords`(value VARCHAR(30)) ENGINE = INNODB;
INSERT INTO `test.my_stopwords`(`value`) VALUES ('admin');
INSERT INTO `test.my_stopwords`(`value`) VALUES ('journalist');

CREATE TABLE `test.Users` (
`Id` varchar(26) NOT NULL,
`Roles` text,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

SET GLOBAL innodb_ft_server_stopword_table = 'test/my_stopwords';

CREATE FULLTEXT INDEX idx_users_role ON Users(Roles);

我正在尝试进行与此等效的全文搜索:

SELECT Id
FROM Users
WHERE Roles LIKE '%admin%' OR Roles LIKE '%journalist%';

想出了这个:

SELECT Id
FROM Users
WHERE MATCH(Roles) AGAINST('admin journalist' IN BOOLEAN MODE);

这里有一些可能的角色值

'master_member allow_news system_user tradersclub system_admin'
'system_user system_admin tradersclub journalist mover'
'allow_news master_member system_user system_admin'
'allow_news system_user system_admin'
'master_member allow_news system_user system_admin system_collab tradersclub'
'system_user system_admin editor_scoop'
'master_member system_user journalist tradersclub'
'allow_news master_member system_user system_admin'
'system_user system_admin'
'allow_news master_member system_user system_admin'
'allow_news master_member system_user system_admin'
'allow_news system_user system_admin master_member system_collab tradersclub'
'system_user system_admin editor_scoop'
'system_user master_member system_admin'
'system_user system_admin tradersclub'
'system_admin system_user master_member tradersclub allow_news allow_ideas'
'system_user journalist master_member system_admin system_collab tradersclub allow_ideas allow_news support moderator c_manager influencer'

第一个返回 128 行,第二个返回 19 行。我做错了什么?

【问题讨论】:

  • 你能提供样本数据吗? %admin% 和 %journalist% 将找到部分文本。如果我没记错的话应该忽略停用词。
  • @PeterKoltai 为角色添加了可能的值
  • 我认为完整的单词 'admin' 和 'journalist' 被排除在第二个查询之外,但当 'admin' 作为单词的一部分出现时,例如 'system_admin' 则不会。另一方面,第一个查询将为 %admin% 找到“system_admin”和“anythin_admin”
  • 所以区别可能是第二个只在单词的一部分时找到“admin”和“journalist”,而在整个单词时不找到;第一个返回所有相似的东西。
  • 但应该是等价的吧?为什么会发生这种行为?

标签: mysql


【解决方案1】:

对于通配符,全文布尔模式搜索和 LIKE 的工作方式有所不同。我将使用您在问题中提供的 17 行数据,为简单起见,我限制为一个搜索短语来显示差异。

  • Roles LIKE ('%admin%') 将返回包含 admin 之外的每一行,否 问题,它位于 Roles 列的字符串中。
  • MATCH(Roles) AGAINST('journalist' IN BOOLEAN MODE) 只会查找包含与 journalist 完全相同的一个单词的行。
  • MATCH(Roles) AGAINST('system*' IN BOOLEAN MODE) 将查找包含以 system 开头的任何单词的行。

考虑基于上述内容的以下查询,针对您的 17 行数据进行:

SELECT count(*)
FROM Users
WHERE Roles LIKE ('%admin%');

结果将是 16,因为在 Roles 的某处只有一行(第 7 行:master_member system_user journalist tradersclub)不包含 admin。 现在,试试这个:

SELECT count(*)
FROM Users
WHERE MATCH(Roles) AGAINST('journalist' IN BOOLEAN MODE);

结果3,你有3行,在Roles中包含journalist作为一个完整的单词。 这个呢:

SELECT count(*)
FROM Users
WHERE MATCH(Roles) AGAINST('system*' IN BOOLEAN MODE);

现在你得到了 17,因为样本中的每一行在Roles 中至少有一个词 system 开头。

你的问题来了:在布尔模式下使用全文搜索时你不能在单词的开头添加通配符。看看这个:

SELECT *
FROM Users
WHERE MATCH(Roles) AGAINST('*admin*' IN BOOLEAN MODE);

人们会期望找到包含一些包含 admin 的单词的行,而你有很多。 但实际上上面的查询结果算0

这就是为什么你的方法会遇到奇怪的结果。

综上,我认为全文搜索不适合这类问题。你有角色和用户,每个用户可以有0..n个角色,每个角色都可以分配给 0..n 个用户。在关系数据库中,这是一种经典的多对多关系。您可以有一个角色表、一个用户表以及一个将用户连接到角色的连接表。这可以在正确编入索引时提高查询效率。

【讨论】:

  • 我理解你的观点。数据有很多问题,没有标准化。我只是有这个没有执行的“喜欢”查询。
  • LIKE 与前导和关闭 % 永远不会在大型数据集上表现良好,我相信您也知道。如果您只使用关闭 %,那会好得多,但我认为这并不能解决您的问题。
猜你喜欢
  • 1970-01-01
  • 2014-10-18
  • 2023-03-16
  • 1970-01-01
  • 2018-05-17
  • 2013-04-21
  • 1970-01-01
  • 2019-04-23
  • 2011-10-04
相关资源
最近更新 更多