When eager loading a HABTM and also including anything beyond
it, duplicate objects in the HABTM only load the first object's
further includes, but the further includes are not loaded for
subsequent objects. It's hard to explain succinctly, so here's an
example (with a test down further down):
Post has_and_belongs_to_many Categories and Categories has_many
Categorizations.
Post 1 has categories 1 and 3.
Post 2 has category 1.
Calling Post.find(:all, :include => {:category =>
:categorizations}) will cause the categorizations to be loaded for
the category objects tied to Post 1, however the category object
for Post 2 will not have the categorizations loaded. Subsequent
calls to it will show database queries executing.
From what I can tell...In preload_associations, after the first
level :category include is handled, it passes an array of
categories [1, 3, 1] to the next level of preload_associations for
the :categorizations. preload_associations calls uniq right at the
start on the records passed in. The uniq ends up changing it to [1,
3], and that is how the duplicates are lost. If I remove the uniq
call, the test below passes, however then it breaks
test_including_duplicate_objects_from_belongs_to. Since that uniq
call is in AR 2.2+, I am assuming this will happen on all versions
including 3.
Add the following test to cases\associations\eager_test.rb, and
also add a require for 'models\categorization' and the fixture for
:categorizations (patch attached, but it's against 2.2.2).
def test_deep_including_through_habtm
posts = Post.find(:all, :include => {:categories => :categorizations}, :order => "posts.id")
assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.size }
assert_no_queries { assert_equal 0, posts[0].categories[1].categorizations.size }
assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.size }
end
The third assert_no_queries will fail. If you change the very
first line of preload_associations to remove the uniq, the test
will pass, however test_including_duplicate_objects_from_belongs_to
will fail. For now, that's the only way I could get the test to
pass.