通过Rails关系选择模型?

时间:2013-09-30 21:59:51

标签: sql ruby-on-rails ruby ruby-on-rails-4

我有这些(简化)模型:

Address
public (Boolean)
has_one :group_member

Group
has_many :Group_Members
belongs_to :User

Group_Member
belongs_to :group
belongs_to :address

User
has_many :groups

我想选择public为真的所有地址以及用户可以通过群组访问的所有地址。

我认为它有以下几点:

Address.where(public: TRUE).joins(:group_member)

我在附近吗?

我使用Rails 4和PostgreSQL作为我的数据库,如果它可以帮助任何人。

2 个答案:

答案 0 :(得分:1)

我认为这会奏效:

Address.joins(group_member: :group).where(is_public: true, groups: {user_id: 12345})

让我们打破这一点。

首先,我们正在调用Address模型,这是我们想要返回的b / c。

.joins(:group_member)会通过group_members Address关系将地址加入has_one :group_member

但是,我们实际上希望更进一步,并加入与group相关联的group_member,因此我们使用嵌套连接,这就是为什么它看起来像joins(group_member: :group)我们加入地址 - > group_member,then group_member - >基。

接下来是where子句。我们想要有两个条件:

  1. 仅公共地址,在地址上显示为一列,因此我们添加: .where(is_public: true)
  2. 我们只想要特定用户与组连接的位置(以及其group_members和地址)。为此,我们需要添加一个嵌套的where子句,如groups: {user_id: 12345}
  3. 将这些结合起来的结果是:

    where(is_public: true, groups: {user_id: 12345})

    总而言之,上面的代码行应该得到你想要的东西。

答案 1 :(得分:1)

尝试以下操作 - 它不是单个查询,但我认为它比具有大连接表的单个查询更好:

public_addresses = Address.where(is_public: true)
# =>  get all public addresses

user_addresses = current_user.groups.includes(:group_members => :address).
                # includes is to eager load records to avoid N+1 queries
                flat_map{|g| g.group_members.map(&:address)}
                # inner block returns array of addresses for each group
                # flat_map converts the array of arrays to single level 
# => get all addresses associated with the user


all_addresses = (public_addresses + user_addresses).uniq
# =>  remove duplicates

要加快查询,请为较慢的查询添加索引。例如

   add_index :groups, :user_id
   # this speeds up finding groups for a given user

   add_index :group_members, :group_id
   # this speeds up finding group_members for a given group

   add_index :addresses, :group_member_id
   # this speeds up finding addresses for a given group_member

其他选项是使用user_addresses

获取join
   user_addresses = Address.joins(group_member: group).where(groups: {user_id: current_user.id} )