Ransack は検索機能を簡単なソースコードで実装できる便利な Gem です。
また、Ransack には検索機能を拡張する方法も用意されているので、少し複雑な検索も実装することができます。
今回は年齢を検索する機能を作ってみました。
年齢を検索させる
年齢を検索させると言っても、DBに直接年齢が記録されているケースは少ないと思います。
普通に考えるとDBにはユーザーの生年月日を記録させて、データ取得時に今日の日付から年齢に変換しますよね。
なので、実装的には生年月日を入力してユーザーを検索させるフォームを用意する方がはるかに簡単ですが、
利便性を考えたら検索時にも年齢を入力したくなるはずです。
今回は Ransack の検索機能を拡張して、整数型で受け取った年齢をDate型に変換して検索させるようにしてみます。
結果からいうと以下のような実装になるかと思います。
Ransack での拡張
# 生年月日を年齢として検索する。
# xx歳未満
config.add_predicate 'to_age_lt',
arel_predicate: 'gteq',
formatter: -> (v) { (v.years.ago + 1.day).to_date },
type: :integer,
compounds: false
# xx歳以下
config.add_predicate 'to_age_lteq',
arel_predicate: 'gteq',
formatter: -> (v) { ((v + 1).years.ago + 1.day).to_date },
type: :integer,
compounds: false
# xx歳以上
config.add_predicate 'to_age_gteq',
arel_predicate: 'lteq',
formatter: -> (v) { v.years.ago.to_date },
type: :integer,
compounds: false
# xx歳超過
config.add_predicate 'to_age_gt',
arel_predicate: 'lteq',
formatter: -> (v) { (v + 1).years.ago.to_date },
type: :integer,
compounds: false
使い方
# 18歳以上のユーザーを取得する。
User.search(birthday_to_age_gteq: 18).result
ハマリポイントとしては、誕生日を迎える直前のユーザーの年齢を考慮する点でしょうか。
((v + 1).years.ago + 1.day)
という箇所がその対応となっています。
また、年齢としては「○○歳以上」、「○○歳以下」という表現ですが、
生年月日的には「年月日以前」、「年月日以降」という表現になるので、
大小比較の演算子が内部で入れ替わる点も考慮が必要です。