用 acts_as_ferret 進行搜索的心得
下載ferret.gem , acts_as_ferret plugin
gem : gem install ferret
plugin : svn://projects.jkraemer.net/acts_as_ferret/trunk/plugin/acts_as_ferret,直接check out下來放到vendor/plugin 目錄下即可
現有一個model:
class Provider< fields =""> [:name,:description,:address,:website,:score,:phone,:direction]
.......... ............
end
這裡:fields就是要進行索引的對象屬性了,當然你也可以不指定,直接就一句"acts_as_ferret",默認不指定的時候是對所有列進行索引,先時事效果:
>>ruby script/console
>>Provider.find_by_contents(" 你要 搜索 的內容")
OK,如果沒有意外的話, 結果會返回一個ActsAsFerret::SearchResults對象。如果出錯了,回過去檢查一下,短短幾步,檢查起來也應該很方便。
幾個tip:
# "關鍵詞1" AND “關鍵詞2”
Provider.find_by_contents(" 關鍵詞1 關鍵詞2")
#"關鍵詞1" OR "關鍵詞2"
Provider.find_by_contents("關鍵詞1 OR 關鍵詞2")
#貌似“關鍵詞1”的結果
Provider.find_by_contents("關鍵詞1~")
上面這幾個都是小兒科啦
下面我們到acts_as_ferret plugin內部去看看,
acts_as_ferret/lib 瞄到 acts_as_ferret.rb 第116行(VERSION=181)
“ActiveRecord::Base.extend ActsAsFerret::ActMethods”
哈哈,許多blog中都建議在要進行分詞查詢的model中加入下面一段代碼:
def full_text_search(q, options = {})
return nil if q.nil? or q.strip.blank?
default_options = {:limit => 10, :page => 1}
options = default_options.merge options
options[:offset] = options[:limit] * (options.delete(:page).to_i-1)
results = self.find_by_contents(q, options)
[results.total_hits, results]
end
不過,本著DRY的原則,我決定從“ActiveRecord::Base.extend ActsAsFerret::ActMethods”這句話上做文章,既然ActiveRecord::Base 繼承了 ActsAsFerret::ActMethods ,何不將這段代碼加入到ActMethods裡去,讓所有model都擁有 full_text_search的功能?
module ActsAsFerret #:nodoc:
# This module defines the acts_as_ferret method and is included into
# ActiveRecord::Base
module ActMethods
def full_text_search(q, options = {})
return nil if q.nil? or q.strip.blank?
default_options = {:limit => 10, :page => 1}
options = default_options.merge options
options[:offset] = options[:limit] * (options.delete(:page).to_i-1)
results = self.find_by_contents(q, options)
[results.total_hits, results]
end
................ ....................
end
end
重啟一下,Provider.full_text_search方法應該出來了,有問題的話自己回去檢查看看
下面就是俗套,在application.rb中加入:
def pages_for(size, options = {})
default_options = {:per_page => 10}
options = default_options.merge options
pages = Paginator.new self, size, options[:per_page], (params[:page]||1)
pages
end
這些種種工作都是為了在controller以及view中調用:
controller:
def search @query=params[:query]
@total, @providers = Provider.full_text_search(@query, :page => (params[:page]||1))
@pages = pages_for(@total)
end
view:
<%= pagination_links(@pages, :params => {:query=>@query}) %>
關於 find_by_contents(q, options = {}, find_options = {})
中的find_options,考慮一下一種情況:
provider表結構:
id name description category_id
如果要對特定category_id的數據進行檢索,應如何?這裡就用到了find_option了:
代碼:
Vendor.find_by_contents(" 要進行檢索的關鍵字 ",{:limit=>30},{:joins=>"providers join categories on providers.category_id = categories.id ", :conditions =>" providers.category_id = XXXXX "})
其他一些小技巧大家可以到到網上找找,這裡就不一一列舉了。