Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails includes() LEFT OUTER JOIN with custom ON clause

In my scenario, I have a BlogPost model that has_and_belongs_to_many :categories. I want to filter out Blog Posts that belong to specific Categories, while still allowing for uncategorized Blog Posts (hence the need for a LEFT OUTER JOIN).

I expected this to work:

BlogPost.active.includes(:categories).where.not(categories: { id: [1, 2, 3] })

But it doesn't filter correctly because it puts the conditions at the end of the query, outside the LEFT OUTER JOIN (see this SO question / answer for the reason: SQL join: where clause vs. on clause)


This works, but it is ugly:

BlogPost.active.joins("LEFT OUTER JOIN blog_posts_categories
      ON blog_posts_categories.blog_post_id = blog_posts.id
      AND blog_posts_categories.category_id NOT IN(1, 2, 3)")

Is there an ActiveRecord-friendly way to add conditionals to the ON clause of a LEFT OUTER JOIN without manually typing it out the way I did?

like image 667
ncherro Avatar asked Feb 14 '14 18:02

ncherro


1 Answers

I think this might work.

BlogPost.active.joins(:categories).merge(Category.where.not(categories: { id: [1,2,3] })

and add it together with a scope that gets all the blogposts without a category. Another thought would be to instead use a has_many through relationship, and actually create a model for BlogPostCategory as you can add intermediate scopes on it.

like image 184
Sean Avatar answered Nov 07 '22 21:11

Sean