一般的なRDBMSを使ったよくあるLIKEでの曖昧検索実装では、検索ボックスに「寿司 和食」と入力してもただの単語での曖昧検索だと、 当たり前なんだけど、キーワードのOR検索を実現することができない。 select * from contents where LIKE body '%寿司 和食%' となってしまって、空白を含めた1単語として認識されてしまい、思うような結果が得られないと思う。
実際に組み立てるクエリは…?
多分イマイチなんだろうけど、簡易的にOR検索を実現するにはこんな感じになると思う。
"SELECT "contents".* FROM "contents" WHERE (("title" LIKE '%寿司%' OR "title" LIKE '%和食%') OR ("body" LIKE '%寿司%' OR "body" LIKE '%和食%'))"
これで擬似的にキーワードのOR検索を実装することができた。精度もまぁ無いよりかはマシくらいなもんだろう。 これでOKな事例というのは、検索対象が小規模な場合で、設定できるキーワードに上限を一応設けたほうが良いかもしれない。 以上の注意点を踏まえた上で、Railsで簡単に上記SQLを作る方法に進んでみよう。
実際にRansackで雑に実装
もちろん複雑なことはやっていないので、自分でさっきのSQL文を組み立てて投げるのでも良いのだけど、 面倒だし、検索機能を実装する時によく使われているRansackで実装してみようと思う。
以下のようにかけばOK。
Content.search(title_or_body_cont_any: ['寿司', '和食']).result
それでは上記で実行されるSQLを見てみよう。
irb(main):007:0> Content.search(title_or_body_cont_any: ['寿司', '和食']).result.to_sql => "SELECT \"contents\".* FROM \"contents\" WHERE ((\"contents\".\"title\" LIKE '%寿司%' OR \"contents\".\"title\" LIKE '%和食%') OR (\"contents\".\"body\" LIKE '%寿司%' OR \"contents\".\"body\" LIKE '%和食%'))"
おお、大丈夫そうだ。 あとは、JOINでスペースを区切り文字にして、splitでキーワードの配列作ればOKな感じ。
# controller def index if params[:keyword] @contents = Content.search(title_or_body_cont_any: params[:keyword].split(' ')).result else @contents = Content.all end end
これで完成。 意外とシュッとできた。やっぱり小規模用途だとransackは便利だった。