kikeda1104's blog

備忘録・技術に関することを書いています。(webエンジニア)

ActiveRecord::Relation#or

弊社の新規プロジェクトでは、Rails5が使われていてそのおかげで、orが利用できた。 今日は、その辺りをまとめてみる。

ActiveRecord::Relation#or

Rails 4系では、sqlorをアプリで実装しようとするとfind_by_sqlwherejoinsなどに直接sqlを書くか。Arelを利用して実装を進めていた。 Rails 5系からorが利用できるようになっているので、直接sqlを記述することなく、ActiveRecordの操作で完結できる。

# A || B

 Model.where(name: 'hoge').or(Model.(name: 'fuga'))

# A && B || C
 Model.where(name: 'hoge').where(age: 10).or(Model.where(name: 'fuga')) 

# A ‖ B && C
 Model.where(name: 'hoge').or(Model.where(age: 10)).where(name: 'fuga'))

という結果が返ってくる。

must be structurally compatible

構造互換性が必要なので、orに指定するActiveRecord::Relationとレシーバの構造は統一する。

互換性がなければ、下記のArgumentErrorが発生する。

ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:limit]

サンプル

Model.joins(:hoges)
     .merge(Model.where(name: nil)
     .or(Hoge.where(fuga: nil)
=> 
SELECT "model".* FROM "model" INNER JOIN "hoges" ON "hoges"."model_id" = "models"."id" WHERE ("models"."name" IS NULL OR "hoges"."fuga" IS NULL)

これで、andorの条件を切り換えられるロジックを書くことが容易になった。

参考

ActiveRecord::Relation

Rails 5 adds OR support in Active Record | BigBinary Blog

Rails 5: ActiveRecord OR query - Stack Overflow