Если я правильно понимаю, вы ищете все Product
, которые должны быть возвращены в порядке убывания, исходя из суммы количества Stock
. Если это так, это должно работать для вас.
@products = Product.left_joins(variants: :stocks)
.group(:id)
.select(Product.arel_table[Arel.star],
Stock.arel_table[:quantity].sum.as('total_quantity'))
.order('total_quantity DESC')
Это приведет к
SELECT
products.*,
SUM(stocks.quantity) AS total_quantity
FROM
products
LEFT OUTER JOIN variants ON variants.product_id = products.id
LEFT OUTER JOIN stocks ON stocks.variant_id = variants.id
GROUP BY
products.id
ORDER BY
total_quantity DESC
Что, кажется, то, что вы хотите. Это также добавит виртуальный атрибут total_quantity
ко всем загруженным Product
объектам, которые можно использовать для отображения без повторного попадания в базу данных, например,
@products.each do |p|
p.total_quantity # virtual attribute equal to SUM(stocks.quantity)
end
В качестве окончательного варианта; , если вы фактически используете p.stocks.map(&:quantity).sum
в своем коде, вы можете изменить это значение на p.stocks.sum(:quantity)
из соображений эффективности. Это выполнит запрос SELECT SUM()
, соответствующий ActiveRecord::Relation
, возвращаемому p.stocks
, что в общем случае будет намного лучше, чем загрузка всех объектов Stock
, затем генерация Array
величин и затем итеративное накопление указанного количества в числовое значение.