带标记的MySQL全文布尔搜索
我以前从未从MYSQL进行过搜索,但我需要实现一个搜索。我有三个表:带标记的MySQL全文布尔搜索,mysql,search,full-text-search,tags,Mysql,Search,Full Text Search,Tags,我以前从未从MYSQL进行过搜索,但我需要实现一个搜索。我有三个表:articles,articles\u tags,和tags 表articles包含我想搜索的第一个内容,即title字段 表articles\u tags是一个数据透视表,它将articles和tags关联在一起articles\u tags有两个字段,分别是:articles\u id和tag\u id 而且,表tags包含我想搜索的第二个内容,即name字段 我的问题是,我需要一种方法来搜索title字段以及与该文章相关的
articles
,articles\u tags
,和tags
表articles
包含我想搜索的第一个内容,即title
字段
表articles\u tags
是一个数据透视表,它将articles
和tags
关联在一起articles\u tags
有两个字段,分别是:articles\u id
和tag\u id
而且,表tags
包含我想搜索的第二个内容,即name
字段
我的问题是,我需要一种方法来搜索title
字段以及与该文章相关的每个标记(tags.name
),并返回特定文章的相关性(或按相关性排序)
实现这一点的好方法是什么?我很确定不能只从一个查询中完成,所以两个查询,然后将相关性混合在一起就可以了
谢谢
编辑:忘了说,如果我能给匹配标签比匹配标题中的单词赋予更多权重,那就太棒了。我不是真的要求任何人写这篇文章,而是给我一些指导。我对PHP和MySQL都有点生疏。有趣的是,这是关于我在两天内看到的几乎相同问题的第三个问题,请查看以下两篇帖子:,这个快速演示查询远未优化,但应该是一个很好的起点
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_分数除以计数(t.id)来规范化它。抱歉,提供查询比解释如何进行查询更容易 从@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\u tags
的联接,那么肯定有一个标记。将标记名比较放在联接条件中,而不是放在WHERE
子句中
布尔模式使
MATCH()
在匹配时返回1.0,这使得它无法作为相关性度量。因此,在选择列表中进行额外的比较,以计算相关性。该值介于0.0和1.0之间。现在,我们可以通过将标记匹配排序视为具有1.0的相关性来进行更高的排序。您可能希望了解sphinx,以下是我在过去是如何做到这一点的。它看起来慢,但我想你会发现它不是
我增加了一点复杂性,以显示还有什么可以轻松完成。在本例中,一篇文章的部分标题匹配得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;
注意:这将不会返回没有标签的文章。我使用简单的连接来减少查询中的噪音,并突出显示正在进行评分的内容。要包含不带标记的文章,只需将联接设为左联接 在这个时候,建议您将搜索任务转移到一些实际上是为这个目的而编写的东西上,这是否值得呢 在我们的产品中,我们使用MySQL存储数据,但使用Lucene(通过Solr)索引所有数据,但这并不重要 值得一看,因为它的设置相对简单,功能非常强大,而且比试图操纵数据库来做你想做的事情容易得多
很抱歉,这不是问题的直接答案,我只是觉得在这种情况下,这种事情总是值得一提:)我看了这两个问题,但看不出它们与我的问题有什么关系。更有趣的是,这实际上是一个评论,而不是答案。嗨,比尔。我在一些地方读到,使用全文连接是不好的,因为它会迫使MySQL运行完整的表扫描,并失去宝贵的性能。“我现在要对此进行测试,看看我读到的是不是真的。@狗仔队,这取决于首先访问哪个表。MySQL使用嵌套循环算法进行连接,因此,如果您使用全文限制第一个表中匹配的行数,然后使用它查找连接表中的行,那么应该没有问题。但是如果您先扫描另一个表,然后在连接条件中使用全文,或者更糟糕的是,在全文搜索中使用第一个表的一列作为要搜索的模式(我不知道这是否可行),那么这将是昂贵的。您可能必须使用直联。