39

我很想知道 Stack Overflow 的标记和搜索是如何构建的,因为它似乎工作得很好。

如果我想做以下所有事情,什么是好的数据库/搜索模型:

  1. 在各种实体上存储标签,(标准化程度如何?即实体、标签和实体标签表?)
    • 搜索具有特定标签的项目
    • 构建适用于特定搜索结果集的所有标签的标签云
    • 如何在搜索结果中显示每个项目的标签列表?

也许将标签以标准化形式存储是有意义的,但也可以作为空格分隔的字符串用于#2、#4 和#3 的目的。想法?

我听说 Stack Overflow 使用 Lucene 进行搜索。真的吗?我听过一些讨论 SQL 优化的播客,但没有关于 Lucene 的内容。如果他们确实使用 Lucene,我想知道有多少搜索结果来自 Lucene,以及“向下钻取”标签云是否来自 Lucene。

4

2 回答 2

57

哇,我刚写了一篇大文章,就这么哽咽着挂在上面,当我点击后退按钮重新提交时,标记编辑器是空的。啊啊啊

所以我又来了……

关于 Stack Overflow,原来他们使用SQL server 2005 全文搜索

关于@Grant 推荐的操作系统项目:

  • *DotNetKicks使用 DB 进行标记,使用 Lucene 进行全文搜索。似乎没有办法将全文搜索与标签搜索结合起来
  • Kigg使用 Linq-to-SQL 进行搜索和标签查询。两个查询都加入了 Stories->StoryTags->Tags。
  • 这两个项目都有一个 3-table 方法来标记,因为每个人似乎都普遍推荐

我还发现了一些我之前错过的关于 SO 的其他问题:

我目前正在为我提到的每个项目做的事情:

  1. 在数据库中,3 个表:Entity、Tag、Entity_Tag。我使用数据库:
    • 构建站点范围的标签云
    • 按标签浏览(即像 SO's /questions/tagged/ASP.NET这样的 URL )
  2. 对于搜索,我使用 Lucene + NHibernate.Search
    • 标签被连接成一个由 Lucene 索引的 TagString
      • 所以我拥有了 Lucene 查询引擎的全部功能(AND/OR/NOT 查询)
      • 我可以同时搜索文本按标签过滤
      • Lucene 分析器合并单词以获得更好的标签搜索(例如,“test”的标签搜索也会找到标记为“testing”的东西)
    • Lucene 返回一个潜在的巨大结果集,我将其分页为 20 个结果
    • 然后 NHibernate 通过 Id 从数据库或实体缓存中加载结果实体
    • 因此,搜索结果完全有可能对数据库产生 0 次点击
  3. 还没有这样做,但我想我可能会尝试找到一种方法来从 Lucene 中的 TagString 构建标签云,而不是再进行一次数据库攻击
  4. 还没有这样做,但我可能会将 TagString 存储在数据库中,这样我就可以显示实体的标记列表,而无需再进行 2 次连接。

这意味着每当修改实体的标签时,我必须:

  • 插入任何尚不存在的新标签
  • 从 EntityTag 表中插入/删除
  • 更新 Entity.TagString
  • 更新实体的 Lucene 索引

鉴于在我的应用程序中读取与写入的比率非常大,我认为我可以接受。唯一真正耗时的部分是 Lucene 索引,因为 Lucene 只能从其索引中插入删除,所以我必须重新索引整个实体才能更新 TagString。我对此并不感到兴奋,但我认为如果我在后台线程中执行此操作,那就没问题了。

时间会告诉我们...

于 2008-10-28T19:44:18.967 回答
5

我不知道它们是否符合最佳条件,但 DotNetKicks 和 Kigg 都是开源 digg 克隆实现。你可以看看他们是如何做标签和搜索的。

我最好的猜测没有经过深思熟虑:)

  1. 我从不喜欢将多个值序列化到一个字段中的想法,因此存储在一个字段中的分隔字符串对我没有吸引力……可能适用于具有树的邻接路径,但这些路径始终是有序的,标签不需要。这似乎会对您可能为找到它们所做的 LIKE 操作员工作增加负担。

所以我最初的想法可能是 Entity -> EntityTag <- Tag。

  1. 这种方法使通过 Tag 查找项目变得非常容易,通过 EntityTag 重新加入,收工。

  2. 您需要在此处进行辅助操作来为结果集选择不同的标签。所以 a.) 拉取结果集,b.) 规范化标签空间。我认为无论#1 的答案是什么,你都会这样做——即使将标签填充到一个字段中仍然会产生重复的标签(并且你必须对它们进行反序列化才能执行此操作——因此需要更多的工作,这是完全关系的另一个论点方法)。

  3. 还是很轻松的。这是序列化方法效果更好的一个领域。无需加入子标签,它就在实体中。也就是说,通过两个表连接提取 0..n 个标签对我来说似乎并不太具有挑战性。如果您正在谈论性能考虑,请先将其标准化,然后通过缓存或 denorm 进行优化。

另一种选择是“两者都做”。这感觉像是过早的优化,但您可以使用完全规范化的方法来支持任何以标签为中心的操作并在持久化后序列化以在实体中拥有非规范化版本。更多的工作,如果没有完全覆盖,一些可能会失去同步,但如果在您的用例中对完全标准化的方式存在真正的限制,那么两全其美。

Lucene 也很有趣,您可以在索引 IIRC 中声明特定的元数据,因此您也可以通过这种方式利用标签搜索。我的怀疑是,如果你在这条路上走得太远,那么你最终会在数据库中存储的内容与索引之间出现一些断开连接。我可以说 Lucene,它功能强大且易于使用——我相信 .Text 使用它是因为它的搜索功能,并且在切换到社区服务器之前它支持所有 weblogs.asp.net。如果 MSSQL 不在图片中/足够,我会坚持使用它进行全文搜索,解决数据库 imo 中的标签问题。

于 2008-10-25T14:27:42.477 回答