MySQL 使用聚合函数进行全文搜索

问题描述 投票:0回答:1

我有一个连接到 MySQL 歌曲数据库的网站,并且正在努力改进该网站上的全文搜索。使用 MATCH() 的简单查询可以按预期工作,但是一旦我向 SELECT 语句添加聚合函数,MATCH() 就不再返回相关结果。

我使用的查询有点复杂,但我能够将其简化为一个最小的示例:

  1. 创建一个带有FTS索引的表,并插入三首歌曲:
CREATE TABLE `Song` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(300) DEFAULT NULL,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `FTS_Songs` (`title`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO Song (`title`)
VALUES ('The Morning Breaks'), ('The Spirit of God'), ('Now Let Us Rejoice');
  1. 使用 MATCH() 运行简单的查询。 这工作正常,将最相关的歌曲(“上帝的灵”)带到顶部:
SELECT id, title,
  MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
  MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance
FROM Song
GROUP BY id
ORDER BY exactTitleRelevance DESC, titleRelevance DESC;

正确结果: enter image description here

  1. 添加任何聚合函数(我的实际查询有一个 GROUP_CONCAT() 来聚合连接表中的数据;在本例中,为了简单起见,我使用 COUNT() 而不使用任何连接)。 这无法正常工作。一旦我添加聚合函数,MATCH() 就会中断并返回不准确的结果:
SELECT id, title,
  MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
  MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance,
  COUNT(*) AS aggregateColumn
FROM Song
GROUP BY id
ORDER BY exactTitleRelevance DESC, titleRelevance DESC;

错误结果: enter image description here

我做错了什么?是否可以在同一查询中同时使用 MATCH() 和 COUNT() 或 GROUP_CONCAT() 等聚合函数?

我在两台不同的服务器上尝试并得到了相同的结果 - 一台使用 MySQL 8.0.37,另一台使用 MySQL 8.3.0。

sql mysql full-text-search aggregate-functions
1个回答
0
投票

您可以使用

EXPLAIN
来检查MySQL如何执行查询:

EXPLAIN SELECT id, title,
  MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
  MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance,
  COUNT(*) AS aggregateColumn
FROM Song
GROUP BY id
ORDER BY exactTitleRelevance DESC, titleRelevance DESC;

这将给出:

id 选择类型 桌子 分区 类型 可能的键 key_len 参考 过滤 额外
1 简单 歌曲 索引 主要,FTS_歌曲 小学 4 3 100.00 临时使用;使用文件排序

聚合函数

COUNT()
GROUP BY
改变了 MySQL 处理查询的方式。 MySQL 使用“id”(主)作为索引,而不是 FTS_Songs(全文),导致
MATCH()
无法按预期工作。

为了确保

MATCH()
准确计算相关性:

  • 先使用CTE/子查询进行全文检索。
  • 然后应用任何聚合函数。

例如

WITH lookup AS (
SELECT 
  id,
  title,
  MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
  MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance
FROM Song
WHERE MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE)
)
SELECT 
  l.id, 
  l.title,
  l.exactTitleRelevance,
  l.titleRelevance,
  COUNT(*) AS aggregateColumn
FROM  lookup l
GROUP BY  l.id,  l.title, l.exactTitleRelevance,  l.titleRelevance;
© www.soinside.com 2019 - 2024. All rights reserved.