Active Record and the IN clause

March 19th, 2007 by Trevor

So here's the setup:

Site => Categories => Forums => Topics => Posts

That's a pretty standard setup for a web-based forum, where Topics are in a particular Forum and those Forums are, in turn, organized by Category. Retrieving the Topics in a particular Forum is easy, since I have a "forum_id" column in my "topics" table:

@topics = Topic.find(:all, :conditions => ["forum_id = ?", @forum.id])

But what if I want to retrieve the Topics in a particular Category? That's not quite as easy, since there's no "category_id" column in my "topics" table. What to do? The "WHERE IN" clause is what you're supposed to use here, I think. That's when you make a query something like this:

@topics = Topic.find(:all, :conditions => ["forum_id in ?", @forums])

This might read something like "find all topics where the forum_id is in my @forums object". But (of course) that won't work. What to do? Hack together a string of forum_ids that's worthy of passing into this query? There's got to be a better way.

When in doubt, I always turn to the fabulous open-source work of one Rick Olson, because searching around the Rails API or plain old Google isn't nearly as informative as seeing real live code examples. And when it comes to really nice real live code examples, it doesn't get any better than techno-weenie.

So, what's the solution?

@category = Category.find(params[:id])
@forums = Forum.find_all_by_category_id(@category.id)
@topics = Topic.find(:all, :conditions => ["forum_id in (?)", @forums.collect(&:id)])

Easy as pie.

Comments

Posting code? Please use Pastie.

Have a question? Please visit the Forum.

5 Comments

  1. would you consider using:

    category = Category.find(params[:id])
    @forums = Forum.find_all_by_category_id(@category.id)
    @topics = @forums.topics

    Comment by aaron on April 3, 2007

  2. Thanks for the tip, aaron. You’re totally right - I can’t believe I forgot about that Rails helper.

    In any case, I’m sure this collection trick will come in handy sooner or later.

    Comment by Trevor on April 3, 2007

  3. Though currently undocumented, using the hash conditions syntax, you can construct IN clauses by using array values to your condition keys.

    Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })

    Check out what you can do with Ranges values too!

    Pretty neat.


  4. Duncan, that’s nice to know! Thanks for your comment.

    Comment by Trevor on May 9, 2007

  5. [...] an invalid query according to MySQL, at least… that’s been my experience in PHP. But I stumbled upon a solution that works quite well (from the [...]