Would it? The more I'm looking into this the more I'm wondering what
ActiveRecord does here.
In the logs I'm seeing something like this (simplyfied):
Article Load IDs For Limited Eager Loading (0.004604)
SELECT DISTINCT contents.id FROM contents
LEFT OUTER JOIN taggings ON (taggings.taggable_id = contents.id AND
taggings.taggable_type = 'Content')
LEFT OUTER JOIN tags ON tags.id = taggings.tag_id
WHERE tags.name IN ('tag')) AND ( (contents.`type` = 'Article' ) )
ORDER BY contents.published_at DESC LIMIT 15
Article Load Including Associations (0.004464)
SELECT * FROM contents
LEFT OUTER JOIN taggings ON (taggings.taggable_id = contents.id AND
taggings.taggable_type = 'Content')
LEFT OUTER JOIN tags ON tags.id = taggings.tag_id
WHERE tags.name IN ('tag')) AND contents.id IN ('972', '971', '448',
'181')
ORDER BY contents.published_at DESC
That is, these queries are practically identical besides that the
first query fetches the ids that are used in the second query.
The :condition => "tags.name in (...)" part is used in both queries.
Why? This doesn't seem to make sense to me. I guess this is some kind
of edge case and the same behaviour would make sense in other cases
that are treated the same by ActiveRecord?
First you have to understand why there are two queries. It only
occurs when you use eager loading along with a :limit option. A LIMIT
clause only restricts the total rows, which is incompatible with eager
loading of associations that have many children since many rows will
be returned for each of the base items. Therefore, AR first fetches
just the DISTINCT base ids so it gets the right number of items.
So, yes, you are talking about an edge case. If you remove the :limit
or the :include, there is only one query. I can see your point that
the conditions are redundant on the second query, but on the same
token, changing this behaviour of ActiveRecord doesn't solve the
problem because it still exists if you don't have a :limit.
The core problem is that "tags.name IN ('tag')" affects the eager
loaded table. That's why you have to do an explicit join and alias
the table AS something_else.
> "INNER JOIN taggings ON taggings.article_id = articles.id AND
> taggings.tag_id = #{id}"
> Then you drop the condition.
Yes, similar to what ActiveRecord does internally, too. But
ActiveRecord doesn't "drop the condition". Shouldn't it? (If it would
my problem with Mephisto's tagging pages was solved.)
What I mean is the INNER JOIN will automatically drop any rows that
don't match the ON conditions. You don't need the condition you
specified, because this INNER JOIN already implies it. Think of this
JOIN as the equivalent of the :conditions you want. It serves the
purpose you are looking for exactly because it only allows articles
that are tagged with a specific tag, but it does not impost a
condition on the eager loaded tables.
> Also, very important, this won't currently work if you add a :limit
> clause because the "eager loading of associations with limit" query
> that is run to fetch the IDs will drop the JOINs and then the final
> query will use a list of IDs that doesn't meet the criteria, therefore
> resulting in fewer than your requested number of results.
As far as I can see from the log (see above) no JOINs are dropped (in
this case)? Also, the :limit is only applied to the first query
("Article Load IDs For Limited Eager Loading"), so this theoretically
should work (in this case)?
I'm talking about :joins you specify, not the ones created
automatically by eager loading.