星期四, 4月 26, 2007

用 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 "})
其他一些小技巧大家可以到到網上找找,這裡就不一一列舉了。

星期一, 4月 23, 2007

Ruby vs. Java

星期三, 4月 11, 2007

Ruby Amazon E-Commerce REST Service API (amazon-ecs)

Here is a generic Amazon E-commerce REST API for ruby. It supports configurable default options and method call options. It uses Hpricot to parse the XML output. Use Response and Element wrapper classes for easy access to the XML elements, and it supports ECS 4.0. read more ...

星期一, 4月 02, 2007

Ruby on Rails Security Blog

Ruby on Rails Security Blog is a new blog for addressing security issues about ror. However, most of the topics are related to MySQL currently.