目的
Railsデフォルトのorだと、何らかの動的な仕様の都合でRelation条件配列として保持している場合、ORを適用するのが大変
first_condition = or_conditions.shift
or_conditions.inject(first_scope) do |merged_scope, scope|
merged_scope.or(scope)
end
理想
このような形でサクッとORクエリーを作りたい
User.where.or(*or_conditions)
実装
Rails5版 OR クエリ構築するマン のRails6.1版(merge! → and!)
Rails6.1からmergeが厳密になり、統合方法の指定が必要になり汎用拡張には使えなくなった、そのため条件の追加である and! を使ってRelationにOR条件を追加する
# initialize/query_methods.rb 等で拡張する
ActiveRecord::QueryMethods::WhereChain.include(Module.new do
def or(first_scope, *scopes)
or_scope = scopes.inject(first_scope) do |relation, scope|
relation.or(scope)
end
@scope.and!(or_scope)
@scope
end
end)
結果
or_conditions = [User.where(id: 2), User.where(id: 3)]
User.where(id: 1).where.or(*or_conditions).where(id: 4).to_sql
=> "SELECT `users`.* FROM `users`
WHERE `users`.`id` = 1 AND (`users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 4"
補足
and!なので本体とOR条件のjoins条件が不一致だとエラーになります
デフォルトのOR挙動と同じなので特に問題ではありませんが、もし既にRails5版 OR クエリ構築するマン を利用されているものをRails6.1に上げる際にはjoinsを揃える訂正が必要になる可能性があります(不一致だとエラーが発生します)