Rails 中模型支援的搜尋。一個精彩的例子:
## app/searches/post_search.rb
class PostSearch < TalentScout :: ModelSearch
criteria :title_includes do | string |
where ( "title LIKE ?" , "% #{ string } %" )
end
criteria :within , choices : {
"Last 24 hours" => 24 . hours ,
"Past Week" => 1 . week ,
"Past Month" => 1 . month ,
"Past Year" => 1 . year ,
} do | duration |
where ( "created_at >= ?" , duration . ago )
end
criteria :only_published , :boolean , default : true do | only |
where ( "published" ) if only
end
order :created_at , default : :desc
order :title
end
## app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@search = model_search
@posts = @search . results
end
end
<!-- app/views/posts/index.html.erb -->
<%= form_with model : @search , local : true , method : :get do | form | %>
<%= form . label :title_includes %>
<%= form . text_field :title_includes %>
<%= form . label :within %>
<%= form . select :within , @search . each_choice ( :within ) , include_blank : true %>
<%= form . label :only_published %>
<%= form . check_box :only_published %>
<%= form . submit %>
<% end %>
< table >
< thead >
< tr >
< th >
<%= link_to_search "Title" , @search . toggle_order ( :title ) %>
<%= img_tag " #{ @search . order_directions [ :title ] || "unsorted" } _icon.png" %>
</ th >
< th >
<%= link_to_search "Time" , @search . toggle_order ( :created_at ) %>
<%= img_tag " #{ @search . order_directions [ :created_at ] || "unsorted" } _icon.png" %>
</ th >
</ tr >
</ thead >
< tbody >
<% @posts . each do | post | %>
< tr >
< td > <%= link_to post . title , post %> </ td >
< td > <%= post . created_at %> </ td >
</ tr >
<% end %>
</ tbody >
</ table >
在上面的例子中:
PostSearch
類別負責搜尋Post
模型。它可以應用其定義的標準的任意組合,自動忽略缺失、空白或無效的輸入值。它還可以按其定義的順序之一對結果進行排序,無論是升序還是降序。PostsController#index
使用model_search
幫助器建構一個PostSearch
實例,並將其指派給@search
變數以供日後在視圖中使用。搜尋結果也會分配給一個變數以在視圖中使用。@search
變數的搜尋表單。 link_to_search
幫助器用於在表頭中建立對結果進行排序的連結。請注意,此處使用的toggle_order
方法傳回一個新的搜尋對象,使@search
保持不變。有關本範例中使用的方法的詳細說明,請參閱 API 文件。
您可以使用talent_scout:search
產生器來產生模型搜尋類別定義。例如,
$ rails generate talent_scout:search post
將產生一個檔案“app/searches/post_search.rb”,其中包含:
class PostSearch < TalentScout :: ModelSearch
end
搜尋類別繼承自TalentScout::ModelSearch
。它們的目標模型類別是從搜尋類別名稱推斷出來的。例如, PostSearch
將預設搜尋Post
模型。若要覆寫此推理,請使用ModelSearch::model_class=
:
class EmployeeSearch < TalentScout :: ModelSearch
self . model_class = Person # search for Person models instead of `Employee`
end
搜尋條件是使用ModelSearch::criteria
方法定義的。可以透過以下三種方式之一指定條件定義:使用隱式 where 子句、使用明確查詢區塊或使用模型範圍參考。為了說明這一點,以下三個:title
標準都是等效的:
class Post < ActiveRecord :: Base
scope :title_equals , -> ( string ) { where ( title : string ) }
end
class PostSearch < TalentScout :: ModelSearch
criteria :title
criteria :title do | string |
where ( title : string )
end
criteria :title , & :title_equals
end
請注意,明確查詢區塊是在模型的ActiveRecord::Relation
上下文中評估的,就像 Active Record scope
區塊一樣。
條件定義可以指定資料類型,這會導致其輸入值在傳遞到查詢區塊或範圍之前進行類型轉換。舉個例子:
class PostSearch < TalentScout :: ModelSearch
criteria :created_on , :date do | date |
where ( created_at : date . beginning_of_day .. date . end_of_day )
end
end
PostSearch . new ( created_on : "Dec 31, 1999" )
此處,傳遞給PostSearch
建構子的字串"Dec 31, 1999"
在傳遞給查詢區塊之前會被型別轉換為Date
。
預設條件類型為:string
,這表示預設情況下所有輸入值都會轉換為字串。無論搜尋物件是如何建構的,無論是來自強型別值或來自搜尋表單請求參數,此預設值(與無型別轉換的預設值相反)都可確保行為一致。
可用的條件類型與活動模型屬性相同: :big_integer
、 :boolean
、 :date
、 :datetime
、 :decimal
、 :float
、 :integer
、 :string
、 :time
,以及您定義的任何自訂類型。
還提供了一種額外的便利類型: :void
。 :void
類型的型別轉換類似:boolean
,但當型別轉換的值為 false 時,會封鎖套用條件。例如:
class PostSearch < TalentScout :: ModelSearch
criteria :only_edited , :void do
where ( "modified_at > created_at" )
end
end
# The following will apply `only_edited`:
PostSearch . new ( only_edited : true )
PostSearch . new ( only_edited : "1" )
# The following will skip `only_edited`:
PostSearch . new ( only_edited : false )
PostSearch . new ( only_edited : "0" )
PostSearch . new ( only_edited : "" )
標準定義可以指定選擇,而不是指定類型。選擇定義了一組可以傳遞給查詢區塊的值。
選擇可以指定為人類友善值的陣列:
class PostSearch < TalentScout :: ModelSearch
criteria :category , choices : %w[ Science Tech Engineering Math ] do | name |
where ( category : name . downcase )
end
end
……或作為具有人類友好密鑰的哈希:
class PostSearch < TalentScout :: ModelSearch
criteria :within , choices : {
"Last 24 hours" => 24 . hours ,
"Past Week" => 1 . week ,
"Past Month" => 1 . month ,
"Past Year" => 1 . year ,
} do | duration |
where ( "created_at >= ?" , duration . ago )
end
end
傳遞給查詢區塊的值將是數組中的值之一或雜湊中的值之一。搜尋物件可以使用任何數組值或哈希鍵或雜湊值來建構:
PostSearch . new ( category : "Math" )
PostSearch . new ( within : "Last 24 hours" )
PostSearch . new ( within : 24 . hours )
但如果指定了無效的選擇,則不會套用相應的標準:
# The following will skip the criteria, but will not raise an error:
PostSearch . new ( category : "Marketing" )
PostSearch . new ( within : 12 . hours )
條件定義可以指定預設值,當輸入值遺失時,該預設值將傳遞到查詢區塊。預設值也會出現在搜尋表單中。
class PostSearch < TalentScout :: ModelSearch
criteria :within_days , :integer , default : 7 do | num |
where ( "created_at >= ?" , num . days . ago )
end
end
# The following are equivalent:
PostSearch . new ( )
PostSearch . new ( within_days : 7 )
若符合以下任一條件,則不會套用標準:
缺少條件輸入值,且未指定預設值。
搜尋物件是使用ActionController::Parameters
(而不是雜湊)建構的,並且條件輸入值為blank?
,並且沒有指定預設值。 (此行為可防止空搜尋表單欄位影響搜尋結果。)
標準輸入值的類型轉換失敗。例如:
class PostSearch < TalentScout :: ModelSearch
criteria :created_on , :date do | date |
where ( created_at : date . beginning_of_day .. date . end_of_day )
end
end
# The following will skip `created_on`, but will not raise an error:
PostSearch . new ( created_on : "BAD" )
標準定義指定類型:void
,且類型轉換的輸入值是 falsey。
標準定義指定了選項,但輸入值不是有效選項。
criteria 查詢區塊傳回nil
。例如:
class PostSearch < TalentScout :: ModelSearch
criteria :minimum_upvotes , :integer do | minimum |
where ( "upvotes >= ?" , minimum ) unless minimum <= 0
end
end
# The following will skip the `minimum_upvotes` where clause:
PostSearch . new ( minimum_upvotes : 0 )
搜尋結果順序是使用ModelSearch::order
方法定義的:
class PostSearch < TalentScout :: ModelSearch
order :created_at
order :title
order :category
end
PostSearch . new ( order : :created_at )
PostSearch . new ( order : :title )
PostSearch . new ( order : :category )
一次只能套用一個訂單,但一個訂單可以包含多個欄位:
class PostSearch < TalentScout :: ModelSearch
order :category , [ :category , :title ]
end
# The following will order by "category, title":
PostSearch . new ( order : :category )
選擇這種受限設計是因為它允許使用更簡單的單列排序 UI 進行策劃的多列排序,並且它可以防止可能不受資料庫索引支援的臨時多列排序。
訂單可以按升序或降序來應用。 ModelSearch#toggle_order
方法將以升序套用順序,或將套用的順序方向從升序改為降序:
class PostSearch < TalentScout :: ModelSearch
order :created_at
order :title
end
# The following will order by "title":
PostSearch . new ( ) . toggle_order ( :title )
PostSearch . new ( order : :created_at ) . toggle_order ( :title )
# The following will order by "title DESC":
PostSearch . new ( order : :title ) . toggle_order ( :title )
請注意, toggle_order
方法不會修改現有的搜尋對象。相反,它使用新順序和前一個搜尋對象的條件值來建立新的搜尋對象。
當依降序套用多列順序時,所有欄位都會受到影響:
class PostSearch < TalentScout :: ModelSearch
order :category , [ :category , :title ]
end
# The following will order by "category DESC, title DESC":
PostSearch . new ( order : :category ) . toggle_order ( :category )
若要避免此行為並以靜態方向修復列,請將" ASC"
或" DESC"
附加到列名稱:
class PostSearch < TalentScout :: ModelSearch
order :category , [ :category , "created_at ASC" ]
end
# The following will order by "category, created_at ASC":
PostSearch . new ( order : :category )
# The following will order by "category DESC, created_at ASC":
PostSearch . new ( order : :category ) . toggle_order ( :category )
透過附加適當的後綴,可以直接按升序或降序應用順序,而無需使用toggle_order
:
class PostSearch < TalentScout :: ModelSearch
order :title
end
# The following will order by "title":
PostSearch . new ( order : :title )
PostSearch . new ( order : "title.asc" )
# The following will order by "title DESC":
PostSearch . new ( order : "title.desc" )
如上例所示,預設後綴是".asc"
和".desc"
。選擇它們是因為它們對國際化友好。它們可以作為訂單定義的一部分被覆蓋:
class PostSearch < TalentScout :: ModelSearch
order "Title" , [ :title ] , asc_suffix : " (A-Z)" , desc_suffix : " (Z-A)"
end
# The following will order by "title":
PostSearch . new ( order : "Title" )
PostSearch . new ( order : "Title (A-Z)" )
# The following will order by "title DESC":
PostSearch . new ( order : "Title (Z-A)" )
可以將訂單指定為預設訂單,這將導致在未另行指定訂單時套用該訂單:
class PostSearch < TalentScout :: ModelSearch
order :created_at , default : :desc
order :title
end
# The following will order by "created_at DESC":
PostSearch . new ( )
# The following will order by "created_at":
PostSearch . new ( order : :created_at )
# The following will order by "title":
PostSearch . new ( order : :title )
注意,透過分別指定default: :asc
或default: :desc
,預設順序方向可以是升序或降序。此外,如同一次只能套用一張訂單一樣,也只能指定一張預設訂單。
可以使用ModelSearch::default_scope
定義預設搜尋範圍:
class PostSearch < TalentScout :: ModelSearch
default_scope { where ( published : true ) }
end
無論條件或訂單輸入值為何,都將套用預設範圍。
控制器可以使用model_search
輔助方法使用目前請求的查詢參數建構搜尋物件:
class PostsController < ApplicationController
def index
@search = model_search
@posts = @search . results
end
end
在上面的範例中, model_search
建構了一個PostSearch
物件。模型搜尋類別會自動從控制器類別名稱派生。若要覆寫模型搜尋類,請使用::model_search_class=
:
class EmployeesController < ApplicationController
self . model_search_class = PersonSearch
def index
@search = model_search # will construct PersonSearch instead of `EmployeeSearch`
@employees = @search . results
end
end
在這些範例中,搜尋物件儲存在@search
中以在視圖中使用。請注意, @search.results
傳回ActiveRecord::Relation
,因此可以套用任何其他範圍(例如分頁)。
搜尋表單可以使用 Rails 的表單產生器和搜尋物件來呈現:
<%= form_with model: @search, local: true, method: :get do |form| %>
<%= form.label :title_includes %>
<%= form.text_field :title_includes %>
<%= form.label :created_on %>
<%= form.date_field :created_on %>
<%= form.label :only_published %>
<%= form.check_box :only_published %>
<%= form.submit %>
<% end %>
注意form_with
method: :get
參數。這是必需的。
表單欄位將填入來自@search
的同名條件輸入(或預設)值。可以使用適合類型的表單字段,例如date_field
用於類型:date
, check_box
用於類型:boolean
和:void
等。
預設情況下,表單將提交到與搜尋物件的model_class
對應的控制器的索引操作。例如, PostSearch.model_class
是Post
,因此具有PostSearch
實例的表單將提交到PostsController#index
。若要變更表單提交到的位置,請使用form_with
的:url
選項。
可以使用link_to_search
視圖幫助器方法呈現搜尋連結:
<%= link_to_search "Sort by title", @search.toggle_order(:title, :asc) %>
此連結將自動指向目前控制器和目前操作,以及來自給定搜尋對象的查詢參數。要連結到不同的控制器或操作,請傳遞選項雜湊來代替搜尋物件:
<%= link_to_search "Sort by title", { controller: "posts", action: "index",
search: @search.toggle_order(:title, :asc) } %>
link_to_search
幫助器也接受與 Rails 的link_to
幫助器相同的 HTML 選項:
<%= link_to_search "Sort by title", @search.toggle_order(:title, :asc),
id: "title-sort-link", class: "sort-link" %>
....以及內容區塊:
<%= link_to_search @search.toggle_order(:title, :asc) do %>
Sort by title <%= img_tag "sort_icon.png" %>
<% end %>
ModelSearch
輔助方法ModelSearch
類別提供了幾種在渲染視圖時很有幫助的方法。
其中一個方法是ModelSearch#toggle_order
,如前面的範例所示。請記住, toggle_order
是一種建構器樣式的方法,不會修改搜尋物件。相反,它會複製搜尋對象,並設定新對象的順序。這種行為適合產生指向多個搜尋變體的鏈接,例如表列標題中的排序連結。
ModelSearch#with
和ModelSearch#without
另外兩個建構器風格的方法是ModelSearch#with
和ModelSearch#without
。與toggle_order
一樣,這兩個方法都傳回一個新的搜尋對象,而不修改原始搜尋對象。 with
方法接受條件輸入值的雜湊值,以合併到原始條件輸入值集的頂部:
class PostSearch < TalentScout :: ModelSearch
criteria :title
criteria :published , :boolean
end
# The following are equivalent:
PostSearch . new ( title : "Maaaaath!" , published : true )
PostSearch . new ( title : "Maaaaath!" ) . with ( published : true )
PostSearch . new ( title : "Math?" ) . with ( title : "Maaaaath!" , published : true )
without
方法接受要排除的條件輸入值清單(預設條件值仍然適用):
class PostSearch < TalentScout :: ModelSearch
criteria :title
criteria :published , :boolean , default : true
end
# The following are equivalent:
PostSearch . new ( title : "Maaaaath!" )
PostSearch . new ( title : "Maaaaath!" , published : false ) . without ( :published )
ModelSearch#each_choice
另一個有用的方法是ModelSearch#each_choice
,它將迭代給定條件的定義選擇。這可用於產生搜尋變體的連結:
class PostSearch < TalentScout :: ModelSearch
criteria :category , choices : %w[ Science Tech Engineering Math ]
end
<% @search.each_choice(:category) do |choice, chosen| %>
<%= link_to_search "Category: #{choice}", @search.with(category: choice),
class: ("active" if chosen) %>
<% end %>
請注意,如果傳遞給each_choice
區塊接受兩個參數,則第二個參數將指示目前是否選擇了該選項。
如果沒有區塊傳遞給each_choice
,它將傳回一個Enumerator
。這可用於產生選擇框的選項:
<%= form_with model: @search, local: true, method: :get do |form| %>
<%= form.select :category, @search.each_choice(:category) %>
<%= form.submit %>
<% end %>
each_choice
方法也可以透過:order
呼叫。這樣做將迭代每個定義順序的每個方向,產生包括方向後綴的適當標籤:
class PostSearch < TalentScout :: ModelSearch
order "Title" , [ :title ] , asc_suffix : " (A-Z)" , desc_suffix : " (Z-A)"
order "Time" , [ :created_at ] , asc_suffix : " (oldest first)" , desc_suffix : " (newest first)"
end
<%= form_with model: @search, local: true, method: :get do |form| %>
<%= form.select :order, @search.each_choice(:order) %>
<%= form.submit %>
<% end %>
上面表格中的選擇框會列出四個選項:「標題(AZ)」、「標題(ZA)」、「時間(最舊的在前)」、「時間(最新的在前)」。
ModelSearch#order_directions
最後, ModelSearch#order_directions
輔助方法傳回一個HashWithIndifferentAccess
反映每個定義的訂單目前應用的方向。它包含每個定義的順序的鍵,並將每個鍵與:asc
、 :desc
或nil
關聯。
class PostSearch < TalentScout :: ModelSearch
order "Title" , [ :title ]
order "Time" , [ :created_at ]
end
< thead >
< tr >
<% @search . order_directions . each do | order , direction | %>
< th >
<%= link_to_search order , @search . toggle_order ( order ) %>
<%= img_tag " #{ direction || "unsorted" } _icon.png" %>
</ th >
<% end %>
</ tr >
</ thead >
請記住,一次只能應用一個訂單,因此哈希中最多只有一個值nil
。
將 gem 新增到您的 Gemfile 中:
$ bundle add talent_scout
並運行安裝生成器:
$ rails generate talent_scout:install
運行bin/test
來運行測試。
麻省理工學院許可證