はじめに
個人開発中のRailsアプリで、複数のワードによる検索機能を実装したので、その方法を共有します。
やることをより具体的に説明すると「ネコ カフェ」と検索をした時に、「ネコカフェ」のように「ネコ」と「カフェ」の両方のワードが含まれているモデルを取得する処理の実装になります。
なお、今回の記事は、Ransackが既に導入されていることを前提としています。
変更前と変更後のコード
以下が変更前と変更後のコードになります。
変更箇所はコントローラーのみでビューの変更は行っていません。
変更前のコードはransackのテンプレート的なものになるので説明は割愛して、次の章では変更後のコードがどのようなことをしているのかを解説していきたいと思います。
def index
@q = Shop.ransack(params[:q])
@shops = @q.result(distinct: true)
end
def index
key_words = params[:q][:name_cont].split(/[\p{blank}\s]+/)
grouping_hash = key_words.reduce({}) do |hash, word|
hash.merge(word => {name_cont: word})
end
@q = Shop.ransack({
combinator: "and",
groupings: grouping_hash
})
@shops = @q.result(distinct: true)
end
<div class="col-md-4">
<%= form.text_field 'q[name_cont]', value: params.dig(:q, :name_cont), class: "form-control", placeholder: "ショップ名で検索" %>
</div>
複数ワード検索の実装方法
先ほどもお見せした以下の変更後のコードを詳しく解説していきます。
def index
key_words = params[:q][:name_cont].split(/[\p{blank}\s]+/)
grouping_hash = key_words.reduce({}) do |hash, word|
hash.merge(word => {name_cont: word})
end
@q = Shop.ransack({
combinator: "and",
groupings: grouping_hash
})
@shops = @q.result(distinct: true)
end
1.スペース区切りで送られてきた検索ワードを分割
以下のコードでは送られてきた検索ワード(params[:q][:name_cont])をsplitメソッドで配列に変換しています。
splitメソッドの引数 /[\p{blank}\s]+/ は、空白文字で検索ワードを分割するための正規表現です。
具体的に説明すると「ネコ カフェ」と検索された時のkey_wordsの中身は["ネコ", "カフェ"]という配列になります。
key_words = params[:q][:name_cont].split(/[\p{blank}\s]+/)
2.ransackで検索を行えるようにハッシュ形式に変換
以下のコードでは1で作成した配列(key_words)をransackで複数ワード検索を行うためにハッシュ形式へ変換を行います。
grouping_hash = key_words.reduce({}) do |hash, word|
hash.merge(word => {name_cont: word})
end
具体的に先ほどのkey_wordsは以下のような形に変換されてgrouping_hashに代入されます。
grouping_hash = {
"ネコ" => { name_cont: "ネコ" },
"カフェ" => { name_cont: "カフェ" }
}
3.検索オブジェクトの作成
2で作成したgrouping_hashを用いてransackの検索オブジェクトの作成を行います。
「combinator: "and"」はand検索を行う設定でもし「combinator: "or"」の場合はor検索が行われます。
次に「groupings: grouping_hash」は検索条件をグループ分けすることで複数の条件で検索が行えるようにするための記述になります。
@q = Shop.ransack({
combinator: "and",
groupings: grouping_hash
})
上記のように記述することで以下のようなSQL文となり、「ネコ」と「カフェ」の両方を含んだshopを検索するための検索オブジェクトが作成されます。
SELECT * FROM shops
WHERE name LIKE '%ネコ%'
AND name LIKE '%カフェ%'
4.検索結果を取得する
最後に3で作成した検索オブジェクトを用いて条件に該当するshopの検索を行います。
@shops = @q.result(distinct: true)
まとめ
今回は、Ransackを使って複数ワードによるAND検索を行う方法について解説しました。
最後まで読んでいただきありがとうございます。
参考文献