検索語を複数対応した話
このアプリで聖書中の語句検索を行えるようにしていますが、元々は1語のみとしていました。今はスペース区切りで複数語句に対応しています。
このアプリの検索はgem ransackを使用しています。ransackを使用すると、検索条件をransackの命名規則に合わせる事で自動的に検索方法が設定されます。要は命名規則によって、吐き出すSQL文を調整するという動作をしています。
語句検索の場合、~を含むという検索方法にしたいので命名規則に合わせ~_contという名前を付けていました。これを~_cont_allにすれば、複数語句を含むに対応できます。
ここまでは、viewの話で簡単なのですが、control側(RailsはMVCモデルを採用しています。Mがmodel、Vがview、Cがcontrol)の対応が必要になります。語句をスペース区切りで切り分けるのは簡単にできます。ただ、単純にこれだけだと、ページ移動した際に障害が発生します。何故か?
ページ移動の様に見せているのは、DBの検索をページ分に区切って行っているから。という事になります。ネットで検索するとページ移動のない形で~cont_allの説明がされていて、ページ移動時の対応までは書かれていません。これは多分、このくらいは自分で考えなさい。わかるでしょ?っていう事なのかもしれませんが、初心者は戸惑うと思います。
ページ移動の再に不具合が発生するのは、先に書いたスペース区切りの処理のせいです。検索条件を最初に処理する時、スペースで区切られた一つの文字列をスペースで区切った複数の文字列に変換したものに置き換えているからです。これはransackが適切なSQL文を吐き出すために必須となります。この状態で、ページ移動しようとすると再度同じ条件で(切り出す範囲だけ異なる)検索を行う必要があるのに、検索条件の内容が(1文から複数文に)変わってしまっているので不具合となるわけです。なので、簡単に対応しようとすると、スペース区切りの処理する前を覚えておき、必要がなくなったら戻すという事をすれば良いのです。
具体的には、
if params[:q].present? # 検索条件が空でなかったら op = params[:q][:textsearch_cont_all] # 検索条件を1文の状態でopに保存 end textsearch = params.dig(:q, :textsearch_cont_all)&.split(/[[:space:]]/) # 検索条件をスペース毎に切り出し配列としてtextsearchに保存 params[:q][:textsearch_cont_all] = textsearch if textsearch # 検索条件をtextsearchに置き換える @search = Table.ransack(params[:q]) # 検索処理実行 if op.present? # 検索条件が空でなかったら params[:q][:textsearch_cont_all] = op # 検索条件を1文の状態(op)に戻す end @tables = @search.result.page(params[:page]) # ページ分の切り出しこんな感じです。