Mohamed El Mahallawy
Mohamed El Mahallawy

Reputation: 13842

Eager loading with has_many through

I have a User model.

A user has many integrations.

An integration is join to a profile via integration_profiles which contains a column data.

I want to eager load all of a user's profiles.

class Integration < ActiveRecord::Base
 has_many :integration_profiles
 has_many :profiles, through: :integration_profiles
end

class IntegrationProfile < ActiveRecord::Base
 belongs_to :integration
 belongs_to :profile
end

class Profile < ActiveRecord::Base
 has_many :integration_profiles
 has_many :integrations, through: :integration_profiles
end

I tried this:

all = User.first.integrations.includes(:profiles)

But I when I did all.count

=> 2

But when I do

all = User.first.integrations.joins(:profiles)
all.count
=> the correct total

Should I be using includes or joins? I have always used includes so not sure why this isn't working here

Upvotes: 6

Views: 6759

Answers (2)

Kirti Thorat
Kirti Thorat

Reputation: 53018

When you do

all = User.first.integrations.joins(:profiles)
all.count

the integrations records would be counted for the first User and with an inner join query on profiles.

And when you do

all = User.first.integrations.includes(:profiles)
all.count

again you get integrations count BUT without join query with profiles as profiles are eager loaded with separate queries because of includes

It seems that you simply want the profiles count associated to a given user. The best way to achieve this would be, to create an association between User and Profile model

User ==> has_many :profiles, through: :integration

Once you do that, you can directly access User.first.profiles.count to get the count of all associated profiles of a particular user.

The other option would be (if you don't want to go with above option) to loop through all integrations and sum up all profiles.count for every integration.

Choose the option that best suits your needs.

Upvotes: 3

Addicted
Addicted

Reputation: 749

Query 1

all = User.first.integrations.includes(:profiles)

all.count is returning count of integrations and not of profile. Profile is loaded eagerly.

If you want to know the count of profile then it needs to be done in this way.

ar = [ ]

 all.each do |a|
   ar << a.profiles.count
 end

ar.reduce(:+) will give you the same count when you run Query 2.

Query 2

all = User.first.integrations.joins(:profiles)

all.count

In case of Query 2, its returning you integrations from integrartion_profiles table.

Select users from users limit 1;

Select integrations from integrations INNER JOIN integration_profiles on integration_profiles.integration_id = integrations.id where integrations.user_id = 'id of user'

To know more about it call .to_sql on Query 1 and Query 2.

If you want to do eager loading then use of includes is the preferred option.

Upvotes: 0

Related Questions