我有以下4种型号的
class User < ActiveRecord::Base
has_many :conversation_memberships
has_many :conversations, :through => :conversation_memberships
has_many :messages, :through => :conversations
end
class Conversation < ActiveRecord::Base
has_many :messages
has_many :members, :class_name => 'ConversationMembership', :foreign_key => :conversation_id
has_many :users, :through => :members
end
class ConversationMembership < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
class Message < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
end
因此,Conversation
可以具有许多users
到ConversationMembership
。理想情况下,每个会话的用户集都是唯一的,因此用户1,2,3
之间只能有一个会话。
有了这个设置,我可以使用以下Rails方法查询Conversation
的成员id:
Conversation.find(1).user_ids # => [1,2,3]
这为我提供了用户1
、2
和3
之间的对话中的用户id。
我想做的是本质上与相反的,所以试着在这3个用户之间找到一个对话,而只有这些用户。在伪代码形式中,类似于Conversation.find_by(:user_ids => [1,2,3])
。
此语法具有所需的效果,但使用了3个(或更多)查询:
User.find(1).conversations & User.find(2).conversations & User.find(3).conversations
我希望通过一个查询(可能使用子查询)实现这一点,但我一辈子都不知道如何实现这一目标。我上一次写这样复杂的原始SQL已经快6年了,所以我的大脑里充满了蜘蛛网。
总结:我希望能够查询Conversation
模型,并返回2个或多个用户之间的现有对话。
如果所有其他方法都失败了,我能想出的最接近的解决方案就是将conversation.user_ids
的内容存储在Conversation
模型的Array
列中。
您需要根据users表加入:
Conversation.joins(:users).where('users.id' => [1, 2, 3])
同样的问题几乎让我在许多场合通过窗口启动我的笔记本电脑,但以下似乎有效。如果有人知道一个更简单的方法,请大声说出来。
对话.rb
def self.findConvo(idArr) # pass in array of user ids
length = idArr.length
idString = "('" + idArr.join("','") + "')" # you have to remove the square brackets and use round ones to keep the sql happy
@conversation = Conversation.find_by_sql("
SELECT *
FROM (
SELECT cm3.conversation_id
FROM (
SELECT cm1.conversation_id
FROM conversationmembership cm1
WHERE cm1.user_id IN #{idString} /* find all conversations involving at least one of our users */
GROUP BY cm1.conversation_id
HAVING COUNT(cm1.user_id) = #{length} /* pick out the conversations involving all our users and possibly other users */
) AS cm2
INNER JOIN conversationmembership cm3
ON cm2.conversation_id = cm3.conversation_id /* get ALL users for our conversations */
GROUP BY cm2.conversation_id, cm3.conversation_id
HAVING COUNT(cm3.user_id) = #{length} /* pick out the only conversation involving ONLY our users */
) AS cm4
INNER JOIN conversations con1
ON cm4.conversation_id = con1.id /* bolt on conversations */
")
return @conversation.first # because @conversation is a collection
end