In looking at (and trying to use) a has_one association, I noticed
that the eager loading code for has one associations doesn''t respect
the "limit one" per record, and could potentially pull back millions
of records (though there is code to make sure only one object per
association is actually turned converted to an ActiveRecord object).
Since I always use Postgresql with my Rails projects nowadays, I
naturally assume that a subselect is the way to go here, but I don''t
know where sqllite is with subselects. I know MySQL now uses them, but
I don''t know if Rails is keeping to some kind of lowest common
database denominator. If not, I think it might be worth it to try to
add some logic to
ActiveRecord::AssociationPreload#find_associated_records, but I don''t
want to get into a whole testing, branching thing if this is too
database-specific.
The code I''m envisioning would be something like this:
<code>
def min_or_max_from_order(reflection)
if reflection.nil? || reflection.options[:order].blank?
return ''MAX''
end
case (reflection.options[:order].split.compact.last.upcase
rescue ''ASC'')
when ''DESC''
''MAX''
else
''MIN''
end
end
def order_column_from_reflection(reflection)
if reflection.nil? || reflection.options[:order].blank?
return "id"
end
reflection.options[:order].split.compact.first.split(''.'').last
end
def find_associated_records(ids, reflection, preload_options)
options = reflection.options
table_name = reflection.klass.quoted_table_name
if interface = reflection.options[:as]
conditions = "#{reflection.klass.quoted_table_name}.#
{connection.quote_column_name "#{interface}_id"}
#{in_or_equals_for_ids
(ids)} and #{reflection.klass.quoted_table_name}.#
{connection.quote_column_name "#{interface}_type"} = ''#
{self.base_class.sti_name}''"
else
foreign_key = reflection.primary_key_name
conditions = "#{reflection.klass.quoted_table_name}.#
{foreign_key} #{in_or_equals_for_ids(ids)}"
end
if reflection.macro == :has_one
if reflection.options[:order]
# we''ll try subselects here
select = " #{reflection.klass.quoted_table_name}.#
{foreign_key} as __original_key, #{table_name}.* "
conditions <<
" and ( #{reflection.klass.quoted_table_name}.#
{order_column_from_reflection(reflection)} (select
#{min_or_max_from_order(reflection)}
(\"subselect_table1\".#{order_column_from_reflection(reflection)})
from #{reflection.klass.quoted_table_name} as
\"subselect_table1\"
where \"subselect_table1\".#{foreign_key} = #
{reflection.klass.quoted_table_name}.#{foreign_key} "
conditions << append_conditions if reflection.options
[:conditions]
conditions << " ) ) "
end
end
conditions << append_conditions(reflection, preload_options)
reflection.klass.with_exclusive_scope do
reflection.klass.find(:all,
:select => (preload_options[:select] ||
options[:select] || "#{table_name}.*"),
:include => preload_options[:include] ||
options[:include],
:conditions => [conditions, ids],
:joins => options[:joins],
:group => preload_options[:group] ||
options[:group],
:order => preload_options[:order] ||
options[:order])
end
end
</code>
Clearly this wouldn''t solve all cases, and some restrictions would
have to be laid out in the docs, especially about valid "order by"
phrasing, and resolving table name issues in adding the reflection
conditions to the subselect conditions...
But anyone have any thoughts about this?
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Core" group.
To post to this group, send email to rubyonrails-core@googlegroups.com.
To unsubscribe from this group, send email to
rubyonrails-core+unsubscribe@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/rubyonrails-core?hl=en.