where.not を使っていて遭遇した問題についてrailsdmの懇親会で話していたら、@kamipo さんから「5.2では少し直っている」という情報を頂いた。
この話をしているときに思ったけど、今の where.not の問題点について知らない人も多そうなので、せっかくなのでブログにまとめてみた。
where.notの問題
where.not のドキュメントにいくつか例がある。
問題は複数の条件を引数で指定した場合。
User.where.not(name: "Jon", role: "admin") # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
これはド・モルガンの法則でORになりそうなのに、なっていない。
# ド・モルガンの法則 # NOT(A AND B) = NOT(A) OR NOT(B) # つまり、これは !(name == "Jon" && role == "admin") # こうなる name != "Jon" || role != "admin"
実際に遭遇したケース
ポリモーフィック関連を使っていて、 where.not と組み合わせた時に遭遇した。
# $ rails g model Picture imageable:references{polymorphic} # $ rails g model Employee # $ rails db:migrate:reset # $ rails c # Loading development environment (Rails 5.1.5) e = Employee.create #=> #<Employee id: 1, created_at: "2018-03-26 09:50:28", updated_at: "2018-03-26 09:50:28"> Picture.where.not(imageable: e).to_sql #=> SELECT "pictures".* FROM "pictures" WHERE ("pictures"."imageable_type" != 'Employee') AND ("pictures"."imageable_id" != 1)
5.2.0.rc2 の挙動
5.2.0.rc2 ではポリモーフィック関連の挙動が直っている。
# $ rails g model Picture imageable:references{polymorphic} # $ rails g model Employee # $ rails db:migrate:reset # $ rails c # Loading development environment (Rails 5.2.0.rc2) e = Employee.create #=> #<Employee id: 1, created_at: "2018-03-26 09:56:18", updated_at: "2018-03-26 09:56:18"> Picture.where.not(imageable: e).to_sql #=> SELECT "pictures".* FROM "pictures" WHERE NOT ("pictures"."imageable_type" = 'Employee' AND "pictures"."imageable_id" = 1)
そして、これが @kamipo さんの最高のコミット。
213796f のコミットでポリモーフィック関連の挙動が直った後、すかさずテストを追加している。*1
ポリモーフィック関連以外のケース
Issue は作られていて、議論されている。
個人的には 6.0 で複数条件のケースも直っていると嬉しい。
まとめ
where.not で ポリモーフィック関連 や 複数の条件 を渡すコードがあったら注意しましょう。
*1:テストのある機能は基本的に維持される