33

我正在开发一个可以支持线程注释的 Web 应用程序。我需要能够根据收到的票数重新排列评论。(与reddit中线程评论的工作方式相同)

我很想听听 SO 社区关于如何做到这一点的意见。

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

Comment
    id
    parent_post
    parent_comment
    author
    points

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

我应该如何从该表中获取详细信息以便以正确的方式显示它们?(欢迎以任何语言实现。我只是想知道如何以最好的方式做到这一点)

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

提前致谢。

4

4 回答 4

14

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

您的表格的优势在于,您可以通过过滤父帖子,一次性获取帖子的所有评论。当您以教科书/幼稚的方式定义评论的父级时,您必须在内存中构建树(见下文)。如果要从数据库中获取树,则需要另一种存储树的方法:请参阅我对基于预计算的方法的描述: http ://www.llblgen.com/tinyforum/GotoMessage.aspx?MessageID =17746&ThreadID=3208使用此处 CELKO 描述的平衡树

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

如果您在内存中获取层次结构中的所有内容并在那里构建树,由于查询非常简单,因此效率会更高: select .. from Comment where ParentPost = @id ORDER BY ParentComment ASC

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

于 2009-02-28T11:51:34.820 回答
6

还要考虑几件事...

1) 当您根据排名或日期说“排序类似于 reddit”时,您是指顶级还是整个事情?

2)当你删除一个节点时,分支会发生什么?你重新养育他们吗?在我的实现中,我认为编辑器将决定——要么隐藏节点并将其与可见的子节点一起显示为“评论隐藏”,要么隐藏评论及其子节点,或者核对整个树。重新养育应该很容易(只需将孩子的父母设置为被删除的父母),但是涉及整个树的任何事情似乎都很难在数据库中实现。

我一直在研究 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

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

102.1.3.31
102.1.3.54

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

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

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

于 2009-03-02T15:52:44.130 回答
4

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

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

如果性能是一个问题,请使用不错的缓存

于 2009-02-28T12:50:14.523 回答
3

我会在上面的表格中添加以下新字段:

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

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

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

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

  1. 在单个操作中获取线程中的所有评论
  2. 按日期或排名对线程中的评论进行排序

不幸的是,如果您想保留接近 SQL 标准的查询数据库,则必须在内存中重新创建树。一些数据库为分层数据提供特殊查询(fe Oracle)

./亚历克斯

于 2009-02-28T13:16:12.680 回答