where.notについて
ActiveRecordでは次のように、引数無しでwhere
を呼び出したあと、すぐさまwhere
に渡すのと同じような条件をnot
に渡すと、その条件に当てはまらないレコードを選択することが出来ます。
Post.where.not(author: author) # SELECT `posts`.* FROM `posts` WHERE (`authors`.`id` != 1)
この機能はRails 4でリリースされました。Rails 4がリリースされてから1年以上経ちましたが、ようやくRails 4.x系を使いはじめました。 備忘録的に調べたことをまとめておきます。
はじまり、消えたwhere.likeとwhere.not_like
現在のwhere.not
のAPIはJeremy Kemperさんのコメントの提案により、amatsudaさんにより実装されました。
このPull Requestでは同時にlike
、not_like
も導入されましたが後にwhere.likeとwhere.not_likeはロールバックされました。
このあたりはRuby on Your Rails 15p〜やCommit.where.not(liked_by: ‘DHH’).reject!あたりに詳しくまとまっていて大変参考になりました。
where.like
やwhere.not_like
はDHHのコメント1、DHHのコメント2を見る限り、納得のいく理由や名前がないかぎり復活することはなさそうですね。
Squeelの場合
Squeelを使うとActiveRecordのクエリをよりRubyishに書けます。
そのメンテナーのErnie Millerさんのブログで、Squeelがwhere.not
に対応したときの話が書かれています。(Ernie MillerさんはActiveRecord HackeryでActiveRecordをハックするようなgemをいくつもメンテナンスしています)
ActiveRecord.where.not(:sane => true) - Ernie Miller
TL;DR - ActiveRecord: This is yak country.
タイトルを訳すなら「ActiveRecordの正気じゃないところ」まとめは「ActiveRecord: ここはヤクの国」つまり、ヤクの毛を刈り続ける大地といったところです。
Rails 4で無引数でwhere
を呼び出した場合の動作が変更されたため動かなくなったSqueelのwhere { condition }
が動くように対応したときの話を書いています。
# このブログ記事は大変参考になるのですが全部ヤクする時間がないので要約すると # 次のようなシチュエーションで`Foo#do_something`を呼びたいという話です module Foo def do_something puts 'Foo#do_something' end end module Bar def do_something super puts 'Bar#do_something' end end class Baz include Foo include Bar end # Fooのメソッドを呼びたいがそのままだとBar#do_somethingが呼ばれてしまう Baz.new.do_something # Foo#do_somethingを使いたいが、Fooが既にBarより前に差し込まれているので # `extend`してもBarの位置を動かすことが出来ない Baz.new.extend(Foo).do_something # 別のモジュールで`include`してそのモジュールを差し込んでも動かすことが出来ない module Pow include Foo end Baz.new.extend(Pow).do_something # Fooモジュールから取り出したメソッドを # `define_method`を使ってPowモジュール自身に持たせることで解決 module Pow include Foo define_method :do_something, Foo.instance_method(:do_something) end Baz.new.extend(Pow).do_something
記事中でwhere.not
の対応のため、ActiveRecordのwhere.not
をwhere_not
に変更するプルリクエストを出したがcloseされたことに触れられています。
Remove WhereChain, and implement where_not. by ernie · Pull Request #9551 · rails/rails
そのプルリクエスト上のwhere.notの提案者Jeremy Kemperさんからのコメントを読むと以下のような理由で、where.not
が消えることはなさそうです。
- この
where
句の組み立て方のパターンは良い not
だけが入ったけど、将来のリリースで他の操作が入るかもしれない- サードパーティーのライブラリが
like
やregexp
やor
をカバーするための足がかりとなる
まとめない
where.not
便利。