提交 e5299c1e 编写于 作者: A Aaron Patterson

hm:t preloading will respect order set on the RHS association

上级 6f9ea581
...@@ -127,7 +127,7 @@ def preloaders_for_hash(association, records) ...@@ -127,7 +127,7 @@ def preloaders_for_hash(association, records)
loaders = preloaders_for_one parent, records loaders = preloaders_for_one parent, records
recs = loaders.flat_map(&:target_records).uniq recs = loaders.flat_map(&:preloaded_records).uniq
loaders.concat Array.wrap(child).flat_map { |assoc| loaders.concat Array.wrap(child).flat_map { |assoc|
preloaders_on assoc, recs preloaders_on assoc, recs
} }
......
...@@ -17,6 +17,7 @@ def initialize(klass, owners, reflection, preload_scope) ...@@ -17,6 +17,7 @@ def initialize(klass, owners, reflection, preload_scope)
@owners_by_key = nil @owners_by_key = nil
@type_caster = IDENTITY_CASTER @type_caster = IDENTITY_CASTER
@associated_records_by_owner = nil @associated_records_by_owner = nil
@loaded = false
end end
def run def run
...@@ -71,20 +72,36 @@ def options ...@@ -71,20 +72,36 @@ def options
reflection.options reflection.options
end end
def target_records def preloaded_records1
associated_records_by_owner.values.flatten associated_records_by_owner.values.flatten
end end
def preloaded_records
if owners.first.association(reflection.name).loaded?
[]
else
associated_records_by_owner.values.flatten
end
end
def loaded?
@loaded
end
private private
def associated_records_by_owner def associated_records_by_owner
@loaded = true
return @associated_records_by_owner if @associated_records_by_owner return @associated_records_by_owner if @associated_records_by_owner
owners_map = owners_by_key owners_map = owners_by_key
owner_keys = owners_map.keys.compact owner_keys = owners_map.keys.compact
# Each record may have multiple owners, and vice-versa # Each record may have multiple owners, and vice-versa
records_by_owner = Hash[owners.map { |owner| [owner, []] }] records_by_owner = Hash.new do |h,owner|
h[owner] = []
end
if klass && owner_keys.any? if klass && owner_keys.any?
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000) # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
...@@ -104,6 +121,9 @@ def associated_records_by_owner ...@@ -104,6 +121,9 @@ def associated_records_by_owner
end end
end end
end end
owners.each_with_object(records_by_owner) do |owner,h|
h[owner] ||= []
end
@associated_records_by_owner = records_by_owner @associated_records_by_owner = records_by_owner
end end
......
...@@ -35,10 +35,10 @@ def association_key ...@@ -35,10 +35,10 @@ def association_key
# actual records, ensuring that we don't create more than one instances of the same # actual records, ensuring that we don't create more than one instances of the same
# record # record
def associated_records_by_owner def associated_records_by_owner
return @records_by_owner if @records_by_owner return @associated_records_by_owner if @associated_records_by_owner
records = {} records = {}
@records_by_owner = super.each_value do |rows| @associated_records_by_owner = super.each_value do |rows|
rows.map! { |row| records[row[klass.primary_key]] ||= klass.instantiate(row) } rows.map! { |row| records[row[klass.primary_key]] ||= klass.instantiate(row) }
end end
end end
......
...@@ -5,13 +5,15 @@ class HasManyThrough < CollectionAssociation #:nodoc: ...@@ -5,13 +5,15 @@ class HasManyThrough < CollectionAssociation #:nodoc:
include ThroughAssociation include ThroughAssociation
def associated_records_by_owner def associated_records_by_owner
return @associated_records_by_owner if @associated_records_by_owner
records_by_owner = super records_by_owner = super
if reflection_scope.distinct_value if reflection_scope.distinct_value
records_by_owner.each_value { |records| records.uniq! } records_by_owner.each_value { |records| records.uniq! }
end end
records_by_owner @associated_records_by_owner = records_by_owner
end end
end end
end end
......
...@@ -12,6 +12,10 @@ def source_reflection ...@@ -12,6 +12,10 @@ def source_reflection
end end
def associated_records_by_owner def associated_records_by_owner
@loaded = true
return @associated_records_by_owner if @associated_records_by_owner
left_loader = Preloader.new(owners, left_loader = Preloader.new(owners,
through_reflection.name, through_reflection.name,
through_scope) through_scope)
...@@ -36,12 +40,31 @@ def associated_records_by_owner ...@@ -36,12 +40,31 @@ def associated_records_by_owner
preloader = Preloader.new(middle_records, preloader = Preloader.new(middle_records,
source_reflection.name, source_reflection.name,
reflection_scope) reflection_scope)
preloader.run preloader.run
through_records.each_with_object({}) { |(lhs,middles,assoc),h| middle_to_pl = preloader.preloaders.each_with_object({}) do |pl,h|
h[lhs] = middles.flat_map { |r| pl.owners.each { |middle|
h[middle] = pl
}
end
@associated_records_by_owner = through_records.each_with_object({}) { |(lhs,middles,assoc),h|
x = middle_to_pl[middles.first]
rhs_records = middles.flat_map { |r|
r.send(source_reflection.name) r.send(source_reflection.name)
}.compact }.compact
if x && x.loaded?
rs = rhs_records.sort_by { |rhs|
x.preloaded_records1.index(rhs)
}
else
rs = rhs_records
end
h[lhs] = rs
} }
end end
......
...@@ -41,6 +41,21 @@ def make_model(name) ...@@ -41,6 +41,21 @@ def make_model(name)
Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } } Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
end end
def test_ordered_habtm
person_prime = Class.new(ActiveRecord::Base) do
def self.name; 'Person'; end
has_many :readers
has_many :posts, -> { order('posts.id DESC') }, :through => :readers
end
posts = person_prime.includes(:posts).first.posts
assert_operator posts.length, :>, 1
posts.each_cons(2) do |left,right|
assert_operator left.id, :>, right.id
end
end
def test_singleton_has_many_through def test_singleton_has_many_through
book = make_model "Book" book = make_model "Book"
subscription = make_model "Subscription" subscription = make_model "Subscription"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册