Eager loading problem

What’s up with this?

Board
has_many :boardings
has_many :topics, :through => :boardings
end

Topics
has_many :boardings
has_many :boards, :through => :boardings
end

Boardings
belongs_to :board
belongs_to :topic
end

Typical has_many :through setup. The following works:

Topic.find(:all, :include => [:boards])

But this fails:

board = Board.find(:first)
topic = board.topics.find(:first, :include => [:boards])

This works:

board = Board.find(:first)
topic = board.topics.find(:first, :include => [{:boardings => :board}])

However that forces me to use: topic.boardings.collect {|b| b.board}
when under ordinary circumstances it should simply be topic.boards

And here is the full error I get:
Mysql::Error: #42000Not unique table/alias: ‘boardings’: SELECT DISTINCT
topics.id FROM topics LEFT OUTER JOIN boardings ON (topics.id =
boardings.topic_id) LEFT OUTER JOIN boards ON (boards.id =
boardings.board_id) INNER JOIN boardings ON topics.id =
boardings.topic_id WHERE ((boardings.board_id = 1) AND
(topics.archived_on IS NULL)) LIMIT 0, 10

I guess the problem is “boardings” is being referred to twice in the
same query and MySQL doesn’t like it. Is there a way to avoid this?

Hi –

On Sun, 24 Jun 2007, R. Elliott M. wrote:

end
But this fails:
when under ordinary circumstances it should simply be topic.boards
You should be able to do topic.boards there, I believe. The topic has
eagerly loaded its boardings, and the boardings have loaded their
boards, so the topic should be able to do its :through thing and get
the boards.

What result did you get from topic.boards using that query?

David

Wow. I’m unsure what made me so convinced I couldn’t do that. I feel
like a dumbass.

Thanks. Thread over :>

Wait, I remember now why that doesn’t work as intended. If I use
topic.boards in my view it will cause a query to run that finds those
boards.

So my problem is, the boards are eager loaded, but only if I user
topic.boardings.boards.

Hi –

On Sun, 24 Jun 2007, R. Elliott M. wrote:

Wait, I remember now why that doesn’t work as intended. If I use
topic.boards in my view it will cause a query to run that finds those
boards.

So my problem is, the boards are eager loaded, but only if I user
topic.boardings.boards.

topic.boardings doesn’t have a boards method, though (or did you mean
that as shorthand for the #collect version?). Are you sure a new
query is being generated? It really shouldn’t be. Can you try this
in the console and post your console session and log file in parallel?

David

Hi –

On Sun, 24 Jun 2007, [email protected] wrote:

So my problem is, the boards are eager loaded, but only if I user
topic.boardings.boards.

topic.boardings doesn’t have a boards method, though (or did you mean
that as shorthand for the #collect version?). Are you sure a new
query is being generated? It really shouldn’t be. Can you try this
in the console and post your console session and log file in parallel?

On second thought…

I’m seeing what you’re seeing: it doesn’t take advantage of the eager
loading. OK, I’ll have some more coffee and start again :slight_smile:

David

I apologize, I was too anxious to post and made typos.

In my previous post I meant to say topic.boardings.board is the only way
I can access the associated boards without an extra query running.
Thus, the only way I can get all of the boards without for a particular
Topic object without having queries run during rendering is to use the
topic.boardings.collect {|b| b.board}. Which isn’t horrible, but
definitely not convenient. I can use topic.boards – it doesn’t
actually cause any error – but it does cause queries to run during
rendering since Rails doesn’t “think” the topic’s boards have been
loaded already.

OK I’ve sort of figured it out but it’s still not actually convenient.
At least this way I can solve the problem in the controllers/model
instead of having to worry about it in helpers/views using Array#collect
method:

board = Board.find(:first)
topics = Topic.find(:first, :joins => “INNER JOIN boardings b ON
topics.id = b.topic_id WHERE ((b.board_id = #{board.id}))”, :include =>
[:boards])

I alias the first reference of boardings as “b” so it doesn’t conflict
with the left outer join for boards later in the query.

It should have to be this difficult though!

Scratch that. It works fine UNTIL I specify my own :condition, at which
point Rails (again) poorly constructs the query and I get an
ActiveRecord::StatementInvalid.

P.S. I meant to say it -shouldn’t- have to be this difficult :frowning:

Hi –

On Sun, 24 Jun 2007, R. Elliott M. wrote:

rendering since Rails doesn’t “think” the topic’s boards have been
loaded already.

I played around with this some more after realizing that my assumption
was wrong (i.e., that the :through stuff could and would happen
without a further query if all the necessary records had been eagerly
loaded), and I couldn’t come up with anything either. The collect
thing really isn’t so bad, since everything’s already loaded, and it
could be wrapped up in a method… but I’ll be interested to see
whether anyone has a further suggestion for doing it all at once,
including the :through’s. (Or if someone can demonstrate why it’s
illogical/impossible.)

David