使用关联外键通过关联查找记录

时间:2015-02-24 01:56:22

标签: ruby-on-rails postgresql activerecord arel

我有以下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可以有多个usersConversationMembership。理想情况下,每次对话都是&#39;用户组是唯一的,因此用户1,2,3只能在它们之间进行一次对话。

通过此设置,我可以使用以下Rails方法查询Conversation其成员ID:

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

这为用户123之间的对话提供了用户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的内容存储在Array模型的Conversation列中

2 个答案:

答案 0 :(得分:2)

您需要加入users表:

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

答案 1 :(得分: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