我正在努力实现这里所解释的: Creating a threaded private messaging system like facebook and gmail, 但是我并不完全理解Joel Brown的答案。任何人都可以解释一下。
这是我的数据库表与样本数据的样子(我假设我正确地填充它用于演示目的):
我需要显示一个基于LoginId的线程列表(最新的)在LINQ中查询的样子是什么? (我要问的是在一组消息线程中,在每个帖子中给我一条最新消息) - 就像这样在facebook上完成。
我需要在消息线程(LINQ)中显示所有消息 - >就像在Facebook上点击消息一样,你会看到整个“对话”。
请帮忙! 感谢
编辑 - >延续 乔尔,这是正确的吗?
乔尔,我有点困惑,你能解释一下(评论/问题用粗体):
这里的想法是,每次用户启动一个全新的线程/消息时,它都会以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(正确??)
这是我在迭代后所拥有的:
我觉得我很困惑,因为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;
如果我错了,请纠正我:)
答案 0 :(得分:71)
那么你为什么不问? :)
让我试着确定你对你的要求的理解。在我看来,你正在看一个线程列表(不是树)两个人之间的消息。我认为你可能想要允许更多人而不仅仅是两个人。这就像Facebook就像有人发布消息一样,然后任何数量的人都可以阅读它,然后开始添加评论。当您添加注释时,它会将您置于该线程中,并开始获取状态更新和电子邮件,告诉您线程中的活动等等。假设这就是您所追求的,那么我向Big Mike建议的架构并不完全符合您的要求。
请考虑以下内容:
这里的想法是,每次用户启动一个全新的线程/消息时,它都会以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回答的问题。表格看起来像这样:(可能过于简化了)
编辑#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