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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With