我有一个照片网站,我想支持标签,因为我的原始类别分桶开始失败(有些照片是家庭和假期,或学校和朋友)。是否有商定的标记数据库模式?
我仍然想支持将照片作为相册的一部分。
现在我有几张桌子:
相片
- 照片ID
- 相册ID
- 标题
- 日期
相片集
- 专辑编号
- 专辑名称
- 专辑日期
有多种有效的模式,随着标记项目数量的增加,每种模式对您需要的常见查询都有自己的性能影响:
就个人而言,我喜欢有一个标签表和一个将标签与项目相关联的链接表,因为它是非规范化的(没有重复的标签名称),并且我可以在必要时在链接表中存储额外的信息(例如项目被标记的时间)。
如果您感觉活泼并且想要简单的选择,您还可以添加一些非规范化数据,但代价是通过在标签表中存储使用计数或存储在项目表本身中使用的标签名称以避免命中所需的额外数据维护每个项目的链接表和标签表,这对于显示带有所有标签的多个项目以及简单的标签版本控制很有用......如果你喜欢那种东西;)
我在一个没有很多用户的小型系统中完成了这项工作,但我之前想知道是否有一种“公认”的方式来管理标签。在阅读了 insin 发布的链接和许多其他关于标记的博客文章之后,似乎可以接受的方式是将其完全标准化存储并在数据集太大时缓存某些内容。
由于它是多对多关系(每个标签可以属于任意数量的照片 - 每张照片可以有很多标签),关系数据库理论让您创建一个照片表、一个标签表和一个交叉引用表来链接它们。
photos
photoid
caption
filename
date
tags
tagid
tagname
phototags
photoid
tagid
这存在从非常大的数据集中选择的缩放问题,但所有标准化程度较低的模式也是如此(例如,通过文本字段进行排序和过滤可能总是比使用整数慢)。如果你变得和美味一样大,甚至可能是 StackOverflow,你可能不得不对你的标签集做一些缓存。
您必须面对的另一个问题是标签规范化问题。这与数据库规范化没有任何关系——它只是确保(例如)“StackOverflow”、“stackoverflow”和“stackoverflow”标签是相同的。许多地方不允许空格或自动将其删除。有时你会看到相同的标点符号 - 使“StackOverflow”与“Stack-Overflow”相同。自动小写是相当标准的。你甚至会看到特殊情况的规范化——比如让“c#”和“csharp”一样。
快乐标记!
我想到了这样的事情:添加这两个表
标签
照片标签
您也可以将其扩展到相册,在相册和标签之间有一个交叉表。
我建议看看成熟的开源软件是如何做到的。例如,Gallery像您一样将其元数据存储在数据库中,并且非常丰富。
不过,我认为您不会找到“标准”模式。我能想到的最接近的东西是 EXIF 元数据格式,它嵌入在图像文件本身(通过相机等)中。
如果您想要拥有数百万条记录的真正性能,您可以将标签存储在一个字段中,以逗号分隔并使用像 sphinxsearch 这样的全文索引/搜索守护程序检索记录。您只需要添加一个表格,其中列出了带有计数值的所有标签,以了解它们连接到项目的频率。
我知道这不是通常的方式,而且比纯数据库解决方案稍微复杂一些,但搜索标签相关项目确实非常快。
您也可以使用数据库引擎的全文搜索功能,但是当记录很多时,大多数引擎往往很慢。
如果是针对一个小项目,你可以按照自己的方式,接缝好的和正确的方式去做。但我只想与您分享这个其他解决方案。你觉得......怎么样 ?
关于如何处理标签的快速说明:
标记系统可以从非常严格定义的标记,其中创建新的标记需要明确的额外工作(想想 gmail 标签)到非常松散的系统,其中鼓励添加尽可能多的标签(想想 flickr,或标记可能应用转录的音频内容直接作为标签)。
一般来说,易于索引的媒体(文本!)应该有一个更严格的系统,因为内容已经是可搜索的。附加标签更多地仅用于分类,并且分类仅在不同用户将事物广泛分配到相同类别时才有用。如果你有原始文本,创建一个新标签几乎需要上帝的作为。
另一方面,更难索引的媒体(图像、视频、音频)应该有一个灵活的系统来鼓励使用许多标签,因为它们和其他元数据是您搜索时的唯一希望。
这很重要,因为您想要的数据库架构可能会根据您发现自己的范围而有所改变。
在我的应用程序 BugTracker.NET 中,我假设不会有太多错误。也许是几万,但不是几千万。这个假设允许我缓存标签和它们引用的项目的 ID。
在数据库中,标签在输入时与错误一起存储在逗号分隔的文本字段中。
添加或更改标签字段时,会启动一个后台线程,该线程会选择所有 bugid 及其标签,解析文本,构建一个映射,其中键是标签,值是具有该标签的所有 id 的列表. 然后我将该映射缓存在 Asp.Net Application 对象中。
下面是我刚刚描述的代码。
可以对代码进行优化,这样它就不会遍历所有错误,而是逐步修改缓存的地图,但即使未优化,它也能正常工作。
当有人使用标签进行搜索时,我会在地图中查找值,获取 id 列表,然后使用带有“where id in (1, 2, 3...)”子句的 SQL 获取这些错误。
public static void threadproc_tags(object obj)
{
System.Web.HttpApplicationState app = (System.Web.HttpApplicationState)obj;
SortedDictionary<string,List<int>> tags = new SortedDictionary<string,List<int>>();
// update the cache
DbUtil dbutil = new DbUtil();
DataSet ds = dbutil.get_dataset("select bg_id, bg_tags from bugs where isnull(bg_tags,'') <> ''");
foreach (DataRow dr in ds.Tables[0].Rows)
{
string[] labels = btnet.Util.split_string_using_commas((string) dr[1]);
// for each tag label, build a list of bugids that have that label
for (int i = 0; i < labels.Length; i++)
{
string label = normalize_tag(labels[i]);
if (label != "")
{
if (!tags.ContainsKey(label))
{
tags[label] = new List<int>();
}
tags[label].Add((int)dr[0]);
}
}
}
app["tags"] = tags;
}