Edited at

ransackを使った複数ワードによるAND検索の実装

More than 3 years have passed since last update.

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の文字列がいずれも含まれる"という条件を満たすモデルを絞り込むことができる。