@Fly3110
web developer

Как сделать eager loading при группировке?

Всем привет.

Есть модели Purchase, Shop и Category
Цель - получить все магазины, в которых были совершены покупки с общей суммой покупок.

Purchase.group(:shop).sum(:amount)

возвращает Hash с верными значениями (объект магазина и сумма)
Но, при этом, когда я хочу получить категорию магазина, то на каждое обращение к shop.category идет запрос к БД.

Я пытался сделать вот так:
Purchace.group(:shop).includes(shop: [:category]).sum(:amount)


но ничего не изменилось.
На этапе выборки к базе выполняется два запроса: 1 - на получение сгруппированных строк из таблицы покупок, 2 - на получение данных всех магазинов, которые присутствовали в выборке. Я бы хотел, чтобы был еще третий запрос - на получение всех категорий магазинов.

Модели описаны вот так:

class Shop < ActiveRecord::Base
  belongs_to :category
  belongs_to :city
  has_many :purchases
end

class Category < ActiveRecord::Base
  has_many :shops
end

class Purchase < ActiveRecord::Base
  belongs_to :user
  belongs_to :shop
end


возможно ли сделать то, что я хочу?

Заранее спасибо
  • Вопрос задан
  • 148 просмотров
Пригласить эксперта
Ответы на вопрос 2
@vsuhachev
Попробуйте preload, похоже includes неверно понимает то чего вы хотите

Purchace.group(:shop).preload(shop: :category).sum(:amount)


Подробнее тут
Ответ написан
@Fly3110 Автор вопроса
web developer
Начинаю думать, что это невозможно... Так как в calculate.rb, в методе execute_grouped_calculation есть такой блок:
if association
        key_ids     = calculated_data.collect { |row| row[group_aliases.first] }
        key_records = association.klass.base_class.find(key_ids)
        key_records = Hash[key_records.map { |r| [r.id, r] }]
      end


то есть, для калькуляции, после самого запроса, rails получает только те записи, которые соответствуют первому полю из тех, по которым была группировка
includes и preload добавляют join к первому запросу, но, так как этих полей нет в select (да и при добавлении их туда ничего не происходит), то при обращении к категории заново идет запрос к базе.

Буду еще пытаться, но видимо придется после запроса самому собирать все shop.category_id и самому делать по ним запрос

В итоге сделал вот так:
purchases = user.purchases.select('shop_id, sum(count)').group(:shop_id)
shop_ids = purchases.collect do |purchase| purchase.shop_id end
shops = Shop.includes(:category).find(shop_ids)


Но все равно полагаю, что должен быть способ сделать это более правильно.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы