How to avoid N + 1 request
I have the following models:
- User
- Possibility
- PricingRule
is determined with the following relations:
- The user has many pricing rules.
- has one pricing rule
The idea is to get all abilities that meet some criteria and select a pricing rule for each one. However, a custom pricing rule for a specific ability can be defined for each user.
Currently I am getting all the mapping possibilities and iterating over them:
- try to find the current ability that matches the user's pricing rule.
- or the default pricing rule
I am using Rails and ActiveRecord and this is what I have so far:
user = User.first
Ability.all.map do |a|
user.pricing_rules.matching_ability(a).first || a.pricing_rule
end
According to the setting of the user-on-demand pricing rule, the business must run. The general workflow is to get a pricing rule from an opportunity.
Any ideas or help to get me on the right track would be much appreciated.
EDIT:
Where the implementation runs matching_ability
:
def self.matching_ability(ability)
where(name: ability.name)
end
source to share
You can "load" to avoid N + 1 queries, for example:
user = User.includes(pricing_rules: :abilities).first
Ability.includes(:pricing_rule).map do |a|
user.pricing_rules.matching_ability(a).first || a.pricing_rule
end
You should see in the generated SQL what this will add LEFT OUTER JOIN
to your queries, so ActiveRecord only loads related records in two queries. Specifically, the user will be loaded with pricing_rules
and abilities
on each pricing_rule
, and the abilities will be loaded with pricing_rules
.
However, a matching_ability
using implementation where
can generate additional queries, returning you to the N + 1 problem. To take advantage of the "eager loading" in the first query, you might need to refactor to:
self.matching_ability(ability)
select{|a| a.name == ability.name}
end
source to share