线程消息系统数据库架构设计

时间:2011-06-30 21:27:39

标签: asp.net database linq entity-framework database-design

我正在努力实现这里所解释的: Creating a threaded private messaging system like facebook and gmail, 但是我并不完全理解Joel Brown的答案。任何人都可以解释一下。

这是我的数据库表与样本数据的样子(我假设我正确地填充它用于演示目的): enter image description here

  1. 我需要显示一个基于LoginId的线程列表(最新的)在LINQ中查询的样子是什么? (我要问的是在一组消息线程中,在每个帖子中给我一条最新消息) - 就像这样在facebook上完成。

  2. 我需要在消息线程(LINQ)中显示所有消息 - >就像在Facebook上点击消息一样,你会看到整个“对话”。

  3. 请帮忙! 感谢

    编辑 - >延续 乔尔,这是正确的吗?

    enter image description here

    乔尔,我有点困惑,你能解释一下(评论/问题用粗体):

    这里的想法是,每次用户启动一个全新的线程/消息时,它都会以THREAD表中的新记录开头。然后将用户添加为THREAD_PARTICIPANT,并将消息内容添加到MESSAGE,该MESSAGE指向包含THREAD。从MESSAGE到USER的FK表示消息的作者。

    LoginId 1向LoginId2发送消息=>新记录被插入到MessageThread表中。此外,还会在MessageThreadId = 1,LoginId = 1(发件人)的MessageThreadParticipant记录中插入记录。并在Message表中插入一条新记录,MessageId = 1,MessageThreadid = 1,SenderLoginId = 1(正确??)

    这是我在迭代后所拥有的: enter image description here

    我觉得我很困惑,因为Loginid 2无法知道他有消息。 ??或者我可能需要在MessageThreadParticipant中插入2条记录? (发送者和接收者) - >这样两者都可以看到整个“对话”??

    EDIT2: 乔,我想我能做到这一点:

    SELECT
      Message.MessageId, Message.CreateDate, Message.Body, Login.Username, Message.SenderLoginId
    , (SELECT MessageReadState.ReadDate 
       FROM MessageReadState 
       WHERE MessageReadState.MessageId = Message.MessageId 
         ) as ReadDate
    FROM Message 
        INNER JOIN Login ON Message.SenderLoginId = Login.LoginId
        INNER JOIN MessageThreadParticipant mtp on mtp.MessageThreadId = Message.MessageThreadId 
    AND ( Message.MessageId in 
            ( SELECT Max(Message.MessageId)
              FROM MessageThreadParticipant INNER JOIN Message 
                ON MessageThreadParticipant.MessageThreadId = Message.MessageThreadId
              GROUP BY MessageThreadParticipant.MessageThreadId
            )
          )
    Where mtp.LoginId = 2
    ORDER BY Message.CreateDate DESC;
    

    如果我错了,请纠正我:)

2 个答案:

答案 0 :(得分:71)

那么你为什么不问? :)

让我试着确定你对你的要求的理解。在我看来,你正在看一个线程列表(不是树)两个人之间的消息。我认为你可能想要允许更多人而不仅仅是两个人。这就像Facebook就像有人发布消息一样,然后任何数量的人都可以阅读它,然后开始添加评论。当您添加注释时,它会将您置于该线程中,并开始获取状态更新和电子邮件,告诉您线程中的活动等等。假设这就是您所追求的,那么我向Big Mike建议的架构并不完全符合您的要求。

请考虑以下内容:

Schema

这里的想法是,每次用户启动一个全新的线程/消息时,它都会以THREAD表中的新记录开头。然后将用户添加为THREAD_PARTICIPANT,并将消息内容添加到MESSAGE,该MESSAGE指向包含THREAD。从MESSAGE到USER的FK表示消息的作者。

当用户阅读邮件时,他们会在MESSAGE_READ_STATE表中获得一个条目,表明他们已明确或隐式地标记了所读取的邮件,具体取决于您的要求。

当有人对线程中的初始消息发表评论时,会添加第二个MESSAGE,其中FK返回到原始THREAD,并且回复作者(用户)被添加到THREAD_PARTICIPANT表中。因此,消息被一个,两个甚至更多的参与者添加到线程中。

要在任何线程中获取最新消息,只需将MESSAGE中的前1个按创建日期(或身份密钥)降序排序,其中消息FK是感兴趣的线程。

要为用户获取最近更新的线程,请从创建日期降序排序的消息中获取与前1相关的THREAD,其中消息位于用户为THREAD_PARTICIPANT的线程中。

我担心在不破坏LinqPad的情况下,我永远无法在LINQ中陈述这些内容。如果你无法从上面看到我的偏差,我可以用表定义和一些SQL来充实答案。请在评论中提问。

编辑:澄清要求和实施

澄清要求:最初我正在考虑公开发布的消息以及评论的机会,而Shane则更多地使用了直接消息功能。在这种情况下,初始接收者需要在开始时包含在THREAD_PARTICIPANT表中。

为了清楚起见,我们在表格中放几行。这是一个场景,(为纪念加拿大日):用户1 DM用户2询问有关啤酒会议的问题。用户2回答有关在哪里见面和用户1回答的问题。表格看起来像这样:(可能过于简化了)

Sample Data Part 1 Sample Data Part 2

编辑#2:访问SQL以获取线程中所有消息的列表,具有读取状态...

使用@ OP的模式,此SQL将获取给定线程中的消息列表,并指示给定用户是否已读取每条消息。消息是最近的第一个订单。

SELECT 
  Message.MessageId
, Message.CreateDate
, Message.Body
, Login.Username
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     and MessageReadState.LoginId = 2) as ReadState
FROM (Message INNER JOIN Login ON Message.SenderLoginId = Login.LoginId) 
WHERE (((Message.MessageThreadId)=10))
ORDER BY Message.CreateDate DESC;

请注意,如果将其称之为公平,那就是通过子选择来获取读取状态。这是必要的,因为获取读取状态的部分标准要求使用外部连接无法满足的where子句。因此,您可以使用子选择从MessageReadState子表中确定所需的值(可能缺少)。

编辑3:用于获取给定用户的每个最新消息的所有线程的SQL ...

要获取给定用户参与的所有线程的列表,首先按最新消息排序,仅显示最新消息(每个线程1条消息),然后您将使用类似的查询上面的一个,除了不是通过他们的FK过滤消息到感兴趣的线程,你通过子查询过滤消息,子查询找到感兴趣的用户参与的每个线程中的最新消息。它将如下所示:

SELECT
  Message.MessageId
, Message.CreateDate
, Message.Body
, Login.Username
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     and MessageReadState.LoginId = 2) AS ReadState
FROM Message INNER JOIN Login ON Message.SenderLoginId = Login.LoginId
WHERE ( Message.MessageId in 
        ( SELECT Max(Message.MessageId)
          FROM MessageThreadParticipant INNER JOIN Message 
            ON MessageThreadParticipant.MessageThreadId = Message.MessageThreadId
          WHERE MessageThreadParticipant.LoginId=2
          GROUP BY MessageThreadParticipant.MessageThreadId
        )
      )
ORDER BY Message.CreateDate DESC;

答案 1 :(得分:2)

根据Joel Brown'answer的说法,您可以将LAST_MESSAGE_ID列添加到THREAD表中,然后获取包含最后消息的所有线程SQL变得非常简单。每次发送邮件时都必须更新此列。

为每个给定用户获取最新消息的所有主题

SELECT *
FROM THREAD T
INNER JOIN MESSAGE M ON T.LAST_MESSAGE_ID=M.MESSAGE_ID
INNER JOIN USER SENDER ON M.USER_ID=SENDER.USER_ID
LEFT JOIN MessageReadState MRS ON M.MESSAGE_ID=MRS.MESSAGE_ID AND MRS.USER_ID=2