Upload
tse-ching-ho
View
4.514
Download
3
Embed Size (px)
DESCRIPTION
This is a lightning talk in Rubyconf Taiwan 2010. I talk about the principles of how to customize your own Model.search method for rails 3. Meta search is an implementation for this purpose and also an available replacement of searchlogic for now.
Citation preview
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