ray
ray

Reputation: 5552

How to avoid eagerloading problem in Rails 4

Here is my code below.

Client has many events with foreign key client_id in 'events' table

has_many :events

In console

clients = Client.where(id: [110,112,113,115]).includes(:events)
=>  Client Load (0.4ms)  SELECT `clients`.* FROM `clients` WHERE `clients`.`id` IN (110, 112, 113, 115)
    Event Load (0.5ms)  SELECT `events`.* FROM `events` WHERE `events`.`client_id` IN (110, 112, 113, 115)

But when run following,

clients.each { |cr| cr.events.count }
   (0.4ms)  SELECT COUNT(*) FROM `events` WHERE `events`.`client_id` = 110
   (0.3ms)  SELECT COUNT(*) FROM `events` WHERE `events`.`client_id` = 112
   (0.3ms)  SELECT COUNT(*) FROM `events` WHERE `events`.`client_id` = 113
   (0.3ms)  SELECT COUNT(*) FROM `events` WHERE `events`.`client_id` = 115

I am getting N+1 queries, I am missing something.

Update

From answer provided I got clue, I wanted ids of events so I was trying as,

clients.each { |cr| cr.events.ids }
clients.each { |cr| cr.events.pluck(:id) }

So N+1 queries got fired, then I worked out with following and solved,

clients.each { |cr| cr.events.map(&:id) }

I treated Relation as Array and used map instead of ids or pluck and solved, no N+1 query got fired.

Upvotes: 0

Views: 51

Answers (2)

Pramod Shinde
Pramod Shinde

Reputation: 1902

If your goal is to fetch event ids use join with pluck

event_ids = Client.where(id: [110,112,113,115]).joins(:events).pluck('events.id')

Upvotes: 2

Marcin Kołodziej
Marcin Kołodziej

Reputation: 5313

.count in case of ActiveRecord Relation will simply run the query. If you change it to .size, the length of array of eager-loaded items will be checked, which is what you're after.

Upvotes: 1

Related Questions