はじめに
開発ですぐに忘れそうなことを、簡単に再度調べられるようにするための備忘録です。
やりたいこと
検索機能を簡単に実装できるransackを用いて、
複数のワードを空白区切りで入力された時の検索と
カラムを跨いだフリーワードでの検索を実装したい。
事前知識
例えば、Blogモデルにransackを用いて検索機能を実装する流れとして、
- カラム名_eq, カラム名_contなどのパラメーターを渡す(
params[:q][:title_cont]
など ) -
Blog.ransack(params[:q])
で検索 - 検索で得られたオブジェクトに対して
result
を用いて、ActiveRecord_Relationに変換する
のような流れで実装していく。
その他、細かな点はソースコードやピカワカさんのサイトが参考になりそう。
実装の流れ
- 入力された文字列を空白で区切る
- 区切った単語と検索したいパラメーターをハッシュに格納
- 得られたハッシュをransackに渡す
を新しい検索メソッドとして、実装していく。
また、検索のロジックになってくるため、model側にscopeメソッドとして実装する。
scope :multi_search, -> (freeword) {
words = freeword.scan(/[[:^space:]]+/)
# ※1
g = words.map do |word|
{
x1_eq: word,
x2_cont: word,
x3_cont: word,
m: "or"
}
end
# ※2
ransack({ m: 'and', g: g, s: 'xxx desc' })
}
※1:x1~x3は検索したいカラムを指定する
※2:cominatorでandかor検索かを指定できる。また、sのバリューにソートしたい条件を渡すことで、出力のソート条件も指定ができる。
上記で複数ワードの検索が実装できる。
※2021年10月6日、コメントいただいた書き方で、※1、 2をそれぞれ修正いたしました。
最後に、ransackのparams[:q]
には、デフォルトでは、カラム名_contなどしか渡せないので、ransackable_scopes
に好きなワードを設定する。
def self.ransackable_scopes(auth_object = nil)
%i[freeword]
end
これで複数ワードで検索する準備ができたので、
ビュー側で、
<%= search_form_for @q, url: search_machines_path do |f| %>
<%= f.search_field :freeword %>
<%= f.submit %>
<% end %>
コントローラー側で、
@search = Blog.multi_search(params[:q])
@list = @search.result(distinct: true)
とすれば、複数ワード、フリーワードでの検索機能が実装できる。