基于公共标签搜索相关项的算法

时间:2009-10-12 19:17:22

标签: mysql algorithm search tags many-to-many

让我们以StackOverflow问题为例。他们每个人都分配了多个标签。如何构建一个算法,根据它们有多少常见标签找到相关问题(按常用标签的数量排序)?

现在我想不出更好的事情,只需选择所有至少有一个共同标记到数组中的问题,然后循环遍历它们,为每个项目分配一些常用标记,然后对这个数组进行排序。

有更聪明的方法吗?完美的解决方案是单个SQL查询。

5 个答案:

答案 0 :(得分:8)

这可能与O(n ^ 2)一样糟糕,但它有效:

create table QuestionTags (questionid int, tag int);

select q1.questionid, q2.questionid, count(*) as commontags
from QuestionTags q1 join QuestionTags q2 
where q1.tag = q2.tag and q1.questionid < q2.questionid
group by q1.questionid, q2.questionid order by commontags desc;

答案 1 :(得分:2)

我没有时间优化WHERE IN()子句,因为死亡很慢。我也没有同时包含索引或指定引擎或排序规则,但这应该是有希望的。请指出任何错误,因为我实际上没有测试过这个草率的代码:

CREATE TABLE question (
    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    question MEDIUMTEXT
);

CREATE TABLE question_rel_tag (
    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    question_id INT(11) UNSIGNED NOT NULL,
    tag_id INT(11) UNSIGNED NOT NULL
);

CREATE TABLE tag (
    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL
);

SELECT question.id, question.title, question.question, count(tag.id) AS `count`
FROM question
INNER JOIN question_rel_tag ON (question.id = question_rel_tag.question_id)
INNER JOIN tag ON (question_rel_tag.tag_id = tag.id)
WHERE question.id != YOUR_QUESTION_ID_HERE
AND tag.id IN 
    (SELECT tag.id FROM question
    INNER JOIN question_rel_tag ON (question.id = question_rel_tag.question_id)
    INNER JOIN tag ON (question_rel_tag.tag_id = tag.id)
    WHERE question.id = YOUR_QUESTION_ID_HERE)
GROUP BY tag.id
ORDER BY `count` DESC

答案 2 :(得分:1)

假设某个表Questions包含主键列Id,表Tags也包含主键列Id,并且连接表QuestionTags使用复合主键QuestionIdTagId引用前两个表的主键,以下查询将提供所需的结果(在SQL Server 2005中)。

SELECT q1.Id AS Id1, q2.Id AS Id2,
   (SELECT COUNT(*) FROM QuestionTags qt1 INNER JOIN QuestionTags qt2
    ON qt1.QuestionId = q1.Id AND qt2.QuestionId = q2.Id AND qt1.TagId = qt2.TagId) AS TagCount
FROM Questions q1 INNER JOIN Questions q2 ON q1.Id < q2.Id
ORDER BY TagCount DESC

这可以改进到以下几点。

SELECT qt1.QuestionId AS Id1, qt2.QuestionId AS Id2, COUNT(*) AS TagCount
FROM QuestionTags qt1 INNER JOIN QuestionTags qt2
ON qt1.QuestionId < qt2.QuestionId AND qt1.TagId = qt2.TagId
GROUP BY qt1.QuestionId, qt2.QuestionId
ORDER BY TagCount DESC

答案 3 :(得分:1)

假设我有以下内容:

可标记实体表:

Taggable

id, stuff

标签表:

Tag

id, tagName

与Taggables

相关联的标签的连接表

TagInfo

taggableId tagId

然后:

SELECT COUNT(Taggable_1.id) AS score, Taggable_1.id FROM dbo.Taggable INNER JOIN dbo.TagInfo ON dbo.Taggable.id = dbo.TagInfo.taggableId INNER JOIN dbo.TagInfo TagInfo_1 ON dbo.TagInfo.tagId = TagInfo_1.tagId INNER JOIN dbo.Taggable Taggable_1 ON TagInfo_1.taggableId = Taggable_1.id WHERE (dbo.Taggable.id = 1) AND (Taggable_1.id <> 1) GROUP BY Taggable_1.id

会将tag-join表连接到自身(对于查询的问题ID;在上面的sql中为1)并计算得分的结果。

答案 4 :(得分:1)

select *
     , count(t.*) as matched_tag_count
  from questions q
  join tags_to_questions t
    on t.question_id = q.id
   and t.tag_id in (select tag_id
                      from tags_to_questions
                     where question_id = ?)
group
    by q.id
order
    by matched_tag_count desc