Model.searchTse-Ching Ho2010-04-25
Ruby Conf Taiwan 2010
ActiveRecord
ActiveRecord v.s. Arelmodule ActiveRecord class Base class < self def unscoped @unscoped ||= Relation.new(self, arel_table) finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped end def arel_table @arel_table ||= Arel::Table.new(table_name, :engine => arel_engine) end end endendmodule ActiveRecord::NamedScope::ClassMethods def scoped(options = {}, &block) if options.present? relation = scoped.apply_finder_options(options) block_given? ? relation.extending(Module.new(&block)) : relation else current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone end endend
Scope with Arelclass Product < ActiveRecord::Base scope :name_like, lambda { |param| where(self.arel_table[:name].matches("%#{param}%")) } scope :attr_like, lambda { |attr, param| where(self.arel_table[attr].matches("%#{param}%")) } scope :attr_gt, lambda { |attr, param| where(self.arel_table[attr].gt(param)) }endProduct.name_like('Ruby')Product.attr_like(:name, 'Ruby') SELECT "products".* FROM "products" WHERE ("products"."name" LIKE '%Ruby%')Product.attr_gt(:price, 100) SELECT "products".* FROM "products" WHERE ("products"."price" > 100)
AREL
Match => LIKE
NotMatch => NOT LIKE
NotMatch ?products = Product.scopedputs products.where(products.table[:name].matches('%Ruby%')).to_sql SELECT "products".* FROM "products" WHERE ("products"."name" LIKE '%Ruby%')
products = Product.scopedputs products.where(%Q{"products"."name" NOT LIKE '%Ruby%'}).to_sqlputs products.where(%Q{"products"."name" NOT LIKE ?}, '%Ruby%').to_sql SELECT "products".* FROM "products" WHERE ("products"."name" NOT LIKE '%Ruby%')
products = Product.scopedproducts.where(products.table[:name].notmatches('%Ruby%'))
products = Product.searchproducts.name_not_like = 'Ruby'
products = Product.search('name_not_like' => 'Ruby')
NotMatch !require 'arel'# lib/arel/algebra/predicates.rbmodule Arel::Predicates class NotMatch < Binary; endend# lib/arel/algebra/attributes/attribute.rbmodule Arel class Attribute def notmatches(regexp); Predicates::NotMatch.new(self, regexp) end endend# lib/arel/engines/sql/predicates.rbmodule Arel::Predicates class NotMatch < Binary def predicate_sql; 'NOT LIKE' end endend# lib/arel/engines/memory/predicates.rbmodule Arel::Predicates class NotMatch < Binary def operator; :"!~" end endend
Rails 2.3 => Search Logic
Rails 3 => Search Logic
Write Your Own Search Method !!!
Meta Search
http://github.com/ernie/meta_search
MetaSearch::Searches::Base
module MetaSearch::Searches::Base def search(opts = {}) search_options = opts.delete(:search_options) || {} builder = MetaSearch::Builder.new(self, search_options) builder.build(opts) endendActiveRecord::Base.send :include, MetaSearch::Searches::ActiveRecord
Product.search('name_not_like' => 'Ruby')
MetaSearch::Buildermodule MetaSearch class Builder attr_reader :base, :relation, :join_dependency delegate :joins, :includes, :all, :count, :to_sql, :paginate, :find_each, :first, :last, :each, :to => :relation def initialize(base, opts = {}) @base = base @associations = {} @join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@base, [], nil) @relation = @base.scoped end def build(opts) @relation = @base.scoped opts.each_pair {|k, v| self.send("#{k}=", v)} self end endend
name_not_like=
MetaSearch::Buildermodule MetaSearch class Builder def method_missing(method_id, *args, &block) if match = matches_attribute_method(method_id) condition, attribute, association = match.captures.reverse build_method(association, attribute, condition) self.send(preferred_method_name(method_id), *args) elsif match = matches_where_method(method_id) condition = match.captures.first build_where_method(condition, Where.new(condition)) self.send(method_id, *args) else super end end endend
name_not_like=
@relation.where(products.table[:name].notmatches('%Ruby%')
MetaSearch::WhereMetaSearch::Where.add(['not_like', 'not_contain', 'notmatches', { :types => [:string, :text, :binary], :condition => :notmatches, :formatter => '"%#{param}%"'}])@@wheres['not_like'] = { :name => 'not_like', :aliases => ['not_contain', 'notmatches'], :types => [:string, :text, :binary], :condition => :notmatches, :formatter => Proc.new {|param| eval '"%#{param}%"'}, :validator => Proc.new {|param| !param.blank?}, :splat_param => false}#=> Where.new(@@wheres['not_like'])
Where.new('not_like') == Where.get('not_like') # if new with string
Arel:: Attribute#notmatches
More Possibilities ?
Article.where(:created_at > 100.days.ago, :title =~ 'Hi%').to_sql SELECT "articles".* FROM "articles" WHERE ("articles"."created_at" > '2010-01-05 20:11:44.997446') AND ("articles"."title" LIKE 'Hi%')
http://gist.github.com/265308http://github.com/ernie/meta_where
About Me
Tse-Ching Ho 何澤清
http://github.com/tsechingho
禾川資訊
http://grassbrook.com
END