我正在开发一个可以支持线程注释的Web应用程序。我需要能够根据收到的投票数重新安排评论。 (与reddit)
中线程评论的工作方式相同我很想听听SO社区对如何做的意见。
我应该如何设计评论表? 这是我现在使用的结构:
Comment
id
parent_post
parent_comment
author
points
应该对这种结构做出哪些改变?
如何从此表中获取详细信息以正确方式显示它们? (欢迎以任何语言实施。我只想知道如何以最佳方式进行)
在实现此功能时我需要注意哪些事项,以便减少CPU /数据库的负载?
提前致谢。
答案 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:附加到特定对象的所有注释的标识符
日期:评论日期(允许按顺序提取评论)
排名:评论排名(允许通过排名获取评论顺序)
使用这些字段,您将能够:
不幸的是,如果你想保持你的查询DB接近SQL标准,你将不得不在内存中重新创建树。一些DB为分层数据提供特殊查询(例如Oracle)
./亚历