I have a parent with many children. I preload a sub-set of children with an Active Record query. When I call parent.children
I only want the sub-set to be returned rather triggering an N+1 query and returning all of the children.
parents = Parent.includes(:child).where(child: { attr: value })
parents.flat_map do |parent|
parent.child # causes N+1
end
Context / Use Case
I'm working on a capacity report for a booking system. If there's a pre-calculated Occupancy
for a given day I want to show the Occupancy
(child) stats, if not I want to show the pre-set for the Resource
(parent).
class Resource < ApplicationRecord
has_many :occupancies
end
class Occupancy < ApplicationRecord
belongs_to :resource
end
report_start_date = Date.today
report_end_date = report_start_date + 2.weeks
@resources = Resource.includes(:occupancies).where(occupancies: { date: report_start_date...report_end_date }).or(Resource.all).uniq
Within the @resources
variable assignment above I want to pre-load occupancies for the reporting period (not outside the period) and I still want Resources
that don't have occupancies
within the reporting period.
So, although @Schwern's answer makes sense, it doesn't work for this use case - whereby I still need all the Resources
.
If you want all children which match a certain criteria, and their parents, in a single query, query the children and preload their parent.
UPDATE
If you want to only use associations which have already been eager loaded, you can check with
loaded?
.