aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/preloader/through_association.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations/preloader/through_association.rb')
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb70
1 files changed, 41 insertions, 29 deletions
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index 32653956b2..54b019e366 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -4,45 +4,57 @@ module ActiveRecord
module Associations
class Preloader
class ThroughAssociation < Association # :nodoc:
- def run(preloader)
- already_loaded = owners.first.association(through_reflection.name).loaded?
- through_scope = through_scope()
- through_preloaders = preloader.preload(owners, through_reflection.name, through_scope)
- middle_records = through_preloaders.flat_map(&:preloaded_records)
- preloaders = preloader.preload(middle_records, source_reflection.name, scope)
- @preloaded_records = preloaders.flat_map(&:preloaded_records)
-
- owners.each do |owner|
- through_records = Array(owner.association(through_reflection.name).target)
-
- if already_loaded
+ PRELOADER = ActiveRecord::Associations::Preloader.new
+
+ def initialize(*)
+ super
+ @already_loaded = owners.first.association(through_reflection.name).loaded?
+ end
+
+ def preloaded_records
+ @preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
+ end
+
+ def records_by_owner
+ return @records_by_owner if defined?(@records_by_owner)
+ source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
+ through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
+
+ @records_by_owner = owners.each_with_object({}) do |owner, result|
+ through_records = through_records_by_owner[owner] || []
+
+ if @already_loaded
if source_type = reflection.options[:source_type]
through_records = through_records.select do |record|
record[reflection.foreign_type] == source_type
end
end
- else
- owner.association(through_reflection.name).reset if through_scope
end
- result = through_records.flat_map do |record|
- record.association(source_reflection.name).target
+ records = through_records.flat_map do |record|
+ source_records_by_owner[record]
end
- result.compact!
- result.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
- result.uniq! if scope.distinct_value
- associate_records_to_owner(owner, result)
- end
-
- unless scope.empty_scope?
- middle_records.each do |owner|
- owner.association(source_reflection.name).reset if owner
- end
+ records.compact!
+ records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
+ records.uniq! if scope.distinct_value
+ result[owner] = records
end
end
private
+ def source_preloaders
+ @source_preloaders ||= PRELOADER.preload(middle_records, source_reflection.name, scope)
+ end
+
+ def middle_records
+ through_preloaders.flat_map(&:preloaded_records)
+ end
+
+ def through_preloaders
+ @through_preloaders ||= PRELOADER.preload(owners, through_reflection.name, through_scope)
+ end
+
def through_reflection
reflection.through_reflection
end
@@ -52,8 +64,8 @@ module ActiveRecord
end
def preload_index
- @preload_index ||= @preloaded_records.each_with_object({}).with_index do |(id, result), index|
- result[id] = index
+ @preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
+ result[record] = index
end
end
@@ -92,7 +104,7 @@ module ActiveRecord
end
end
- scope unless scope.empty_scope?
+ scope
end
end
end