はじめに
Ransack は人気のある gem で、Rails + Ransack + RDB ですぐに検索システムが実装できる。
データソースに PostgreSQL を利用する場合、LIKE 句での検索ができない。LIKE 句が書かれて欲しいケースでも、ILIKE が書かれてしまう。これでは困る。
Rails + Ransack + PostgreSQL の組み合わせで LIKE が発行されるようにしたい。
同じ問題はすでに Ransack の github でも話題になっている。(そして解決方法も書かれている。)
How to use LIKE instead ILIKE on PostgreSQL? · Issue #699 · activerecord-hackery/ransack · GitHub
いろいろ試して解決できたので、紹介します。
まとめ
下記のコメントのようにモンキーパッチを適用して Arel に新しい Predications を追加する。 それを ransack で利用すれば良い。
How to use LIKE instead ILIKE on PostgreSQL? · Issue #699 · activerecord-hackery/ransack · GitHub
解説
Ransack + PostgreSQL では LIKE を書けない
まず、Ransack + PostgreSQL の組み合わせで LIKE を書く機能は標準では存在しない。
例えば name
というカラムについて LIKE を使いたい場合は name_cont
でリクエストするが name_cont
でも name_i_cont
でも ILIKE が書かれる。これは spec でも期待値として書かれている。
そのため、自分で predicate を追加する必要がある。
↓のような感じ
Ransack.configure do |config| config.add_predicate 'custom_cont', arel_predicate: 'matches', formatter: proc { |v| "%#{v}%" }, validator: proc { |v| v.present? }, type: :string, case_insensitive: false end
これで上手くいきそうな気がするのだが、この predicate を使っても ILIKE になってしまう。
case_insensitive
を true
にしても false
にしても ILIKE になる。どうやら case_insensitive
では LIKE か ILIKE かの選択はできないみたい。(このオプションの本来の用途はちゃんと見ていない)
Arel に新しい Predication を追加する
Ransack で cont
を指定すると Arel の matches
が利用されるのだが、この matches は LIKE/ILIKE の選択に対応している。
matches の定義
LIKE/ILIKE の選択
どうやら matches の引数 case_sensitive
に Ransack の predicate の case_insensitive
は反映されない。
そこで、デフォルトで case_sensitive
が true な matches をモンキーパッチで追加する。
↓のような感じ
module Arel module Predications # オプションcase_sensitiveをデフォルトでtrueにする(本来のmatchesはfalse) def s_matches(other, escape = nil, case_sensitive = true) Nodes::Matches.new self, quoted_node(other), escape, case_sensitive end end end
追加した Predication を Ransack から使う
上で Arel に追加した Predication を Ransack から使えるように add_predicate する。
Ransack.configure do |config| config.add_predicate 's_cont', arel_predicate: 's_matches', # ここに追加したパッチで追加したPreficationを指定 formatter: proc { |v| "%#{v}%" }, validator: proc { |v| v.present? }, type: :string, case_insensitive: false end
これで PostgreSQL を利用している場合でも LIKE で検索ができる。
感想
今回は Arel のモンキーパッチを書いて強制的に case_sensitive
を true にした。本来は Arel のモンキーパッチを書くのは避けたいので、Ransack 側から case_sensitive
に渡す値を操作できるとベストなのだが、これは実装できなかった。(ちゃんと Ransack のコードを読めばできる気はする)
もっとスマートにできるよ!という方がいたら教えてください。よろしくお願いします。
ひとまず LIKE で検索できるようになったので良かったです。
ILIKE/LIKE の区別はけっこう大事な気がするので、コントリビュートチャンスかも知れない。