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

Merge branch 'typecast'

* typecast:
  correctly typecast keys, remove conditionals, reduce object allocations
  no need to to_a the scope
  remove extra flat_map array
  avoid extra empty array allocation
  extract owner id calculation to a method
  only call to_a when we have to
......@@ -29,6 +29,10 @@ def scope
end
def records_for(ids)
query_scope(ids)
end
def query_scope(ids)
scope.where(association_key.in(ids))
end
......@@ -52,12 +56,9 @@ def owner_key_name
raise NotImplementedError
end
# We're converting to a string here because postgres will return the aliased association
# key in a habtm as a string (for whatever reason)
def owners_by_key
@owners_by_key ||= owners.group_by do |owner|
key = owner[owner_key_name]
key && key.to_s
owner[owner_key_name]
end
end
......@@ -71,27 +72,34 @@ def associated_records_by_owner
owners_map = owners_by_key
owner_keys = owners_map.keys.compact
if klass.nil? || owner_keys.empty?
records = []
else
# Each record may have multiple owners, and vice-versa
records_by_owner = Hash[owners.map { |owner| [owner, []] }]
if klass && owner_keys.any?
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
records = sliced.flat_map { |slice| records_for(slice).to_a }
sliced.each { |slice|
records = records_for(slice)
caster = type_caster(records, association_key_name)
records.each do |record|
owner_key = caster.call record[association_key_name]
owners_map[owner_key].each do |owner|
records_by_owner[owner] << record
end
end
}
end
# Each record may have multiple owners, and vice-versa
records_by_owner = Hash[owners.map { |owner| [owner, []] }]
records.each do |record|
owner_key = record[association_key_name].to_s
owners_map[owner_key].each do |owner|
records_by_owner[owner] << record
end
end
records_by_owner
end
IDENTITY = lambda { |value| value }
def type_caster(results, name)
IDENTITY
end
def reflection_scope
@reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
end
......
......@@ -12,7 +12,7 @@ def initialize(klass, records, reflection, preload_options)
# Unlike the other associations, we want to get a raw array of rows so that we can
# access the aliased column on the join table
def records_for(ids)
scope = super
scope = query_scope ids
klass.connection.select_all(scope.arel, 'SQL', scope.bind_values)
end
......@@ -40,6 +40,11 @@ def associated_records_by_owner
end
end
def type_caster(results, name)
caster = results.column_types.fetch(name, results.identity_type)
lambda { |value| caster.type_cast value }
end
def build_scope
super.joins(join).select(join_select)
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册