ransackを使って複数語でのAND検索を実装しようと思ったら、意外と情報が少なかったのでメモ。
ransackを用いた検索機能の実装としては、以下のようなものが最低構成だと思う
class EventController
def index
keyword = params[:q] # params[:q] = 'hello'
@event = Event.ransack(title_cont: keyword).result
end
end
上記の例では、Eventモデルのtitleカラムに対して'hello'という文字列でLIKE検索を実行している。
これに対して、キーワードを複数用いてAND検索を行いたい場合は、ransackに以下のようにパラメータを渡す
def index
keyword = params[:q].split(' ') # params[:q] = 'hello world'
@event = Event.ransack({ combinator: 'and', groupings: { 'a' => {title_cont: keyword[0]}, 'b' => {title_cont: keyword[1]}} })
end
この例では、Eventモデルのtitleカラムに対して'hello'と'world'の二つの文字列でAND検索(LIKE)を実行している。ちなみに、groupings内の'a'や'b'の部分は、ユニークな値なら何でも構わない。
さらに、ソートの条件を指定する場合は以下のように行う
@event = Event.ransack({ combinator: 'and', groupings: { 'a' => {title_cont: keyword[0]}, 'b' => {title_cont: keyword[1]}}, s: 'title desc' })
これらを踏まえた、最低限のAND検索の実装が以下。
def index
key_words = params[:q].split(/[\p{blank}\s]+/) # params[:q] = 'hello world ruby'
grouping_hash = keywords.reduce({}){|hash, word| hash.merge(word => { title_cont: word })}
Event.ransack({ combinator: 'and', groupings: grouping_hash, s: 'title desc' }).result
end
params[:q]には半角スペースあるいは全角スペースで区切られた形で、複数の検索ワードが入っている(今回は'hello world ruby')。2行目では検索ワードを分割し、配列に格納している。3行目では、ransackに渡すgroupingsパラメータの中身を組み立てている。この例では、以下のようなハッシュが形成される
{
'hello' => {title_cont: 'hello'},
'world' => {title_cont: 'world'},
'ruby' => {title_cont: 'ruby'}
}
最終的に4行目ではransackメソッドにパラメータが渡され、"titleカラムにhello、world、rubyの文字列がいずれも含まれる"という条件を満たすモデルを絞り込むことができる。