2015-02-24 93 views
2

查找记录,我有以下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可以通过ConversationMembership有很多users。理想情况下,每个对话的用户组是唯一的,因此用户只能在他们之间进行一次对话。

有了这个设置,我可以使用下面的Rails的方法来查询其成员ID的一Conversation

Conversation.find(1).user_ids # => [1,2,3] 

这给了我用户的ID在会话的用户123之间。

什么我希望做的是这样做的本质上相反,所以要尽量找到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列中。

+0

我发现有关这个问题的唯一其他相关职位是在这里:http://stackoverflow.com/questions/18889824/rails-find-record-with- all-association-id-matching 但是它并没有真正**解决我的问题,因为它表明与'&'相同的'merge'简写,如果没有一些成员,它不能链接到'n'个成员元编程 – 2015-02-24 02:00:58

回答

1

你需要加入对用户表:

Conversation.joins(:users).where('users.id' => [1, 2, 3]) 
+0

你也可以在where调用中引用关联名称:'Conversation.joins(:users).where(users:{id:[1,2,3]})' – 2015-02-24 02:42:02

+0

不幸的是,我试过这个是前面。这种方法的问题来自多个协会。所以我们可以说'c = Convo.find(1)&& c.user_ids == [1,2]'和'c1 = Convo.find(2)&& c1.user_ids == [1,3]'和'c2 = Convo.find(3)&& c2.user_ids == [1,2,3]'。使用'Conversation.joins(:用户).where('用户。id'=> [1,2,3])'(或@NathanWallace方法)会返回'c','c1' **和**'c2',我只想要'c2' – 2015-02-24 03:11:00

+0

让我知道如果我可以格式化上述更好一点,也感谢你们的回应。如果您有任何提示可帮助我更好地设置我的初始问题,以便上述评论清楚,请告诉我 – 2015-02-24 03:17:23

0

同样的问题差点让我通过多次的窗口中打开我的笔记本电脑,但下面似乎工作。如果有人知道更简单的方法,请说出来。

Conversation.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