创建一个像facebook和gmail这样的线程化私人消息系统

时间:2011-06-21 04:12:34

标签: php sql design-patterns database-design messaging

我正在创建一个类似于gmail和facebook的线程消息系统,其中收件箱列出显示主题的最新线程,发件人的姓名和最新消息的时间戳。

以下是我的表格设置方式:

users:
    user_id
    user_name

thread:
    thread_id
    title
    to_id
    to_keep
    to_read
    from_id
    from_keep
    date

message:
    message_id
    thread_id
    to_id
    from_id
    message_text
    date

我现在正在做的是当用户创建新消息时,它在线程表中创建一个新线程,然后在消息表中创建一个新消息,如果用户响应一个线程,它会复制当前线程表中的线程除了交换to_idfrom_id之外,然后根据它创建一条新消息。

此外,对于收件箱视图,我只能基于user_id查询所有线程。所以类似于SELECT * FROM thread WHERE to_id = 2 and to_keep = TRUE ORDER BY date DESC,或者如果我想查看发件箱中的邮件,那就像SELECT * FROM thread WHERE from_id = 2 and from_keep = TRUE ORDER BY date DESC

如果用户在有新消息时打开一个帖子,则to_read会更新为true UPDATE thread SET to_read = TRUE WHERE thread_id = 4

我觉得我过度复杂化这个过程,应该有更好的方法来做到这一点。

任何帮助或想法都将不胜感激。

这样我就可以从线程表中选择所有内容,然后使用user表进行连接,以显示我需要的所有内容。但是我觉得应该有更好的方法来做到这一点。

1 个答案:

答案 0 :(得分:45)

为什么不从用户对每条消息的视图中分离出消息关系?

我会通过消息上的自引用关系来进行线程化。换句话说,该消息具有“respond_to_message_id”列。

我不确定我理解为什么你有“to_id”。邮件是针对个人用户的吗?这似乎非常有限。我认为你要么没有收件人(即收件人是任何人都可以阅读的留言板),要么你可以指定多个收件人,就像电子邮件一样。也许你可以解释一下如何使用系统。

假设(为简单起见)您要发布到主板上,所以只有“from”很重要,那么您就拥有了消息表,其中包含用于线程的自引用关系,一个用户表,然后是一个交集表。用户和消息,用于存储每个用户已读取的消息。

这样,如果您想知道用户是否已阅读过消息,只需尝试在交叉表中读取给定消息的用户ID。如果不是,则该用户将读取该消息。

请注意,如果您希望拥有此设计的单个收件人,并且您希望拥有多个收件人,则可以使用交叉表来保存每封邮件的收件人列表。如果您有一个收件人交叉表,它可以作为您的读状态表执行双重操作。

编辑:ERD草图:

这是我正在谈论的内容的快速草图......

ERD Sketch

发件人是否已选择保留邮件标记在邮件本身上。如果消息是新线程的开始,则reply_to_message_id列为NULL,否则为父消息的message_id。可以有多个收件人,每个收件人都有自己保留邮件的能力,以及跟踪收件人阅读邮件的日期和时间的能力。

编辑2:替代ERD和查询最近的消息

@OP询问如何查询线程中的最新消息。答案取决于线程的形式。你可以有一个扁平线程,其中每条消息都到达线性消息流的末尾,或者你可以有一个树形线程,其中每条消息都有一个特定的父节点,除非它是线程的根。在上面的ERD中,可以以任一方式使用reply_to_message_id字段。如果线程是平的,则FK始终是根MESSAGE。如果线程是树形的,那么FK就是回复MESSAGE的直接父级。

如果您要运行的典型查询是“线程中最近的消息是什么?”并且你的线程是扁平的,那么你可以像这样使用SQL:

select top 1
  M.message_id
, M.sent_datetime
, M.title
, M.message_text
, S.user_id
, S.user_name
-- and anything else you want...
from MESSAGE M inner join USER S
  on M.sender_user_id = U.user_id
where M.reply_to_message_id = @ThreadRootMessageID
order by
  M.sent_datetime desc

另一方面,如果您的线程是树形的,并且这是一个您希望能够快速轻松地运行的查询,那么上面的ERD中的模式不是很容易使用。 SQL不擅长树木。您可以通过一点非规范化来解决问题。请参阅下面的ERD:

Tree Thread ERD

请注意,现在有一个FK显示直接父级,一个FK显示根。由于线程不受编辑 - 至少在编辑消息根被指向不同线程的情况下,这需要的非规范化并不意味着更新异常的风险,因此冗余不会太成问题。

如果您使用此ERD,则查询“线程X中的最新消息”与上面相同,但在where子句中使用M.thread_root_message_id而不是M.reply_to_message_id。