My development environment is on Rails 4.1 and postgresql
I've 3 models with has_many through relationship:
class Item < ActiveRecord::Base
has_many :item_parts
has_many :parts, through: :item_parts
end
class Part < ActiveRecord::Base
has_many :item_parts
has_many :items, through: :item_parts
end
class ItemPart < ActiveRecord::Base
belongs_to :item
belongs_to :part
end
The item_parts join table has a unit_count attribute to keep track of how many parts the item contains.
When in the view I iterate through all parts of a specific item I also need to get the unit_count value from the join table.
This give me the n+1 query issue.
I tried to eager load the join table:
item.parts.includes(:item_parts)
ItemPart Load (0.5ms) SELECT "item_parts".* FROM "item_parts" WHERE "item_parts"."part_id" IN (24, 12, 3, 26)
but when after I do:
part.item_parts.where(item_id: item.id).first.unit_count
I got following sql query for every part; the eager loading didn't work
ItemPart Load (0.5ms) SELECT "item_parts".* FROM "item_parts" WHERE "item_parts"."part_id" = $1 AND "item_parts"."item_id" = $2 [["part_id", 24], ["item_id", 18]]
Any suggestions?
Following this link I found the solution for my case: Rails Eager Loading and where clause
I didn't known the Active Record Query Method: Select (http://apidock.com/rails/v4.1.8/ActiveRecord/QueryMethods/select)
It seems that I wasn't able to eager load the join table because after includes the item_parts I use the where clause during the iteration of the item parts.
It seems that the where clause make a new query to the db every time it is used during the iteration.
Instead select method doesn't hit the db every time but works on the Active Record collection loaded into memory.
So I changed this:
item.parts.includes(:item_parts)
part.item_parts.where(item_id: item.id).first.unit_count
with this:
item.parts.includes(:item_parts)
part.item_parts.select{|item_part| item_part.item_id == item.id}.first.unit_count
and then I have a single query to load item_parts:
ItemPart Load (0.5ms) SELECT "item_parts".* FROM "item_parts" WHERE "item_parts"."part_id" IN (24, 12, 3, 26)
I expect this is because you are using the "count" method which, according to:
http://dev.mensfeld.pl/2014/09/activerecord-count-vs-length-vs-size-and-what-will-happen-if-you-use-it-the-way-you-shouldnt/
"is not stored internally during object life cycle, which means, that each time we invoke this method, SQL query is performed again"
Try using .length or .size methods instead.
Note: (to quote the article)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With