Skip to content

Instantly share code, notes, and snippets.

@kvirani
Created December 19, 2011 15:09
Show Gist options
  • Save kvirani/1497596 to your computer and use it in GitHub Desktop.
Save kvirani/1497596 to your computer and use it in GitHub Desktop.
Searchability mixin for ActiveRecord
module S6
module Searchable
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def make_searchable(opts={})
# Store the fields and includes as class attributes for use when #with_keywords is called and search query needs to be built
cattr_accessor :search_fields
cattr_accessor :search_includes
self.search_fields = opts.delete(:fields)
self.search_includes = opts.delete(:include)
# This is the scope to be used on the ActiveRecord model.
# Eg: User.with_keywords(params[:q]) # --> User.with_keywords('Khurram Virani')
scope :with_keywords, lambda { |query|
chain = nil
if self.search_includes && self.search_includes.size > 0 # always expect array
self.search_includes.each {|j| chain = chain ? chain.includes(j.to_sym) : includes(j.to_sym) }
end
chain ? chain.where(build_search_conditions(query)) : where(build_search_conditions(query))
}
end
protected
# Builds conditions for the AR #where method.
# Dynamic Params are sent in using a Hash of :keys instead of the traditional Array approach
def build_search_conditions(query)
words = query.strip.upcase.split.collect{|w| w.strip }
query = []
params = {}
words.each_with_index do |word, i|
key = "word#{i}"
query << build_query_string_for_key(key)
params[key.to_sym] = "%#{word}%"
end
[query.join(" AND "), params]
end
# This will build something like "(UPPER(videos.name) LIKE :#{key} OR UPPER(videos.description) LIKE :#{key} OR UPPER(videos.tags) LIKE :#{key})"
def build_query_string_for_key(key)
"(" + self.search_fields.collect {|field| "UPPER(CAST(#{field.to_s} AS varchar)) LIKE :#{key}" }.join(" OR ") + ")"
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment