Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid n+1 queries when using polymorphic associations?

I have 4 models, let's say:

class Photo < ActiveRecord::Base
  belongs_to :photoable, polymorphic: true
end

class User < ActiveRecord::Base
  has_one :photo, as: :photoable
end    

class Company < ActiveRecord::Base
  has_one :photo, as: :photoable
  has_many :products
end

class Products < ActiveRecord::Base
  belongs_to :company
end

So, the query Photo.all.includes(:photoable) works.

But if I use Photo.all.includes(photoable: :products) only works if all loaded photos belongs to Company. If the relation contains photos of users and companies, this error is raised:

ActiveRecord::ConfigurationError (Association named 'products' was not found; perhaps you misspelled it?):

This occurs because user hasn't relationship with products.

Is there any way to eager load users and companies with products for a relation of photos?

EDIT:

This question isn't duplicated of Eager load polymorphic. As I commented below, in this question I want to do eager load for polymorphic associations which has different associations(one has products and the other don't). In that question, the OP uses wrong names for table names.

like image 504
Rodrigo Avatar asked Jan 27 '15 12:01

Rodrigo


People also ask

How to avoid n 1 queries Rails?

You can avoid most n+1 queries in rails by simply eager loading associations. Eager loading allows you to load all of your associations (parent and children) once instead of n+1 times (which often happens with lazy loading, rails' default). As seen above, . includes allows nested association eager loading!

What is n 1 query in Rails?

The n+1 query problem is one of the most common scalability bottlenecks. It involves fetching a list of resources from a database that includes other associated resources within them. This means that we might have to query for the associated resources separately.

What is polymorphic association in Ruby on Rails?

In Ruby on Rails, a polymorphic association is an Active Record association that can connect a model to multiple other models. For example, we can use a single association to connect the Review model with the Event and Restaurant models, allowing us to connect a review with either an event or a restaurant.

What is eager loading rails?

Eager loading lets you preload the associated data (authors) for all the posts from the database, improves the overall performance by reducing the number of queries, and provides you with the data that you want to display in your views, but the only catch here is which one to use. Gotcha!


1 Answers

You may add specific associations to Photo model:

class Photo < ActiveRecord::Base
  belongs_to :photoable, polymorphic: true

  belongs_to :user, -> { where(photoable_type: 'User' ) }, foreign_key: :photoable_id
  belongs_to :company, -> { where(photoable_type: 'Company' ) }, foreign_key: :photoable_id
end

After that you may preload nested specific associations:

photos = Photo.all.includes(:photoable, company: :products)

With access to records like this:

photos.each do |photo|
  puts photo.photoable.inspect
  puts photo.company.products.inspect if photo.photoable_type == "Company"
end

This approach loads company association twice, but does not do n+1 queries.

like image 191
Ilya Lavrov Avatar answered Oct 05 '22 17:10

Ilya Lavrov