在根据包含的元素过滤父模型的同时从`includes`获取所有结果?

时间:2013-06-10 22:01:35

标签: sql ruby-on-rails postgresql ruby-on-rails-3.2

我正在处理我们应用中的一个小错误,我将在此处尝试简化。

我有这个公司模型(实际模型有更多关联,但这里显示问题):

class Company < ActiveRecord::Base
  has_many :contacts

  def self.search(query, page=1)
    companies = includes(:contacts).order("companies.name ASC")

    if query.present?
      companies = companies.where(%(
        companies.name ILIKE :q,
        OR contacts.name ILIKE :q
      ), q: "%#{query}%")
    end

    companies.paginate(page: page)
  end
end

现在这大部分工作得很好。唯一的问题是公司有2个或更多联系人,并且用户根据其中一个联系人名称进行搜索。公司被正确找到,我们的自动完成UI显示公司。但是,当用户从自动完成中选择公司时,他们应该能够从下拉列表中选择联系人,但是下拉列表最终只包含他们已过滤到的联系人。公司应根据其搜索条件进行过滤,但该下拉列表仍应包含公司的所有联系人供用户选择。

我知道为什么它正在执行此操作,这是因为rails在查询运行时自动加载所有关联,因此它可以获取所有关联属性和< / em>同时包含所有条件。

它正在运行select {every_attribute_from_every_included_table} left outer join {all_included_tables}类型的查询,但我想要它做的是:

select companies.* from companies
left outer join contacts <and other included tables> on <join criteria>
where <filter criteria>

过滤公司后跟a:

select contacts.* from contacts
where company_id IN (<company_ids from previous query>)

如果include表中没有where表,那么它通常会包含条目。

Rails在这里做了太多的魔法,我想让它回到它的正常魔法而不是它的特殊魔法!我怎样才能做到这一点?或者什么是更好的方式来完成我想要完成的事情(仍然根据联系人名称进行过滤,但是当我打电话给company.contacts时不要阻止包含其他联系人。)

2 个答案:

答案 0 :(得分:1)

好的,让我们按你的意愿进行查询

select companies.* from companies
left outer join contacts <and other included tables> on <join criteria>
where <filter criteria>

让我们手动覆盖.joins(...)

Company.includes(:contacts).
        joins("left outer join contacts c on c.company_id = companies.id").
        where(...)

这会使:contacts关系保持不变,而是加入一个单独的别名&#39;相同的contacts表。

答案 1 :(得分:0)

我发现你需要两个查询:

  • 一个选择公司(或公司)
  • 一个选择(选定)公司的联系人

使用Rails魔术,您可以使用joins

选择公司
@companies = Company.joins(:contacts).where(...).uniq

给予SELECT DISTINCT "companies".* FROM "companies" INNER JOIN "contacts" ON ... WHERE (...)

然后获取特定公司的所有联系人:

company= @companies.first
@contacts = company.contacts