Transcript
Page 1: model.search: customize your own search logic

Model.searchTse-Ching Ho2010-04-25

Ruby Conf Taiwan 2010

Page 2: model.search: customize your own search logic

ActiveRecord

Page 3: model.search: customize your own search logic

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

Page 4: model.search: customize your own search logic

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)

Page 5: model.search: customize your own search logic

AREL

Page 6: model.search: customize your own search logic

Match => LIKE

NotMatch => NOT LIKE

Page 7: model.search: customize your own search logic

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')

Page 8: model.search: customize your own search logic

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

Page 9: model.search: customize your own search logic

Rails 2.3 => Search Logic

Rails 3 => Search Logic

Page 10: model.search: customize your own search logic

Write Your Own Search Method !!!

Page 11: model.search: customize your own search logic

Meta Search

http://github.com/ernie/meta_search

Page 12: model.search: customize your own search logic

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')

Page 13: model.search: customize your own search logic

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=

Page 14: model.search: customize your own search logic

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%')

Page 15: model.search: customize your own search logic

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

Page 16: model.search: customize your own search logic

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

Page 17: model.search: customize your own search logic

About Me

Tse-Ching Ho 何澤清

http://github.com/tsechingho

禾川資訊

http://grassbrook.com

Page 18: model.search: customize your own search logic

END


Recommended