如何在查询孩子

时间:2018-05-13 20:57:13

标签: ruby-on-rails activerecord rails-activerecord

Order has_many Items就是这种关系。

因此,假设我在数据库中包含以下2个订单:

Order1 {email: alpha@example.com, items_attributes:
    [{name: "apple"},
     {name: "peach"}]
}
Order2 {email: beta@example.com, items_attributes:
    [{name: "apple"},
     {name: "apple"}]
}      

我正在根据子属性运行Order的查询。所以,假设我想要所有订单的电子邮件,他们有一个项目是苹果。如果我将查询设置为:

orders = Order.joins(:items).where(items: {name:"apple"})

然后结果,因为它在Item水平拉动,将是这样的:

orders.count = 3
orders.pluck(:email) = ["alpha@exmaple.com", "beta@example.com", "beta@example.com"]

但我希望的结果实际上是知道有哪些独特的订单(我不在乎beta@example.com有2个苹果,只有它们至少有1个),所以类似于:

orders.count = 2
orders.pluck(:email) = ["alpha@exmaple.com", "beta@example.com"]

我该怎么做?

如果我orders.select(:id).distinct,这将解决问题,orders.count == 2,但这会扭曲结果(不再创建AR对象),因此我无法迭代它。所以下面就好了

deduped_orders = orders.select(:id).distinct
deduped_orders.count = 2
deduped_orders.pluck(:email) = ["alpha@exmaple.com", "beta@example.com"]

但是下面的内容不起作用:

deduped_orders.each do |o|
  puts o.email # ActiveModel::MissingAttributeError: missing attribute: email
end

就像我基本上想要orders的输出一样,但是以一种独特的方式。

4 个答案:

答案 0 :(得分:1)

I find using subqueries instead of joins a bit cleaner for this sort of thing:

Order.where(id: Item.select(:order_id).where(name: 'apple'))

that ends up with this (more or less) SQL:

select *
from orders
where id in (
  select order_id
  from items
  where name = 'apple'
)

and the in (...) will clear up duplicates for you. Using a subquery also clearly expresses what you want to do–you want the orders that have an item named 'apple'–and the query says exactly that.

答案 1 :(得分:0)

使用.uniq代替.distinct

deduped_orders = orders.select(:id).uniq
deduped_orders.count = 2
deduped_orders.pluck(:email) = ["alpha@exmaple.com", "beta@example.com"]

答案 2 :(得分:0)

如果您想保留订单的所有属性,请使用group

deduped_orders = orders.group(:id).distinct

deduped_orders.each do |o|
  puts o.email
end
#=> output: "alpha@exmaple.com", "beta@example.com"

答案 3 :(得分:0)

我认为您只需删除select(:id)

orders = Order.joins(:items).where(items: {name:"apple"}).distinct

orders.pluck(:email)
# => ["alpha@exmaple.com", "beta@example.com"]

orders = deduped_orders
deduped_orders.each do |o| 
  puts o.email # loop twice
end