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
的输出一样,但是以一种独特的方式。
答案 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