1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Fuse traversals of the record set when preloading

Although consuming code will almost certainly retraverse this set, we can avoid
walking it twice here. As an extra upside, we can avoid the double-use of an
identity-sensitive hash; this is convenient, because it is another collection we
actually don't need to build.
This commit is contained in:
Michael Fowler 2019-12-29 18:24:34 +13:00
parent 9aa59f9d4a
commit 989dda36d6

View file

@ -27,40 +27,42 @@ module ActiveRecord
end
def records_by_owner
# owners can be duplicated when a relation has a collection association join
# #compare_by_identity makes such owners different hash keys
@records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
owners_by_key[convert_key(record[association_key_name])].each do |owner|
(result[owner] ||= []) << record
end
end
load_records unless defined?(@records_by_owner)
@records_by_owner
end
def preloaded_records
return @preloaded_records if defined?(@preloaded_records)
load_records unless defined?(@preloaded_records)
raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
seen_records_by_owner = {}.compare_by_identity
@preloaded_records = raw_records.select do |record|
assignments = []
owners_by_key[convert_key(record[association_key_name])].each do |owner|
entries = (seen_records_by_owner[owner] ||= [])
if reflection.collection? || entries.empty?
entries << record
assignments << record
end
end
!assignments.empty?
end
@preloaded_records
end
private
attr_reader :owners, :reflection, :preload_scope, :model, :klass
def load_records
# owners can be duplicated when a relation has a collection association join
# #compare_by_identity makes such owners different hash keys
@records_by_owner = {}.compare_by_identity
raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
@preloaded_records = raw_records.select do |record|
assignments = []
owners_by_key[convert_key(record[association_key_name])].each do |owner|
entries = (@records_by_owner[owner] ||= [])
if reflection.collection? || entries.empty?
entries << record
assignments << record
end
end
!assignments.empty?
end
end
# The name of the key on the associated records
def association_key_name
reflection.join_primary_key(klass)