【Rails】ransackを使って検索機能の実装

##検索機能の実装
今回はよくある検索バーの実装を行う。その際に``ransack``と言うgemを使う。
``ransack``が提供する検索フォームには``シンプルモード``と``アドバンスモード``がある。
``アドバンスモード``は入れ子になったAND/OR検索など、かなり複雑なことが実現できるが、今回は基本的な利用が目的なのでシンプルモードで実装を行う。

**手順**
①``ransack``のインストール
②コントローラーの編集
③viewの実装

##①``ransack``のインストール
Gemfileに以下のコードを記入後、``bundle install``。

```
gem 'ransack'
```

##②コントローラーの編集
掲示板に検索機能を追加する場合、以下のようにコントローラーの編集を行う。

```boards_controller.rb
#このよくある１文を下記2行に変換
@boards = Board.all.includes(:user).order(created_at: :desc).page(params[:page])

↓↓

def index
  @q = Board.ransack(params[:q])
  @boards = @q.result(distinct: true).includes(:user).page(params[:page]).order("created_at desc")
end
```

``params[:q]``には検索パラメータが渡され、``result``により検索結果を得られる。

``(distinct: true)``は記載しなくても使えるが、例えば絞り込み要件が「xxxというコメントがついている掲示板を取得する」場合に``distinct: true``がないと結果がおかしくなる。
掲示板Aに対して「ruby1」というコメントと「ruby2」というコメントがあったとし、この``(distinct: true)``を入れなかったとき、``ruby``でコメントを検索した場合に、掲示板Aが2回取得されて検索結果が2件になってしまうからである。

##③viewの実装
[![Image from Gyazo](https://i.gyazo.com/6da1bfadaad5a0f678ada1dcab2f0e04.png)](https://gyazo.com/6da1bfadaad5a0f678ada1dcab2f0e04)
検索フォームはよく使われるので、パーシャルテンプレートとして作成。
``search_form_for``という``ransack``が提供するフォームヘルパーを使ってフォームを作成。

```
#views/boards/_search.html.rb
<%= search_form_for @q, url: url do |f| %>
   <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: '検索ワード' %>
   <%= f.submit '検索', class: 'btn btn-primary' %>
<% end %>

#index.html.rb
<%= render 'search', q: @q, url: boards_path %>
```

・``url: url``と記入することで、どこからでも呼び出せる汎用性の高いパーシャルファイルになる。

例えば、すべての掲示板から検索したい場合は、``url: boards_path``と``render``に記載。ブックマークしている掲示板から検索したい場合は、``url: bookmarks_boards_path``と記載…と言う風になる。

・``title_or_body_cont``はそのカラムの**title**と**body**に検索した情報を含むレコード全てを取得する。

##ransackのよく使う述語（predicate）
述語（predicate）とは、先程``boards``の``titleとbody``を絞り込み検索する際に、``title_or_body_cont``と表記したが、この``_cont``の部分が述語（predicate）に該当する。複数検索を導入する際には``title_or_body_cont``にもある通り、``_or``を挟む。

*にはカラムを代入。


| 述語| 意味（英語） | 意味 |
|:-:|:-:|:-:|
|  *_eq |  equal | 等しい  |
| *_not_eq  |  not equal |  等しくない |
| *_lt  |   less than| より小さい  |
|  *_lteq | less than or equal  |  より小さい（等しいものも含む） |
|  *_gt | grater than  | より大きい  |
|  *_gteq | grater than or equal  | より大きい（等しいものも含む）  |
| *_cont  | contains value  | 部分一致（内容を含む）  |


##選択による検索
下の図のように性別をボタンで選択し検索もできる。
[![Image from Gyazo](https://i.gyazo.com/0e20d84c2dcc97301a3b4b6a07ca0f33.png)](https://gyazo.com/0e20d84c2dcc97301a3b4b6a07ca0f33)

```example.rb
<%= search_form_for @q do |f| %>
  <%= f.label :sex %>
  <%= f.radio_button :sex_eq, '', {:checked => true} %>指定なし
  <%= f.radio_button :sex_eq, 1 %>男性
  <%= f.radio_button :sex_eq, 2 %>女性
  <%= f.submit "検索" %>
<% end %>
```
``sex_eq``によって選んだものと数字の1,2が一致しているかどうかを検索。

##ソート機能
またransackには表示している情報を昇順降順で並び替える機能も備わっている。


```index.html.rb
<tr>
  <th>Name</th>
  <th>Age</th>
  <th>Sex</th>
  <th>Address</th>
</tr>

↓以下のように変換すると、ソート機能が使える

<tr>
  <th><%= sort_link(@q, :name, "Name") %></th>
  <th><%= sort_link(@q, :age, "Age") %></th>
  <th><%= sort_link(@q, :sex, "Sex") %></th>
  <th><%= sort_link(@q, :address, "Address") %></th>
</tr>
```

##参考記事
[【Rails】ransackの使い方をざっくりまとめてみた](https://zenn.dev/yukihaga/articles/c9dbabc220705f)
[Rubyon Rails で検索機能を作ろう（ransack）](https://qiita.com/nojinoji/items/e1b174220da8c81a1756)
[Ransackで簡単に検索フォームを作る73のレシピ](https://nekorails.hatenablog.com/entry/2017/05/31/173925#005-%E8%BF%B0%E8%AA%9E%E3%81%A8%E3%81%AF)