MySQL带标签的全文布尔搜索

时间:2009-09-02 02:48:13

标签: mysql search full-text-search tags

我之前从未尝试过MYSQL,但我需要实现搜索。我有三个表,'articles','articles_tags'和'tags'。

'文章'包含我想要搜索的第一件事,即“标题”字段。

'articles_tags'是一个数据透视表,它将'articles'和'tags'关联在一起。 'articles_tags'有两个字段:'articles_id'和'tag_id'。

'tags'包含我要搜索的第二件事,即'name'字段。

我的问题是,我需要一种方法来搜索“标题”字段,以及与该文章相关的每个标记(“tags.name”),并返回文章的相关性(或按相关性排序)。 / p>

实施此方法的好方法是什么?我很确定只能从一个查询中完成这两个查询,然后将相关性“混合”在一起就可以了。

感谢。

编辑:忘了说,如果我可以为匹配标签提供更多权重而不是匹配标题中的单词,那就太棒了。我并不是真的要求任何人写这个东西,而是给我一些方向。我是PHP和MySQL的新手。

6 个答案:

答案 0 :(得分:2)

从@james.c.funk给出的答案开始,但做了一些更改。

SELECT a.id, a.title, 
  MATCH (a.title) AGAINST (?) AS relevance
FROM articles AS a
LEFT OUTER JOIN (articles_tags AS at
  JOIN tags AS t ON (t.id = at.tag_id AND t.name = ?))
  ON (a.id = at.article_id)
WHERE MATCH (a.title) AGAINST (? IN BOOLEAN MODE) 
ORDER BY IF(t.name IS NOT NULL, 1.0, relevance) DESC;

我假设您希望标记匹配与完整字符串匹配,而不是使用全文搜索。

同样使用一个左外连接而不是两个,因为如果满足articles_tags的连接,那么肯定会有一个标签。将标记名称比较放在连接条件内而不是WHERE子句中。

布尔模式使得MATCH()在匹配时返回1.0,这使得它无用作为相关性的度量。因此,在选择列表中进行额外比较以计算相关性。该值介于0.0和1.0之间。现在,我们可以通过将其标记为1.0的相关性来使标记匹配排序更高。

答案 1 :(得分:2)

此时是否值得,建议您将搜索工作卸载到实际为此目的编写的内容上?

在我们的产品中,我们使用MySQL来存储数据,但使用Lucene索引我们的所有数据(通过Solr - 但这无关紧要)。

值得一看,因为设置相对简单,它非常强大,比试图操纵数据库做你想要的更容易。

对不起,这不是问题的直接答案,我只是觉得这种情况在这种情况下总是值得一提:)

答案 2 :(得分:1)

以下是我过去的做法。它看起来很慢,但我认为你会发现它不是。

我添加了一点复杂性来展示其他可以轻松完成的工作。在此示例中,文章将获得1分的部分标题匹配,2分部分用于部分标记匹配,3分用于精确标记匹配,4分用于精确标题匹配。然后它按照分数添加和排序。

SELECT
  a.*,
  SUM(
    CASE WHEN a.title LIKE '%keyword%' THEN 1 ELSE 0 END
    +
    CASE WHEN t.name LIKE '%keyword%' THEN 2 ELSE 0 END
    +
    CASE WHEN t.name = 'keyword' THEN 3 ELSE 0 END
    +
    CASE WHEN a.title = 'keyword' THEN 4 ELSE END
  ) AS score
FROM article a, articles_tags at, tags t
WHERE a.id = at.article_id
AND at.tag_id=t.id
AND (a.title LIKE '%keyword%' OR t.name LIKE '%keyword%')
GROUP BY a.id
ORDER BY score;

注意:这不会返回没有标签的文章。我使用简单连接来减少查询中的噪音,并突出显示正在进行评分的内容。要包含没有标签的文章,只需将连接保留为连接。

答案 3 :(得分:0)

有趣的是,第3个问题是关于我在2天内看到的几乎相同的问题,请查看这两个帖子:12

答案 4 :(得分:0)

这个快速演示查询远未优化,但应该是一个很好的起点

SELECT * FROM
(SELECT a.id, a.title, 
  MATCH (a.title) AGAINST ('$s_search_term') AS title_score,
  SUM(MATCH (t.name) AGAINST ('$s_search_term')
) AS tag_score
FROM articles AS a
LEFT JOIN articles_tags AS at
  ON a.id = at.article_id
LEFT JOIN tags AS t
  ON t.id = at.tag_id
WHERE MATCH (a.title) AGAINST ('$s_search_term') 
  OR MATCH (t.name) AGAINST ('$s_search_term')
GROUP BY a.id) AS table1
ORDER BY 2*tag_score + title_score DESC

您可能希望将tag_score除以COUNT(t.id)来规范化。抱歉,提供查询比解释如何制作更容易。

答案 5 :(得分:0)

您可能需要查看sphinx,http://www.sphinxsearch.com/