Ransack で ActiveRecord の Scope を利用して検索を行う方法

#はじめに

全国津々浦々の Ransacker の皆様、今日も元気に [Ransack](https://github.com/activerecord-hackery/ransack) 使ってますか？
[Ransack](https://github.com/activerecord-hackery/ransack) は DB に対する検索を実装する上ではお手軽に使えるので利用されてる方も多いと思います。
ただ、[Ransack](https://github.com/activerecord-hackery/ransack) を使ってて全文検索までとはいかずとも「もうちょっと込み入った検索もしたいな・・・」って思った時に痒いところに手が届かずに悶絶していませんか？
ここでは、そんな時に便利な [ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) の [Scope](http://guides.rubyonrails.org/active_record_querying.html#scopes) を利用した検索の実現方法を紹介します。

#Ransack における検索

[Ransack](https://github.com/activerecord-hackery/ransack) では基本的に2種類の検索方法が準備されています。
一つは「[Simple Mode](https://github.com/activerecord-hackery/ransack#simple-mode)」で、もう一つは「[Advanced Mode](https://github.com/activerecord-hackery/ransack#advanced-mode)」です。

## Simple Mode
[Simple Mode](https://github.com/activerecord-hackery/ransack#simple-mode) では、```Predicates``` と呼ばれる検索用の述語を指定することで簡易な検索を行います。
例えば ```users``` テーブルの ```name``` カラムに指定した文字が含まれている事、といった検索をしたい場合は ```User.search(name_cont: 'ruzia').result``` といった形になります。
条件は ```or``` や ```and``` などで連結することも可能なので、簡単な検索であれば十分使えます。

```Predicates``` の詳しい書き方は以下のURLを参照して下さい。
https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching

## Advanced Mode
[Advanced Mode](https://github.com/activerecord-hackery/ransack#advanced-mode) では、```Condition Groups``` と呼ばれる検索条件のグループを複数組み合わせる事で複雑な検索を行います。
```Condition Groups``` はネストする事もでき、相当複雑なクエリの組み立てが行えます。

とは言え言葉だけではイメージが湧かないと思うので、詳しくは以下の URL を参照して下さい。
http://ransack-demo.herokuapp.com/users/advanced_search
触ってみればわかると思いますが、相当複雑な条件を指定できます。

#帯に短し襷に長し
というわけで [Ransack](https://github.com/activerecord-hackery/ransack) には 2 つの検索方法があるわけですが、絶妙に痒いところに届かないっ・・・！
実際にいろいろ作ろうとすると [Simple Mode](https://github.com/activerecord-hackery/ransack#simple-mode) よりもちょっと複雑な条件を指定したいけど、[Advanced Mode](https://github.com/activerecord-hackery/ransack#advanced-mode) ほど自由度が高く無くても良い、という状況によく陥ります。
そして大体脳裏に浮かぶのは「[Scope](http://guides.rubyonrails.org/active_record_querying.html#scopes) なら簡単に書けるのに・・・！」という台詞ではないでしょーか。

[Simple Mode](https://github.com/activerecord-hackery/ransack#simple-mode) だとやりたい事が実現できないけど、かといって [Advanced Mode](https://github.com/activerecord-hackery/ransack#advanced-mode) を使って ```Condition Groups``` を組み立てるのは~~めんどくさい~~気が進まない・・・

そんなあなたに、[ransackable_scopes](https://github.com/activerecord-hackery/ransack#using-scopesclass-methods)！！

#ransackable_scopes を利用した検索
[Ransack](https://github.com/activerecord-hackery/ransack) には [ransackable_scopes](https://github.com/activerecord-hackery/ransack#using-scopesclass-methods) という機能があります。
[ransackable_scopes](https://github.com/activerecord-hackery/ransack#using-scopesclass-methods) を一言で表現すると、「[Scope](http://guides.rubyonrails.org/active_record_querying.html#scopes) を利用した [Simple Mode](https://github.com/activerecord-hackery/ransack#simple-mode) による検索」です。

例えば、```name``` と ```address``` を持っている ```User``` がある際に「ruzia 東京」とかいった感じのフリーワード検索っぽいものをしたいとします。
その場合、例えば以下の様な形で実現できてしまいます。

~~~ruby
class User < ActiveRecord::Base
  # あくまでも例なので、DBの負荷とか考慮してないです
  scope :by_freeword, -> (freeword) {
    user_table = User.arel_table
    freeword_node = Arel::Nodes::InfixOperation.new('||', user_table[:name], user_table[:address]))
    result = all
    freeword.split(/ |　/).each do |term|
      result = result.where(freeword_node.matches(term))
    end
    result
  }

  def self.ransackable_scopes(auth_object = nil)
    %i[by_freeword]
  end
end
~~~

```Model``` を上記の様な形で実装しておくと

~~~ruby
User.search(by_freeword: 'ruzia 東京').result
~~~

という実装が可能になります。
つまり、[Simple Mode](https://github.com/activerecord-hackery/ransack#simple-mode) における ```Predicates``` として [Scope](http://guides.rubyonrails.org/active_record_querying.html#scopes) が利用できちゃうわけですね、すてき！

#まとめ
というわけで、実は [Ransack](https://github.com/activerecord-hackery/ransack) って [Scope](http://guides.rubyonrails.org/active_record_querying.html#scopes) を使った検索もできちゃうんだよ！という紹介でした。

実はこの機能、[Ransack](https://github.com/activerecord-hackery/ransack) の前身である [MetaSearch](https://github.com/activerecord-hackery/meta_search) には結構前からあったんですよね。
なかなか [Ransack](https://github.com/activerecord-hackery/ransack) に実装されなくて悶々としてたのですが、ちょっと前に以下の pull request でやっと取り込まれました！

https://github.com/activerecord-hackery/ransack/pull/390

[Ransack](https://github.com/activerecord-hackery/ransack) の ```sort_link``` などの便利な機能を使いつつも [Scope](http://guides.rubyonrails.org/active_record_querying.html#scopes) を利用した複雑な検索も実現できるため、全文検索ほどではない場合にとても便利だと思います。

まだ使った事が無い方は、ぜひお試しを！

## 参考情報
ransackable_scopes：https://github.com/activerecord-hackery/ransack#using-scopesclass-methods
ActiveRecordのScope：http://guides.rubyonrails.org/active_record_querying.html#scopes