我如何实现线程注释?

时间:2009-02-28 11:22:10

标签: performance database-design comments query-optimization table-structure

我正在开发一个可以支持线程注释的Web应用程序。我需要能够根据收到的投票数重新安排评论。 (与reddit

中线程评论的工作方式相同

我很想听听SO社区对如何做的意见。

我应该如何设计评论表? 这是我现在使用的结构:

Comment
    id
    parent_post
    parent_comment
    author
    points

应该对这种结构做出哪些改变?

如何从此表中获取详细信息以正确方式显示它们? (欢迎以任何语言实施。我只想知道如何以最佳方式进行)

在实现此功能时我需要注意哪些事项,以便减少CPU /数据库的负载?

提前致谢。

4 个答案:

答案 0 :(得分:14)

在数据库中存储树是一个有许多不同解决方案的主题。这取决于你是否也要检索子层次结构(所以项目X的所有子项)或者你只想获取整个层次结构集并使用字典以O(n)方式在内存中构建树。

您的表的优势在于您可以通过过滤父帖来获取帖子中的所有评论。当您以教科书/天真的方式定义注释的父级时,您必须在内存中构建树(参见下文)。如果要从数据库中获取树,则需要使用不同的方法来存储树: 在这里查看我对基于预计算的方法的描述: http://www.llblgen.com/tinyforum/GotoMessage.aspx?MessageID=17746&ThreadID=3208 或者using balanced trees described by CELKO here

或另一种方法: http://www.sqlteam.com/article/more-trees-hierarchies-in-sql

如果你在内存中获取层次结构中的所有内容并在那里构建树,那么由于查询非常简单,它可以更有效:select from from Comment where ParentPost = @id ORDER BY ParentComment ASC

在该查询之后,您只使用1个字典在内存中构建树,该字典跟踪元组CommentID - Comment。现在,您可以浏览结果集并动态构建树:您遇到的每个注释,您都可以在字典中查找其父注释,然后将当前处理的注释也存储在该字典中。

答案 1 :(得分:5)

还要考虑一些事情......

1)当你根据排名或日期说“排序像reddit”时,你的意思是顶级还是整个?

2)删除节点时,分支会发生什么?你重新养育他们吗?在我的实现中,我认为编辑将决定 - 隐藏节点并将其显示为“隐藏评论”以及可见的孩子,隐藏评论及其子项,或者整个树。重新父母应该很容易(只需将chidren的父级设置为已删除的父级),但是涉及整个树的任何内容在数据库中实现似乎都很棘手。

我一直在关注PostgreSQL的ltree模块。它应该使涉及树的部分的数据库操作更快一些。它基本上允许您在表中设置一个类似于:

的字段
ltreetest=# select path from test where path <@ 'Top.Science';
                path                
------------------------------------
 Top.Science
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology

但是,它不能确保任何类型的参照完整性。换句话说,你可以拥有“Top.Science.Astronomy”的记录而没有“Top.Science”或“Top”的记录。但是它让你做的是:

-- hide the children of Top.Science
UPDATE test SET hide_me=true WHERE path @> 'Top.Science';

-- nuke the cosmology branch
DELETE FROM test WHERE path @> 'Top.Science.Cosmology';

如果与使用存储过程的传统“comment_id”/“parent_id”方法相结合,我认为你可以充分利用这两个方面。您可以使用“路径”快速遍历数据库中的注释树,并通过“comment_id”/“parent_id”确保参照完整性。我想象的是:

CREATE TABLE comments (
comment_id SERIAL PRIMARY KEY,
parent_comment_id int REFERENCES comments(comment_id) ON UPDATE CASCADE ON DELETE CASCADE,
thread_id int NOT NULL  REFERENCES threads(thread_id) ON UPDATE CASCADE ON DELETE CASCADE,
path ltree NOT NULL,
comment_body text NOT NULL,
hide boolean not null default false
);

评论的路径字符串看起来像是

<thread_id>.<parent_id_#1>.<parent_id_#2>.<parent_id_#3>.<my_comment_id>

因此,带有comment_id为“1”的线程“102”的根注释将具有以下路径:

102.1

而comment_id为“3”的孩子将是:

102.1.3

一些身份为“31”和“54”的“3”的孩子将是:

102.1.3.31
102.1.3.54

要隐藏节点“3”及其孩子,请发出以下命令:

UPDATE comments SET hide=true WHERE path @> '102.1.3';

我不知道 - 它可能会增加不必要的开销。另外,我不知道ltree的维护情况如何。

答案 2 :(得分:3)

您当前的设计基本上适用于小型层次结构(少于千件)

如果要获取certian级别或深度,请在结构中添加“级别”项并将其作为保存的一部分进行计算

如果性能问题,请使用合适的缓存

答案 3 :(得分:3)

我将以下新字段添加到上面的表格中:

  • thread_id:附加到特定对象的所有注释的标识符

  • 日期:评论日期(允许按顺序提取评论)

  • 排名:评论排名(允许通过排名获取评论顺序)

使用这些字段,您将能够:

  1. 在单个操作
  2. 中获取线程中的所有注释
  3. 按日期或等级在线程中排序评论
  4. 不幸的是,如果你想保持你的查询DB接近SQL标准,你将不得不在内存中重新创建树。一些DB为分层数据提供特殊查询(例如Oracle)

    ./亚历