I am using the graphql and batch-loader gems and running into this N+1 query:
I have a calendar appointment that belongs to an account, and when we display the appointment time we base it on the account's time zone.
def appointment_time
read_attribute(:appointment_time).in_time_zone(self.account.timezone)
end
calendarAppts {
appointmentTime
}
results in an N+1 query
POST /graphql
USE eager loading detected
CalendarAppt => [:account]
Add to your query: .includes([:account])
Call stack
/....rb:866:in `appointment_time'
/.../app/controllers/graphql_controller.rb:17:in `execute'
And I have many other similar examples of instance methods creating N+1 queries and unsure the best way to tackle this.
So far I have just seen ways to conquer eager loading associations directly. Do I need to treat each of such method as an association?
For instance this is how I eager load an account itself, do I need to do something similar when I call this method as well?
field :account,
AccountType,
null: true,
resolve: lambda { |obj, _args, _ctx|
BatchLoader::GraphQL.for(obj.account_id).batch do |account_ids, loader|
Account.where(id: account_ids).each { |account| loader.call(account.id, account) }
end
}
Also then if we did something like this would it call accounts twice?
calendarAppts {
appointmentTime
account {
id
name
}
}
Assuming you're using graphql-ruby
to handle your queries you should use lookahead
to see what sub-fields are required and prepare your query accordingly.
So in your case, in the method implementing callendarAppts, you should do something like:
field :calendar_appts, ..., extras: [:lookahead]
def callendar_appts(lookadead:)
scope = Model.where(...)
if(lookahead.selects?(:appointment_time))
scope = scope.includes([:accounts])
end
scope.all
end
(I'm spit-balling here it as the actual implementation has not been provided)