Created
April 20, 2012 03:07
-
-
Save dfurber/2425600 to your computer and use it in GitHub Desktop.
Union Query in AR with Lazy Evaluation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module UnionQuery | |
extend ActiveSupport::Concern | |
included do | |
def self.union(relation_to_unite, type=nil) | |
LazyUnionQuery.new self, relation_to_unite, type | |
end | |
end | |
end | |
class LazyUnionQuery | |
def initialize(klass, query2, type=:all) | |
@klass, @query2, @type = klass, query2, type | |
@query1 = @klass.scoped | |
end | |
def to_sql | |
if @limit or @order | |
to_sql_with_extras | |
else | |
to_sql_without_extras | |
end | |
end | |
def limit(value) | |
@limit = value | |
self | |
end | |
def order(value) | |
@order = value | |
self | |
end | |
def to_a | |
@records ||= @klass.find_by_sql to_sql | |
end | |
def all | |
to_a | |
end | |
def first | |
to_a.first | |
end | |
def each(&block) | |
to_a.each &block | |
end | |
def as_json(options = nil) #:nodoc: | |
to_a.as_json(options) | |
end | |
# Returns size of the records. | |
def size | |
to_a.length | |
end | |
# Returns true if there are no records. | |
def empty? | |
to_a.empty? | |
end | |
def any? | |
if block_given? | |
to_a.any? { |*block_args| yield(*block_args) } | |
else | |
!empty? | |
end | |
end | |
private | |
def to_sql_without_extras | |
"#{@klass.scoped.to_sql} UNION #{@type.to_s} #{@query2.to_sql}" | |
end | |
def to_sql_with_extras | |
extras = [] | |
extras << "ORDER BY #{@order}" if @order | |
extras << "LIMIT #{@limit}" if @limit | |
"(#{@klass.scoped.to_sql}) UNION #{@type.to_s} (#{@query2.to_sql}) #{extras.join ' '}" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
How to do union queries with lazy evaluation. | |
class Thing1 < ActiveRecord::Base | |
include UnionQuery | |
scope :recent, lambda { order 'created_at desc' } | |
end | |
class Thing2 < ActiveRecord::Base | |
end | |
Thing1 easily unites with another Thing1 query because they are of the same class: | |
Thing1.recent.union(Thing1.where('name like "%Bob%"')) | |
You can also unite Thing1 with Thing2. The objects returned will all be Thing1. Maybe refactor to pass in a presenter class? | |
Thing1.select('name').union Thing2.select('name') | |
If you want to put limit and order on the union, you can: | |
Thing1.select('name').union(Thing2.select('name')).limit(10).order('name desc') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment