我希望能听到 SO 社区在如何实现这一点方面的建议。
我应该如何设计 "comments" 表格?以下是我现在使用的结构:
Comment
id
parent_post
parent_comment
author
points
这个结构需要做哪些改变?
我应该如何从这个表中获取细节以正确地显示它们? (欢迎使用任何编程语言进行实现。我只想知道如何以最佳方式完成它)
在实现此功能时需要注意哪些内容,以便减少CPU/数据库的负载?
提前感谢。
Comment
id
parent_post
parent_comment
author
points
这个结构需要做哪些改变?
我应该如何从这个表中获取细节以正确地显示它们? (欢迎使用任何编程语言进行实现。我只想知道如何以最佳方式完成它)
在实现此功能时需要注意哪些内容,以便减少CPU/数据库的负载?
提前感谢。
在数据库中存储树形结构有很多不同的解决方案。这取决于您是否想要检索子层次结构(例如 X 项的所有子代)或者只是想获取整个层次结构集并使用字典在内存中以 O(n) 的方式构建树。
您的表具有在一个请求中获取帖子下所有评论的优点,通过过滤 parentpost 可以实现。但是,由于您以教科书/朴素的方式定义了评论的父级,因此您必须在内存中构建树(见下文)。如果想从数据库中获取树形结构,则需要不同的树形结构存储方式:
参见我在这里描述的基于预计算的方法: http://www.llblgen.com/tinyforum/GotoMessage.aspx?MessageID=17746&ThreadID=3208 或者采用 CELKO 在此处描述的平衡树方式: using balanced trees described by CELKO here:
还有另一种方法: http://www.sqlteam.com/article/more-trees-hierarchies-in-sql
如果将整个层次结构在内存中获取并在那里构建树,则可以更有效率,因为查询非常简单:select .. from Comment where ParentPost = @id ORDER BY ParentComment ASC
在该查询之后,您可以使用一个仅保留元组 CommentID - Comment 的字典,在内存中构建树。现在,只需遍历结果集并即时构建树:每当处理到一条评论,您可以在字典中查找其父评论,然后还可以将当前正在处理的评论存储在该字典中。
还有几件事需要考虑...
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';
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>
因此,帖子“102”的根评论,其评论ID为“1”,其路径如下:
102.1
而其评论ID为“3”的子级,则为:
102.1.3
一些ID为“31”和“54”的“3”的子级则为:
102.1.3.31
102.1.3.54
UPDATE comments SET hide=true WHERE path @> '102.1.3';
但我不确定--这可能会增加不必要的开销。另外,我不知道ltree维护得如何。
对于小型层次结构(少于一千个项),您当前的设计基本上是可以的。
如果您想在特定级别或深度上获取,请向您的结构添加一个“级别”项,并在保存时计算它。
如果性能是一个问题,请使用一个良好的缓存。
thread_id:特定对象附加的所有评论的标识符
date:评论日期(允许按顺序获取评论)
rank:评论排名(允许按排名获取评论)