Как получить уникальный набор родительских моделей после опроса на ребенка - PullRequest
0 голосов
/ 13 мая 2018

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"}]
}      

Я выполняю запросы для заказа на основе дочерних атрибутов. Допустим, я хочу получать электронные письма от всех заказов, где у них есть Предмет, который является яблоком. Если я настрою запрос так:

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 голосов
/ 14 мая 2018

Я думаю, вам просто нужно удалить 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
0 голосов
/ 14 мая 2018

Если вы хотите сохранить все атрибуты заказов, используйте group

deduped_orders = orders.group(:id).distinct

deduped_orders.each do |o|
  puts o.email
end
#=> output: "alpha@exmaple.com", "beta@example.com"
0 голосов
/ 14 мая 2018

Я нахожу использование подзапросов вместо объединений немного чище для такого рода вещей:

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

, который заканчивается этим (более или менее) SQL:

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

и in (...) очистит дубликаты для вас. Использование подзапроса также ясно выражает то, что вы хотите сделать - вы хотите, чтобы заказы имели элемент с именем 'apple' - и запрос говорит именно это.

0 голосов
/ 14 мая 2018

используйте .uniq вместо .distinct

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