Ruby on rails 使用Mongoid的MongoDB对话/私有消息模式

Ruby on rails 使用Mongoid的MongoDB对话/私有消息模式,ruby-on-rails,ruby,mongodb,mongoid,schema-design,Ruby On Rails,Ruby,Mongodb,Mongoid,Schema Design,为了更加熟悉Rails和Mongoid,我正在Rails中构建一个论坛系统。我想添加的一个功能是一个私人消息系统论坛,用户可以使用它互相发送消息。 在模式设计方面,我可以想到两种解决方案: 解决方案1 用户和消息是使用“has\u many”和“belown\u to”相互链接的独立文档 用户文档 已发送多条消息,:class\u name=>'Message',:reverse\u of=>:Message\u发送者 是否收到多封:邮件,:class\u name=>Message',:rev

为了更加熟悉Rails和Mongoid,我正在Rails中构建一个论坛系统。我想添加的一个功能是一个私人消息系统论坛,用户可以使用它互相发送消息。 在模式设计方面,我可以想到两种解决方案:

解决方案1 用户和消息是使用“has\u many”和“belown\u to”相互链接的独立文档

用户文档

已发送多条消息,:class\u name=>'Message',:reverse\u of=>:Message\u发送者

是否收到多封:邮件,:class\u name=>Message',:reverse\u of=>:Message\u收件人

消息文档

字段:已创建,类型:DateTime,默认值:->{Time.now}

字段:内容,类型:字符串

属于:message\u sender,:class\u name=>“User”,:Reverse\u of=>:messages\u sent

属于:邮件收件人:class\u name=>'User',:reverse\u of=>:messages\u received

为了向用户显示其收件箱,我会查看由
排序的
某个用户收到的
邮件:创建
并进行筛选,这样我就有了一个唯一的发件人ID列表,这些ID是在上次将邮件发送给
某个用户时排序的

然后,为了显示一个特定的对话,我只需获取两个参与者之间的所有消息,并根据时间戳将它们交错:

messages\u in=some\u user.messages\u received.where(:message\u sender=>selected\u received)

messages\u out=some\u user.messages\u sent.where(:message\u recipient=>selected\u recipient)

我不喜欢这个解决方案,因为它涉及到多次使用“where”查询命中消息集合,以及大量手动筛选和交错发送和接收的消息。努力

解决方案2(我现在正在使用) 在对话文档中嵌入消息。我将在下面为用户、消息和对话提供代码。一个对话通过
has\u和\u belient\u to \u many
链接到两个或多个用户(n-n,因为用户也可能有许多对话)。这也可能允许多用户对话

我喜欢此解决方案,因为为了向用户显示其收件箱,我可以使用
某个用户。对话
排序:最后收到的消息
存储并更新在对话文档中,无需过滤。要显示特定的对话,我不需要交错发送和接收的消息,因为消息已经按照正确的顺序嵌入到对话文档中

此解决方案的唯一问题是,当您要添加消息时,找到两个(或更多)用户共享的正确对话文档。这里建议了一个解决方案:,但我不喜欢它,因为查询似乎相对昂贵,多用户对话的扩展看起来会变得棘手。相反,我在对话文档中有一个名为
:lookup\u hash
的字段,它是根据参与对话的每个用户的对象ID计算的SHA1 hash。这样,对于两个或两个以上的用户来说,找到他们相应的对话文档(或者如果它还不存在的话创建它)是很简单的

要将消息添加到对话中,我只需使用
对话。add_message
(类方法,而不是实例方法,因为对话可能还不存在)为其提供发件人、收件人和新消息对象

问题: 我的问题是:考虑到Mongoid(或一般的NoSQL)模式设计最佳实践,我是否做了任何明显错误的事情?我能做些什么来改进我的解决方案吗?我使用哈希查找对话的想法是不是一个坏主意

代码 user.rb

类用户
include Mongoid::Document
字段:用户名,类型:字符串
字段:joined,类型:DateTime,默认值:->{Time.now}
字段:last_活动,类型:DateTime,默认值:->{Time.now}
你和你属于很多人吗:对话
结束
对话.rb

需要“摘要/sha1”
课堂对话
include Mongoid::Document
字段:查找\u哈希,类型:字符串
字段:已创建,类型:DateTime,默认值:->{Time.now}
字段:last_message_time,类型:DateTime,默认值:->{time.now}
#已阅读此对话中所有消息的用户的用户ID数组
字段:最后一条消息,类型:数组,默认值:[]
嵌入许多:消息
拥有且属于多个参与者,:class\u name=>“用户”
验证是否存在:查找\u哈希
索引({lookup\u hash:1},{unique:true,name:“lookup\u hash\u index”})
#用于向用户显示按上次消息时间排序的对话列表
索引({u id:1,上次消息时间:-1},{unique:true,名称:“id\u上次消息时间索引”})
定义自我添加消息(收件人、发件人、消息)
#查找或创建对话:
对话=对话。查找或创建(
:lookup_hash=>get_lookup_hash([recipient.id,sender.id])do|c|
c、 participants.concat[收件人、发件人]
结束
conversation.messages{Time.now}
字段:文本,类型:字符串
嵌入:会话
属于:作者,:类名=>'User'
验证文本的长度,最小值:2,最大值:256
验证是否存在:作者
结束

我推测您正在使用MongoId 3.0。我认为您的第一个解决方案没有任何问题:

messages_in = some_user.messages_received.where(:message_sender => current_user)

messages_out = some_user.messages_sent.where(:message_recipient => current_user).
您可以找到各种示例:

我在几个MongoId项目上有一个内部消息传递系统,并使用了第一个解决方案

如果您添加了其他类“对话”
,则不应嵌入消息,因为对话可以包含无限数量的消息。您应该使用
has\u may messages
属于会话

我认为这两种解决方案都很好,所以您可以根据项目逻辑选择您的需求。如果您的逻辑更简单,您可以选择第一种解决方案。否则,如果您的逻辑更复杂,请选择后一种解决方案

R