我正在尝试使用mysql构建一个小型的练习搜索引擎。
每个练习可以有任意数量的搜索标签。
这是我的数据结构:
TABLE exercises
ID
title
TABLE searchtags
ID
title
TABLE exerciseSearchtags
exerciseID -> exercises.ID
searchtagID -> searchtags.ID
... exerciseSearchtags是一个多对多的连接表,表示练习和搜索标签之间的关系。
搜索引擎接受未知数量的用户输入关键字。
我想根据关键字/搜索标签匹配的数量对搜索结果进行排名。
这是我目前用于选择练习的sql。 CASE规则和WHERE规则都是动态生成的,每个关键字一个。例如,如果用户输入3个关键字,则将有3个CASE规则和3个WHERE规则。
SELECT
exercises.ID AS ID,
exercises.title AS title,
(
(CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END)+
(CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END)+
...etc...
(CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END)
) AS relevance
FROM
exercises
LEFT JOIN exerciseSearchtags
ON exerciseSearchtags.exerciseID = exercises.ID
LEFT JOIN searchtags
ON searchtags.ID = exerciseSearchtags.searchtagID
WHERE
searchtags.title LIKE CONCAT('%',?,'%') OR
searchtags.title LIKE CONCAT('%',?,'%') OR
...etc...
searchtags.title LIKE CONCAT('%',?,'%')
GROUP BY
exercises.ID
ORDER BY
relevance DESC
这几乎有效。然而,结果并没有按照我期望的顺序排列。
关于为什么会发生这种情况的最佳猜测是,在通过exercise.ID对行进行分组之前,正在计算相关分数。因此,如果左连接导致特定练习在结果集中出现10次,而另一练习出现4次,则第一次练习可能会获得更高的相关分数,即使它可能没有更多的关键字/搜索标签匹配。 / p>
有没有人对如何防止这种情况发生/解决此问题有任何建议/意见?
先谢谢你的帮助。
答案 0 :(得分:1)
我找到了解决上述问题的有效方法,并将其发布在此处,以防其他人遇到类似问题。
解决方案是使用子选择而不是case语句。以上是上面的代码转移,更正了。 (我不知道这是否是最好或最有效的解决方案,但它已经解决了我的麻烦,暂时,并且似乎能够合理地快速返回搜索结果。)
SELECT
exercises.ID AS ID,
exercises.title AS title,
(
(
SELECT COUNT(1)
FROM searchtags
LEFT JOIN exerciseSearchtags
ON exerciseSearchtags.searchtagID = searchtags.ID
WHERE searchtags.title LIKE CONCAT('%',?,'%')
AND exerciseSearchtags.exerciseID = exercises.ID
)+
(
SELECT COUNT(1)
FROM searchtags
LEFT JOIN exerciseSearchtags
ON exerciseSearchtags.searchtagID = searchtags.ID
WHERE searchtags.title LIKE CONCAT('%',?,'%')
AND exerciseSearchtags.exerciseID = exercises.ID
)+
...etc...
(
SELECT COUNT(1)
FROM searchtags
LEFT JOIN exerciseSearchtags
ON exerciseSearchtags.searchtagID = searchtags.ID
WHERE searchtags.title LIKE CONCAT('%',?,'%')
AND exerciseSearchtags.exerciseID = exercises.ID
)
) AS relevance
FROM
exercises
LEFT JOIN exerciseSearchtags
ON exerciseSearchtags.exerciseID = exercises.ID
LEFT JOIN searchtags
ON searchtags.ID = exerciseSearchtags.searchtagID
WHERE
searchtags.title LIKE CONCAT('%',?,'%') OR
searchtags.title LIKE CONCAT('%',?,'%') OR
...etc...
searchtags.title LIKE CONCAT('%',?,'%')
GROUP BY
exercises.ID
ORDER BY
relevance DESC
答案 1 :(得分:0)
分而治之。尝试将问题分解为更小的部分,而不是尝试在一个语句中完成所有操作。例如,首先创建一个临时表,其中包含至少包含一个搜索标记的所有练习。然后进行第二遍以对临时表中的每个练习进行排名。最后选择按排名排序的结果。
答案 2 :(得分:0)
我只为MSSQL而不是mySQL做了类似的事情......所以这可能根本不相关,但值得一试:)
我必须将CASE作为ORDER BY子句的一部分,以使其正确地拾取它,例如:
ORDER BY CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END + CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END + ...etc... CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END DESC
同时也将它们留在SELECT中,这样我就可以在页面上输出相关性(按要求)
无论哪种方式,祝你好运!