45

假设我有两列,关键字和内容。我对两者都有一个全文索引。我希望关键字中包含 foo 的行比内容中包含 foo 的行具有更高的相关性。我需要做什么才能使 MySQL 将关键字中的匹配权重高于内容中的匹配?

我正在使用“匹配”语法。

解决方案:

能够通过以下方式完成这项工作:

SELECT *, 
CASE when Keywords like '%watermelon%' then 1 else 0 END as keywordmatch, 
CASE when Content like '%watermelon%' then 1 else 0 END as contentmatch,
MATCH (Title, Keywords, Content) AGAINST ('watermelon') AS relevance 
FROM about_data  
WHERE MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE) 
HAVING relevance > 0  
ORDER by keywordmatch desc, contentmatch desc, relevance desc 
4

9 回答 9

96

创建三个全文索引

  • a) 关键字列上的一个
  • b) 一个在内容栏
  • c) 关键字和内容列中的一个

然后,您的查询:

SELECT id, keyword, content,
  MATCH (keyword) AGAINST ('watermelon') AS rel1,
  MATCH (content) AGAINST ('watermelon') AS rel2
FROM table
WHERE MATCH (keyword,content) AGAINST ('watermelon')
ORDER BY (rel1*1.5)+(rel2) DESC

关键是它只在列rel1中为您提供了查询的相关性keyword(因为您仅在该列上创建了索引)。 rel2做同样的事情,但对于content列。您现在可以应用您喜欢的任何权重将这两个相关性分数相加。

但是,您没有使用这两个索引中的任何一个进行实际搜索。为此,您使用第三个索引,它位于两列上。

(关键字,内容)上的索引控制您的召回。也就是返回什么。

两个独立的索引(一个仅针对关键字,一个仅针对内容)控制您的相关性。您可以在此处应用自己的加权标准。

请注意,您可以使用任意数量的不同索引(或者,可能会根据其他因素在查询时更改您在查询时使用的索引和权重......如果查询包含停用词,则仅搜索关键字......减少权重偏差如果查询包含超过 3 个单词...等)。

每个索引确实会占用磁盘空间,因此索引越多,磁盘就越多。反过来,mysql的内存占用更高。此外,插入将花费更长的时间,因为您有更多的索引要更新。

您应该针对您的情况对性能进行基准测试(小心关闭 mysql 查询缓存以进行基准测试,否则您的结果将出现偏差)。这不是谷歌级别的效率,但它非常简单并且“开箱即用”,几乎可以肯定它比您在查询中使用“喜欢”要好得多。

我发现它真的很好用。

于 2009-03-02T00:34:45.507 回答
19

实际上,使用 case 语句来制作一对标志可能是一个更好的解决方案:

select 
...
, case when keyword like '%' + @input + '%' then 1 else 0 end as keywordmatch
, case when content like '%' + @input + '%' then 1 else 0 end as contentmatch
-- or whatever check you use for the matching
from 
   ... 
   and here the rest of your usual matching query
   ... 
order by keywordmatch desc, contentmatch desc

同样,仅当所有关键字匹配的排名高于所有仅内容匹配时。我还假设关键字和内容的匹配是最高排名。

于 2009-02-13T21:51:51.687 回答
7

仅使用 2 个全文索引的更简单版本(取自 @mintywalker 的学分):

SELECT id, 
   MATCH (`content_ft`) AGAINST ('keyword*' IN BOOLEAN MODE) AS relevance1,  
   MATCH (`title_ft`) AGAINST ('keyword*' IN BOOLEAN MODE) AS relevance2
FROM search_table
HAVING (relevance1 + relevance2) > 0
ORDER BY (relevance1 * 1.5) + (relevance2) DESC
LIMIT 0, 1000;

这将针对 搜索两个完整索引的列,keyword并将匹配的相关性选择到两个单独的列中。我们将排除不匹配的项目(相关性 1 和相关性 2 都为零),并通过增加content_ft列的权重对结果进行重新排序。我们不需要复合全文索引。

于 2017-07-08T12:02:12.033 回答
0

几年前我这样做了,但没有全文索引。我手头没有代码(前雇主),但我记得很清楚这项技术。

简而言之,我从每一列中选择了一个“权重”。例如:

select table.id, keyword_relevance + content_relevance as relevance from table
   left join
      (select id, 1 as keyword_relevance from table_name where keyword match) a
   on table.id = a.id
   left join
      (select id, 0.75 as content_relevance from table_name where content match) b
   on table.id = b.id

请原谅这里的任何伪劣 SQL,我需要写任何 SQL 已经好几年了,而且我正在做这件事...

希望这可以帮助!

J.Js

于 2009-02-17T15:28:16.637 回答
0

在布尔模式下,MySQL 支持“>”和“<”运算符来更改单词对分配给行的相关值的贡献。

我想知道这样的事情是否可行?

SELECT *, 
MATCH (Keywords) AGAINST ('>watermelon' IN BOOLEAN MODE) AS relStrong, 
MATCH (Title,Keywords,Content) AGAINST ('<watermelon' IN BOOLEAN MODE) AS relWeak 
FROM about_data  
WHERE MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE) 
ORDER by (relStrong+relWeak) desc
于 2009-08-10T09:13:36.273 回答
-1

据我所知,MySQL全文搜索不支持此功能,但是您可以通过在关键字字段中多次重复该单词来达到效果。与其使用关键字“foo bar”,不如使用“foo bar foo bar foo bar”,这样 foo 和 bar 在关键字列中同样重要,并且由于它们多次出现,它们与 mysql 更相关。

我们在我们的网站上使用它并且它有效。

于 2009-02-13T20:34:21.857 回答
-1

好吧,这取决于您的确切含义:

我希望关键字中包含 foo 的行比内容中包含 foo 的行具有更高的相关性。

如果您的意思是关键字中包含 foo 的行应该位于内容中包含 foo 的任何行之前 ,那么我将执行两个单独的查询,一个用于关键字,然后(可能是懒惰的,仅在请求时)另一个在内容。

于 2009-02-16T03:26:42.917 回答
-1

我需要类似的东西并使用了 OP 的解决方案,但我注意到全文与部分单词不匹配。因此,如果“西瓜”作为单词的一部分出现在关键字或内容中(如 watermelonsalesmanager),则它不匹配,并且由于 WHERE MATCH 不包含在结果中。所以我玩弄了一下,将 OP 的查询调整为:

SELECT *, 
CASE WHEN Keywords LIKE '%watermelon%' THEN 1 ELSE 0 END AS keywordmatch, 
CASE WHEN Content LIKE '%watermelon%' THEN 1 ELSE 0 END AS contentmatch,
MATCH (Title, Keywords, Content) AGAINST ('watermelon') AS relevance 
FROM about_data  
WHERE (Keywords LIKE '%watermelon%' OR 
  Title LIKE '%watermelon%' OR 
  MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE)) 
HAVING (keywordmatch > 0 OR contentmatch > 0 OR relevance > 0)  
ORDER BY keywordmatch DESC, contentmatch DESC, relevance DESC

希望这可以帮助。

于 2011-02-01T12:06:03.117 回答
-4

如果指标只是所有关键字匹配比所有内容匹配更“有价值”,那么您可以只使用带有行数的联合。沿着这些思路。

select *
from (
   select row_number() over(order by blahblah) as row, t.*
   from thetable t
   where keyword match

   union

   select row_number() over(order by blahblah) + @@rowcount + 1 as row, t.*
   from thetable t
   where content match
)
order by row

对于比这更复杂的事情,如果您想对每一行应用实际重量,我不知道如何提供帮助。

于 2009-02-13T20:46:01.847 回答