如何实现标签系统

我想知道什么是最好的方法来实现一个标签系统,就像在SO上使用的那样。 我正在考虑这个,但我不能提出一个好的可扩展解决scheme。

我正在考虑有一个基本的三表解决scheme:有一个tags表,一个articles表和一个tag_to_articles表。

这是这个问题的最佳解决scheme,还是有其他的select? 使用这种方法,表格会在时间上变得非常大,而对于search来说,这并不是我假设的太高效。 另一方面查询执行速度并不重要。

我相信你会发现有趣的这篇博客文章: 标签:数据库模式

问题:你想有一个数据库模式,你可以在任何地方使用尽可能多的标签来标记书签(或博客文章或其他)。 之后,您想要运行查询来将书签约束为标记的联合或交集。 您还希望从search结果中排除(例如:减去)一些标签。

“妙不可言”的解决scheme

在这个解决scheme中,模式只有一个表,它是非规范化的。 这种types被称为“MySQLicious解决scheme”,因为MySQLicious将del.icio.us数据导入到具有此结构的表中。

在这里输入图像说明在这里输入图像说明

交叉(AND)查询“search+ web服务+ semweb”:

 SELECT * FROM `delicious` WHERE tags LIKE "%search%" AND tags LIKE "%webservice%" AND tags LIKE "%semweb%" 

联合(OR)查询“search | webservice | semweb”:

 SELECT * FROM `delicious` WHERE tags LIKE "%search%" OR tags LIKE "%webservice%" OR tags LIKE "%semweb%" 

Minus Query for“search + webservice-semweb”

 SELECT * FROM `delicious` WHERE tags LIKE "%search%" AND tags LIKE "%webservice%" AND tags NOT LIKE "%semweb%" 

“Scuttle”解决scheme

Scuttle将其数据组织在两个表格中。 该表“scCategories”是“标签”表,并获得了“书签”表的外键。

在这里输入图像说明

与“bookmark + webservice + semweb”的交集(AND)查询:

 SELECT b.* FROM scBookmarks b, scCategories c WHERE c.bId = b.bId AND (c.category IN ('bookmark', 'webservice', 'semweb')) GROUP BY b.bId HAVING COUNT( b.bId )=3 

首先,search所有书签标签组合,其中标签是“书签”,“webservice”或“semweb”(c.category IN('bookmark','webservice','semweb')),然后是书签已经将所有search到的三个标签都考虑在内(HAVING COUNT(b.bId)= 3)。

联合(OR)查询“书签| webservice | semweb”:只是省略HAVING子句,你有联盟:

 SELECT b.* FROM scBookmarks b, scCategories c WHERE c.bId = b.bId AND (c.category IN ('bookmark', 'webservice', 'semweb')) GROUP BY b.bId 

“书签+ webservice-semweb”的减号(排除)查询,即:书签和webservice AND NOT semweb。

 SELECT b. * FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND (c.category IN ('bookmark', 'webservice')) AND b.bId NOT IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = 'semweb') GROUP BY b.bId HAVING COUNT( b.bId ) =2 

离开HAVING COUNT会导致查询“bookmark | webservice-semweb”。


“托西”解决scheme

Toxi想出了一个三桌的结构。 通过表格“标签图”,书签和标签是相互关联的。 每个标签可以与不同的书签一起使用,反之亦然。 这个DB模式也被wordpress使用。 查询与“解决scheme”中的查询完全相同。

在这里输入图像说明

交集(AND)查询“书签+ webservice + semweb”

 SELECT b.* FROM tagmap bt, bookmark b, tag t WHERE bt.tag_id = t.tag_id AND (t.name IN ('bookmark', 'webservice', 'semweb')) AND b.id = bt.bookmark_id GROUP BY b.id HAVING COUNT( b.id )=3 

Union(OR)Query for“bookmark | webservice | semweb”

 SELECT b.* FROM tagmap bt, bookmark b, tag t WHERE bt.tag_id = t.tag_id AND (t.name IN ('bookmark', 'webservice', 'semweb')) AND b.id = bt.bookmark_id GROUP BY b.id 

“书签+ webservice-semweb”的减号(排除)查询,即:书签和webservice AND NOT semweb。

 SELECT b. * FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND (t.name IN ('Programming', 'Algorithms')) AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = 'Python') GROUP BY b.id HAVING COUNT( b.id ) =2 

离开HAVING COUNT会导致查询“bookmark | webservice-semweb”。

你的三表解决scheme没有错。

另一个select是限制可以应用于文章的标签数量(如SO中的5),并将其直接添加到文章表格中。

规范化数据库有其优点和缺点,就像硬连接到一个表中有益处和缺点。

没有说你不能这样做。 它违背了关系数据库范例来重复信息,但如果目标是性能,则可能不得不打破范例。

你提出的三个表的实现将用于标记。

但是堆栈溢出使用不同的实现。 它们以纯文本forms将标记存储到posts表中的varchar列,并使用全文索引来获取与标记匹配的post。 例如posts.tags = "algorithm system tagging best-practices" 。 我肯定杰夫曾经在某处提到过这个,但是我忘记了它在哪里。

所提出的解决scheme是最好的 – 如果我不能解决标签和文章之间的多对多关系,那么这不是唯一可行的方法。 所以我的投票是“是的,它仍然是最好的”。 我会对任何替代品感兴趣。

如果你的数据库支持可索引数组(比如PostgreSQL),我会推荐一个完全非规范化的解决scheme – 把标签作为一个string数组存储在同一张表上。 如果没有,将对象映射到标签的辅助表是最好的解决scheme。 如果您需要针对标签存储额外的信息,则可以使用单独的标签表,但为每个标签查找引入第二个联接没有意义。