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 句、明示的なクエリ ブロック、またはモデル スコープ参照の 3 つの方法のいずれかで指定できます。たとえば、次の 3 つの: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
明示的なクエリ ブロックは、Active Record scope
ブロックと同様に、モデルのActiveRecord::Relation
のコンテキストで評価されることに注意してください。
条件定義ではデータ型を指定できます。これにより、その入力値はクエリ ブロックまたはスコープに渡される前に型キャストされます。例として:
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
と同様に型キャストしますが、型キャストされた値が falsey の場合には条件が適用されなくなります。例えば:
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
クエリ ブロックに渡される値は、配列内の値の 1 つ、またはハッシュ内の値の 1 つになります。検索オブジェクトは、配列値、ハッシュ キー、ハッシュ値のいずれかを使用して構築できます。
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 )
一度に適用できる注文は 1 つだけですが、1 つの注文に複数の列を含めることができます。
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"
です。これらは、I18n との親和性のために選ばれました。これらは順序定義の一部としてオーバーライドできます。
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
指定することによって昇順または降順のいずれかにできることに注意してください。また、一度に適用できる注文は 1 つだけであるのと同様、デフォルトに指定できる注文は 1 つだけです。
デフォルトの検索スコープは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 %>
method: :get
form_with
の引数に注目してください。これは必須です。
フォームフィールドには、 @search
からの同じ名前の条件入力 (またはデフォルト) 値が入力されます。タイプに適したフォームフィールドを使用できます。たとえば、 :date
タイプのdate_field
、 :boolean
および:void
タイプのcheck_box
などです。
デフォルトでは、フォームは検索オブジェクトの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
クラスは、ビューをレンダリングするときに役立つメソッドをいくつか提供します。
そのようなメソッドの 1 つは、前の例で示したModelSearch#toggle_order
です。 toggle_order
、検索オブジェクトを変更しないビルダー スタイルのメソッドであることに注意してください。代わりに、検索オブジェクトを複製し、新しいオブジェクトに順序を設定します。このような動作は、テーブル列ヘッダーのソート リンクなど、検索の複数のバリエーションへのリンクを生成するのに適しています。
ModelSearch#with
とModelSearch#without
追加の 2 つのビルダー スタイル メソッドは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
もう 1 つの便利なメソッドは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
に渡されるブロックが 2 つの引数を受け入れる場合、2 番目の引数はその選択肢が現在選択されているかどうかを示すことに注意してください。
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)」、「時刻(古い順)」、「時刻(新しい順)」の4つの選択肢が表示されます。
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 >
一度に適用できる順序は 1 つだけなので、ハッシュ内の非nil
になる値は最大でも 1 つだけであることに注意してください。
gem を Gemfile に追加します。
$ bundle add talent_scout
そして、インストール ジェネレーターを実行します。
$ rails generate talent_scout:install
bin/test
実行してテストを実行します。
MITライセンス