问题 基于通用标签搜索相关项的算法


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

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

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


11664
2017-10-12 19:17


起源

如果您正在寻找的解决方案是SQL查询,那么了解您用于存储标记的模式会很有帮助。 - Emily
@Emily:只是与中间表的常规多对多关系。 - serg


答案:


这可能和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;

8
2017-10-12 19:41



谢谢你,我从阅读这个优雅的例子中学到了一些关于SQL的知识。 - prismofeverything
你的意思是 q1.questionid != q2.questionid? - Xeoncross
@Xeoncross:不,我的意思 <。它避免了两种自我匹配(其中 != 也避免)以及两次列出每一对(其中 != 才不是)。 - Keith Randall
是的,我跑完之后就看到了。用一个简单的 != 每对被列出两次。谢谢。 - Xeoncross


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

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
2017-10-12 19:44



你也许可以使用 WHERE ... tag.id EXISTS (... 而不是慢 IN (...) - Xeoncross


假设我有以下内容:

可标记实体表:

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)并计算得分的结果。


1
2017-10-12 20:02





假设一张桌子 Questions 使用主键列 Id, 一张桌子 Tags 使用主键列 Id,也是一个连接表 QuestionTags 使用复合主键 QuestionId 和 TagId 引用两个前表的主键,以下查询将给出所需的结果(在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

1
2017-10-12 20:01





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

1
2017-10-12 20:19