N+1 проблема для пользовательского метода в рельсах
В моем приложении рельсы у меня есть User
модель с методом под названием price_tier
:
def price_tier
Spree::PriceTier.by_code[read_attribute(:price_tier)]
end
Тем не менее, когда у меня есть коллекция @users
и вызовите price_tier один за другим, он загрузит PriceTier
запрос для каждого экземпляра, который вызывает проблему N+1.
includes
здесь не работает, так как это не ассоциация.
@users.map { |user| user.price_tier }
Есть ли способ изменить код, который исправляет проблему N+1?
1 ответ
Вы действительно должны установить отношения между моделями напрямую, например, user has_one :price_tier
, Это сделает код здесь бризом.
Если вы этого не сделаете, вы прыгаете через обручи для чего-то, что должно быть простым. Возможно, вы могли бы использовать что-то вроде следующего, хотя это все равно будет гораздо менее эффективным, чем прямые отношения:
tiers = Spree::PriceTier.where(code: @users.pluck(:price_tier))
.each_with_object({}) { |tier, hash| hash[tier[code]] = tier }
@users.map { |user| tiers[user.price_tier] }
NB. Вышесказанное предполагает удаление метода экземпляра. :price_tier
и используя атрибут.
Я не уверен, что действительно стоит включить вышесказанное - вы собираете пользовательский Price_tier, нажимаете на БД (только один раз, а не для каждого пользователя) для уровней, итерируете их все, чтобы создать хэш для быстрого доступа, затем итерируете через пользователей, чтобы сопоставить их. Это большая работа для чего-то, что легко решается путем установления отношений.
Надеюсь, что это поможет, и действительно надеюсь, что тон выше в приведенном выше смысле - в основном означает, что вы могли бы сделать что-то подобное выше, хотя реальный совет был бы: пожалуйста, не устанавливайте отношения и работайте с традиционным подходом здесь:)