Posted at

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

More than 3 years have passed since last update.


はじめに

全国津々浦々の Ransacker の皆様、今日も元気に Ransack 使ってますか?

Ransack は DB に対する検索を実装する上ではお手軽に使えるので利用されてる方も多いと思います。

ただ、Ransack を使ってて全文検索までとはいかずとも「もうちょっと込み入った検索もしたいな・・・」って思った時に痒いところに手が届かずに悶絶していませんか?

ここでは、そんな時に便利な ActiveRecordScope を利用した検索の実現方法を紹介します。


Ransack における検索

Ransack では基本的に2種類の検索方法が準備されています。

一つは「Simple Mode」で、もう一つは「Advanced Mode」です。


Simple Mode

Simple Mode では、Predicates と呼ばれる検索用の述語を指定することで簡易な検索を行います。

例えば users テーブルの name カラムに指定した文字が含まれている事、といった検索をしたい場合は User.search(name_cont: 'ruzia').result といった形になります。

条件は orand などで連結することも可能なので、簡単な検索であれば十分使えます。

Predicates の詳しい書き方は以下のURLを参照して下さい。

https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching


Advanced Mode

Advanced Mode では、Condition Groups と呼ばれる検索条件のグループを複数組み合わせる事で複雑な検索を行います。

Condition Groups はネストする事もでき、相当複雑なクエリの組み立てが行えます。

とは言え言葉だけではイメージが湧かないと思うので、詳しくは以下の URL を参照して下さい。

http://ransack-demo.herokuapp.com/users/advanced_search

触ってみればわかると思いますが、相当複雑な条件を指定できます。


帯に短し襷に長し

というわけで Ransack には 2 つの検索方法があるわけですが、絶妙に痒いところに届かないっ・・・!

実際にいろいろ作ろうとすると Simple Mode よりもちょっと複雑な条件を指定したいけど、Advanced Mode ほど自由度が高く無くても良い、という状況によく陥ります。

そして大体脳裏に浮かぶのは「Scope なら簡単に書けるのに・・・!」という台詞ではないでしょーか。

Simple Mode だとやりたい事が実現できないけど、かといって Advanced Mode を使って Condition Groups を組み立てるのはめんどくさい気が進まない・・・

そんなあなたに、ransackable_scopes!!


ransackable_scopes を利用した検索

Ransack には ransackable_scopes という機能があります。

ransackable_scopes を一言で表現すると、「Scope を利用した Simple Mode による検索」です。

例えば、nameaddress を持っている User がある際に「ruzia 東京」とかいった感じのフリーワード検索っぽいものをしたいとします。

その場合、例えば以下の様な形で実現できてしまいます。

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 を上記の様な形で実装しておくと

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

という実装が可能になります。

つまり、Simple Mode における Predicates として Scope が利用できちゃうわけですね、すてき!


まとめ

というわけで、実は Ransack って Scope を使った検索もできちゃうんだよ!という紹介でした。

実はこの機能、Ransack の前身である MetaSearch には結構前からあったんですよね。

なかなか Ransack に実装されなくて悶々としてたのですが、ちょっと前に以下の pull request でやっと取り込まれました!

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

Ransacksort_link などの便利な機能を使いつつも Scope を利用した複雑な検索も実現できるため、全文検索ほどではない場合にとても便利だと思います。

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


参考情報

ransackable_scopes:https://github.com/activerecord-hackery/ransack#using-scopesclass-methods

ActiveRecordのScope:http://guides.rubyonrails.org/active_record_querying.html#scopes