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

Posted

技术标签:

【中文标题】创建一个像 facebook 和 gmail 这样的线程私人消息系统【英文标题】:Creating a threaded private messaging system like facebook and gmail 【发布时间】:2011-09-19 04:49:14 【问题描述】:

我正在创建一个类似于 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

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

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

这样,我只需从线程表中选择所有内容,然后与用户表进行连接以显示我需要的所有内容。不过我觉得应该有更好的方法来做到这一点。

【问题讨论】:

为什么线程需要一个to_id和一个from_id?消息是将发送者和接收者绑定在一起的东西。线程只是消息的容器。还是我误会了? 不,你是对的。我的查询很难选择应该在收件箱中的所有线程以及来自线程中最新消息的信息。这有意义吗? 【参考方案1】:

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

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

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

假设(为简单起见)您要发布到板,所以只有“发件人”很重要,那么您就有了消息表,具有线程的自引用关系、用户表,然后是两者之间的交集表存储每个用户已阅读的消息的用户和消息。

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

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

编辑:ERD 草图:

这是我正在谈论的内容的简要概述......

发件人是否选择保留邮件会在邮件本身上标明。如果消息是新线程的开始,则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:

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

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

【讨论】:

嗨乔尔,你能不能告诉我你会如何设置你的桌子,我不太了解你会怎么做。谢谢! 感谢您的贡献,但我看不出这是如何创建线程消息的。例如,1 个线程可以返回 5 条消息和第四条消息。这有意义吗? 消息 ID 1 的 reply_to_message_id = null。消息 ID 2 的 reply_to_message_id = 1。消息 ID 3 的 reply_to_message_id = 2,依此类推。线程由指向其父级的每个子消息定义。线程中的第一条消息没有父消息。每个回复都会。某人收件箱中的内容要么是指向他们作为发件人的消息 (MESSAGE.sender_user_id),要么是他们已发送的内容(通过 RECIPIENT 加入)。 sender_keep/recipient_keep 是“标记为已读”功能。消息之间的关系用于在视觉上将它们分类为线程。 @chovy 线程根被用作线程上所有内容的任意分组属性,而无需沿着每个边缘一直向上和向下遍历树。您并不绝对需要它,因为您可以通过程序得出它,但可以说“当您在附近时”做笔记会很方便。 @user1107173 我的答案中的模型支持定义单个收件人的 1:1 和 1:many 消息。向组发送消息有点棘手,因为组是许多收件人的一个地址。我的答案中的模型可以通过几种方式为群体工作。 1)您可以将组替换为组的单个成员。这隐藏了使用组的事实,因此它可能并不理想。 2)您可以将组和所有成员都放在收件人表中,并带有一个代码以指示它是组还是组成员或个人收件人。

以上是关于创建一个像 facebook 和 gmail 这样的线程私人消息系统的主要内容,如果未能解决你的问题,请参考以下文章

我想创建像 GMAIL 这样的新消息框 [关闭]

像 Facebook 时间线一样创建 tableview ...... iPhone 应用程序 [关闭]

我怎样才能像这样的 Facebook 帖子一样显示专辑图片?

facebook喜欢与添加或删除朋友的选项聊天

对于像 Facebook 这样的大型社交网站来说,最佳的 RDBMS 解决方案是啥?

如何添加像gmail应用程序android这样的底部圆形图标?