有没有在数据库中处理标签的通用方法?
我正在考虑在管道中使用tinytext。我认为添加另一个表和使用it可能会使它变得更加复杂,但收益很小。
你最喜欢的方式是什么?
在表中执行查询以查找匹配多个或单个标记的结果的正确方法是什么?
谢谢
发布于 2009-11-07 10:28:03
我会在这里传播一些异端。
大男孩,包括这个网站,为了可伸缩性的原因,对标签使用非规范化模式,在文本类型字段中为每行存储逗号、竖线或空格分隔的标签,并为带有计数的标签存储单独的表格。在插入或更新项目时,只需检查添加或删除了哪些标记,并相应地更新计数(分解为数组、旧标记字符串和新标记字符串并执行array_diff() )。
现在你有了一种廉价的方式来通过简单的SELECT * FROM tags来显示带有计数的标签云,而不需要花哨的查询。要找到带有给定名称标记的项目,只需执行LIKE '%TAG%',这将适用于小流量网站(例如,每天少于100k的页面浏览量)和小型数据集(同样,例如少于100k的记录)。除此之外,你可以使用全文搜索来加快速度,并最终使用像Lucene或Sphinx这样的合适的搜索引擎。
查找相关标签也很容易(Kohana特定的代码,如based、MySQL特定的代码):
$tags = array('foo', 'bar');
private function get_related_tags( $tags )
{
## Get db entries with specific tags and build array with counts
## is it cached already? ------------------------------------------------
$this->cache = Cache::instance();
$tags = array_filter( array_flip(array_flip($tags)) );
sort($tags);
$cache_name = implode('', $tags);
$cache = $this->cache->get( $cache_name );
if( $cache )
return $cache;
## not cached, fire up ---------------------------------------------------
$db = Database::instance();
## count tagged items ----------------------------------------------------
// build like string
$like = array();
foreach( $tags as $tag )
$like[] = "tags LIKE '%$tag%'";
$like = implode(' AND ', $like);
// get counts
$count = $db->query("SELECT count(id) AS count FROM `articles` WHERE $like")->current()->count;
## check what tags are related ------------------------------------------
$offset = 0;
$step = 300;
$related_tags = array();
while( $offset < $count )
{
$assets = $db->query("SELECT tags FROM `articles` WHERE $like ORDER BY id ASC LIMIT $step OFFSET $offset");
foreach($assets as $asset)
{
// tags
$input = explode( ' ', trim($asset->tags) );
foreach( $input as $k => $v )
{
if( $v == ''){
//do nothing, shouldnt be here anyway
}
elseif( array_key_exists($v, $related_tags) ){
$related_tags[$v]++;
}
else{
$related_tags[$v] = 1;
}
}
}
$offset += $step;
}
// remove already displayed from list
foreach( $tags as $tag )
unset( $related_tags[$tag] );
ksort($related_tags);
// set cache
$this->cache->set( $cache_name, array($related_tags, $count), 'related_tags_counts', 0);
return array($related_tags, $count);
}这并不是很便宜,所以我会缓存给定标签集的计数,直到我更改了文章表中的标签。
这种设置无论如何都不是完美的,但肯定有一些优点。模式很简单,获取标签云很简单,只需一个简单的查询(即无子查询)即可获得文章和标签。作为主要的缺点,我认为无法在不修改出现的每一行的情况下在系统范围内重命名或删除标记,但是嘿,你有多频繁地这样做?
目前我正在使用这个设置我的几个项目,它的工作方式就像一个梦想,但我必须承认这些不是高流量的网站(因此我喜欢),明年我将能够在繁忙的网站上测试它,但我非常确定它会做的。规范化纳粹可能会投票反对我,但我只是喜欢它的简单性,我很乐意用cpu周期来换取它。
实际上,我打算在前段时间发布这个标签系统,并询问专家们对它的看法,所以请随时留言。
传统上,对不起,我的英语,我相信这是有趣的=)
编辑
既然你已经在评论中提供了你的需求,我认为这个设置非常适合你。我已经在pastie here中发布了完整的标签模型,带有处理计数的方法,但是如果你了解Codeigniter,你会有家的感觉。只需这样使用它:
table TAGS: id, tag_name, tag_count
// insert new item/article
$tag_model->update_tags( $tags_str, null );
// update existing item
$tag_model->update_tags( $new_tags_str, $old_tags_str ); // $old_tags as stored in db
// delete item, you'll have to get item from db before deletion
$tag_model->update_tags( null, $old_tags_str ); 我已经修改了代码,因为markdown已经把它弄乱了,而且查询是mySQL风格的,而不是SQLite。
发布于 2009-11-07 03:22:17
实现一个简单的N:N关系。
项目:
-id
-name
-title
-foo
-bar
ItemTagRelations:
-Fkey itemId
-Fkey tagId
标签:
-id
-name
-etc
发布于 2009-11-07 03:28:45
是的,不要害怕规范化和将每个标签放在自己的记录中。这最终将是最灵活的,正确的索引也是最快的。
https://stackoverflow.com/questions/1689704
复制相似问题