Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord Query Union

I've written a couple of complex queries (at least to me) with Ruby on Rail's query interface:

watched_news_posts = Post.joins(:news => :watched).where(:watched => {:user_id => id}) watched_topic_posts = Post.joins(:post_topic_relationships => {:topic => :watched}).where(:watched => {:user_id => id}) 

Both of these queries work fine by themselves. Both return Post objects. I would like to combine these posts into a single ActiveRelation. Since there could be hundreds of thousands of posts at some point, this needs to be done at the database level. If it were a MySQL query, I could simply user the UNION operator. Does anybody know if I can do something similar with RoR's query interface?

like image 791
LandonSchropp Avatar asked Jul 13 '11 23:07

LandonSchropp


1 Answers

Here's a quick little module I wrote that allows you to UNION multiple scopes. It also returns the results as an instance of ActiveRecord::Relation.

module ActiveRecord::UnionScope   def self.included(base)     base.send :extend, ClassMethods   end    module ClassMethods     def union_scope(*scopes)       id_column = "#{table_name}.id"       sub_query = scopes.map { |s| s.select(id_column).to_sql }.join(" UNION ")       where "#{id_column} IN (#{sub_query})"     end   end end 

Here's the gist: https://gist.github.com/tlowrimore/5162327

Edit:

As requested, here's an example of how UnionScope works:

class Property < ActiveRecord::Base   include ActiveRecord::UnionScope    # some silly, contrived scopes   scope :active_nearby,     -> { where(active: true).where('distance <= 25') }   scope :inactive_distant,  -> { where(active: false).where('distance >= 200') }    # A union of the aforementioned scopes   scope :active_near_and_inactive_distant, -> { union_scope(active_nearby, inactive_distant) } end 
like image 163
Tim Lowrimore Avatar answered Oct 03 '22 19:10

Tim Lowrimore