Rubyon Rails で検索機能を作ろう（ransack）

#はじめに
今回は`ransack`というgemを使って検索機能を作っていきます。[ransack](https://github.com/activerecord-hackery/ransack)は少ないコードで複雑な検索機能を簡単に作れるgemでとても便利です。また、`ransack`ではソート機能も簡単に作れるのでそれも含めて作成していきます。
#準備
##バージョン等
・Rails 5.2.2
・ruby 2.5.0

##アプリケーションの作成
検索機能を作る前にアプリケーションを作成します。（もし作成中のサービスに検索機能を追加する方はここは無視して大丈夫です！！）

```terminal:ターミナル
$ rails new search_sample
$ cd search_sample
```

Gemfileに`ransack`を追加し、`bundle install`します

```ruby:Gemfile
gem 'ransack'
```
```terminal:ターミナル
$ bundle install
```

今回は`scaffold`を利用してUser作成に関するアプリケーションの雛形を作成します。カラムは以下のとおりです

|User   |
|:---   |
|name   |
|age    |
|sex    |
|address|

```terminal:ターミナル
$ rails g scaffold user name age:integer sex:integer address
$ rails db:migrate
```

#検索機能の追加
これから検索機能を作成していきます。目標は下のような検索フォームの作成です。

<img width="500" alt="スクリーンショット 2019-03-19 19.00.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/225909/5f486257-97a2-2c29-9cc0-e53c3c0e0767.png">
名前と住所に関してはキーワードから検索できるようにします。
また、年齢は数値を入力して、〇〇以上と〇〇未満から検索、性別に関しては選択形式で検索できるようにします。

`ransack` では様々な方法によって検索することができます。例は下のとおりです。これらはほんの一部です。
`*`にはカラム名が入ります。

| 検索方法 |意味(英語)|意味|
|:---|:--|:--|
|`*_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|部分一致（内容を含む）|

コントローラーの内容を変更していきます。`users_controller.rb`の`index`アクションを次のように変更してください。

```app/controllers/users_controller.rb
#省略

def index
  @q = User.ransack(params[:q])
  @users = @q.result(distinct: true)
end

#省略
```

シードデータを作成します。
`seeds.rb`に以下を追加しましょう。

```db/seeds.rb
User.create(name: 'けんたろう',age: '24', sex: '1', address: '新宿')
User.create(name: 'けんじ',age: '25', sex: '1', address: '渋谷')
User.create(name: 'かな',age: '21', sex: '2', address: '原宿')
User.create(name: 'みき',age: '17', sex: '2', address: '新宿')
User.create(name: 'かんな',age: '22', sex: '2', address: '池袋')
User.create(name: 'あきお',age: '13', sex: '1', address: '上野')

```

下のコマンドを入力します。

```terminal:ターミナル
$ rails db:seed
```

##検索方法
###キーワード検索
下の図のように入力したキーワードと名前の部分一致から検索できるようにします。
<img width="141" alt="スクリーンショット 2019-03-19 19.21.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/225909/3b589f99-e94b-ca9a-3b2e-99b9a5e171a0.png">

```erb:app/views/index.html.erb
<%= search_form_for @q do |f| %>
  <%= f.label :name, "Keyword" %>
  <%= f.search_field :name_cont %>

  <%= f.submit "検索" %>
<% end %>

<!-- 省略 -->
```

`<%= search_form_for @q do |f| %>`〜`<% end %>`で囲った部分が検索フォームの部分になります。

`name_cont`は`name`カラムの単語の一部分を含むものを検索するという意味になります。

###数値の大小による検索
下の図のように年齢を入力した数字の大小で検索できるようにします。今回は〇〇以上〇〇未満といった形で検索できるようにします。
<img width="335" alt="スクリーンショット 2019-03-19 19.22.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/225909/f3ebb39a-98b1-8008-d9f4-1446831316ee.png">

```erb:app/views/index.html.erb
<%= search_form_for @q do |f| %>
  <%= f.label :name, "Keyword" %>
  <%= f.search_field :name_cont %>

  <!--ここから追加-->
  <%= f.label :age %>
  <%= f.number_field :age_gteq %>以上〜
  <%= f.number_field :age_lt %>未満
  <!--ここまで追加-->

  <%= f.submit "検索" %>
<% end %>

<!-- 省略 -->
```

`age_gteq`によって入力した数字以上の`age`を、`age_lt`によって入力した数字未満の`age`を検索するという意味になります。

###選択による検索
下の図のように性別をラジオボタンで選択し検索できるようにします。
<img width="231" alt="スクリーンショット 2019-03-19 19.23.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/225909/9f32615d-1bd2-a0a2-60d1-ef044ea464e4.png">


```erb:app/views/index.html.erb
<%= search_form_for @q do |f| %>
  <%= f.label :name, "Keyword" %>
  <%= f.search_field :name_cont %>
  
  <%= f.label :age %>
  <%= f.number_field :age_gteq %>以上〜
  <%= f.number_field :age_lt %>未満

  <!--ここから追加-->
  <%= 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が一致しているかどうかを検索します。また、`*_eq`は完全一致の検索であるので、キーワード検索などにも利用できます。


###複数カラムでのキーワード検索
さきほど作ったキーワード検索で、名前だけでなく住所からも検索できるようにします。

```erb:app/views/index.html.erb
<%= search_form_for @q do |f| %>

  <!--ここから変更-->
  <%= f.label :name_or_address, "Keyword" %>
  <%= f.search_field :name_or_address_cont %>
  <!--ここまで変更-->

  <%= f.label :age %>
  <%= f.number_field :age_gteq %>以上〜
  <%= f.number_field :age_lt %>未満

  <%= 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 %>

<!-- 省略 -->
```
##検索機能完成
検索機能が完成しました！！！！！
![search_search.gif](https://qiita-image-store.s3.amazonaws.com/0/225909/58aedec0-4fd0-d0a7-4151-502e3add1741.gif)


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


`index.html.erb`の以下の部分を変更します。

```erb:app/views/index.html.erb(変更前)
<!--省略-->
<tr>
  <th>Name</th>
  <th>Age</th>
  <th>Sex</th>
  <th>Address</th>
  <th colspan="3"></th>
</tr>
<!--省略-->
```
上のコードを以下のように変更してください。

```erb:app/views/index.html.erb(変更後)
<!--省略-->
<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>
  <th colspan="3"></th>
</tr>
<!--省略-->
```
##ソート機能完成
ソート機能が完成しました！！！！！！
![search_sort.gif](https://qiita-image-store.s3.amazonaws.com/0/225909/45940e3a-6f29-b78b-9e28-c293ec80d680.gif)


#終わりに
`ransack`によって検索機能とソート機能を作成しました。
もしこれより良い実装方法や記事内のミスなどありましたらご指摘していただけるとありがたいです。