Arelを使って OR の LIKE 検索って冗長になりがちだけどそこそこパターン化出来そうだなーとつらつらとコード書いてみた。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# こんなテーブルがあるとします | |
create_table :users do |t| | |
t.string :name | |
t.string :email | |
end | |
create_table :belongings do |t| | |
t.integer :user_id | |
t.string :company_name | |
t.string :dept_name | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 1つのキーワードであらゆる属性をあいまい検索したい場合、Arelを使えばこう書けます | |
# left join も Arel で書けますが、冗長なので eager_load で代用してます | |
match_key = "%#{keyword}%" | |
name_matches = User.arel_table[:name].matches(match_key) | |
email_matches = User.arel_table[:email].matches(match_key) | |
company_name_matches = Belonging.arel_table[:company_name].matches(match_key) | |
dept_name_matches = Belonging.arel_table[:dept_name].matches(match_key) | |
User.eager_load(:belonging).where(name_matches.or(email_matches).or(company_name_matches).or(dept_name_matches)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# or のメソッドチェーンがなんかやだね。こうすれば対応カラムが増えた時 matchers の要素を増やすだけで済むぞ | |
match_key = "%#{keyword}%" | |
matchers = [User.arel_table[:name].matches(match_key), | |
User.arel_table[:email].matches(match_key), | |
Belonging.arel_table[:company_name].matches(match_key), | |
Belonging.arel_table[:dept_name].matches(match_key)] | |
condition = matchers.inject { |cond, matcher| cond.or(matcher) } | |
User.eager_load(:belonging).where(condition) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# これならシンプルに inject(&:sym) でいけるね | |
match_key = "%#{keyword}%" | |
matchers = [User.arel_table[:name].matches(match_key), | |
User.arel_table[:email].matches(match_key), | |
Belonging.arel_table[:company_name].matches(match_key), | |
Belonging.arel_table[:dept_name].matches(match_key)] | |
User.eager_load(:belonging).where(matchers.inject(&:or)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# matchers も定型的なので冗長だよね | |
class_columns_set = { User => [:name, :email], Belonging => [:company_name, :dept_name] } | |
matchers = class_columns_set.map do |cls, cols| | |
cols.map { |col| cls.arel_table[col].matches("%#{keyword}%") } | |
end.flatten | |
User.eager_load(:belonging).where(matchers.inject(&:or)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# なんかシンプルなパターンなら切り出せそう | |
class SimpleFinder | |
def initialize(base_class, class_columns_set) | |
@base_class = base_class | |
@class_columns_set = class_columns_set | |
end | |
def find(keyword) | |
matchers = @class_columns_set.map do |cls, cols| | |
cols.map { |col| cls.arel_table[col].matches("%#{keyword}%") } | |
end.flatten | |
foreign_tables = @class_columns_set.keys.reject { |cls| cls == @base_class }.map(&:table_name) | |
@base_class.eager_load(*foreign_tables).where(matchers.inject(&:or)) | |
end | |
end | |
SimpleFinder.new(User, User => [:name, :email], Belonging => [:company_name, :dept_name]).find('foobar') |
人によっては 03_inject_with_symbol ぐらいが一番可読性がいいって意見もありそう。
Arelを使って OR や LIKE をするメリットは scope にして merge した時に壊れないって記述をよく見ますが、こういう風に動的に対応箇所を増やせるように持っていくのも楽というのもメリットですね。文字列で where 内を書いていたらなかなかこうはできない
あ、こんな処理を他にもたくさん書かなければいけない場合は Squeel 入れたほうがいいと思います。
全然関係ないけど、gist って編集時はインスタンス変数に色つけてくれるのに閲覧時には色つけてくれないの何でだろ
[追記] たまたま Ruby on Rails Advent Calendar 2015 が空いてたので飛び入りしました。
0 件のコメント :
コメントを投稿