はじめに
いよいよダットのターンです!たまにはRailsについて書かずHTMLかJavascriptばかりの記事を投稿したので今回はRailsのメソッドについて説明させていただきます。
開発者としてはこれらのメソッドを触ったことがあるはずですね。風通はこの三つの中に勝手に選んでどっちでも使って無事で計算されるので大丈夫と思いましたが、実はそうでしょうか?実際は場合によって返った結果も変わって実行する時間も異なる可能性があります。さっそくですが、始めましょう!
概要
この記事はこれらのメソッドがRubyに使われる時を気にしないでActiveRecordに使用する時だけ中心してください。結局、アソシエーションの中に幾つのレコードがあるか教えてくれますが、内部の処理がちょっと違います。
例えば、このモーデルとリレーションがあります。
class Post
has_many :comments, dependent: :destroy
end
class Post
belongs_to :post
end
post.comments.count
SQLのCOUNTのクエリを実行して結果からエレメントを数えます。このcount
の前に条件を絞ってサブセットのエレメント数を数えることもできます。
$ post.comments.count
(1.0ms) SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1
=> 3
$ post.comments.where(author: 'dat').count
(1.0ms) SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1 AND `comments`.`author` = `dat`
=> 1
尚、リレーションにcounter_cache: true
を使用したらcount
は新しいクエリを実行せず、すぐにキャッシュの値を返るようになります。
カウンターキャッシュはこの記事に書いてありませんがこれは参考になるかと思っています。
post.comments.length
この場合はアソシエーションのコンテンツをロードしてロードされたエレメントを数えます。このメソッドはアソシエーションが既にロードされたらもう一度クエリを実行しません。
$ post.comments.length # クエリが実行される
Comment Load (1.1ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` = 1
=> 5
$ post.comments.length # もう一度実行するとクエリが実行されなくてすぐに返す
=> 5
post.comment.size
これは以上の二つのメソッドを合わせるものです。もしコレクションが既にロードされたらlength
メソッドのようにすぐに結果を返します。逆だったらcount
メソッドのようにクエリを実行して結果を返します。ザッと見るとこのメソッドが全部自動的に適当な方法を選んで実行するのでもっとも良いメソッドかなと思う人もいますね。しかし、特別なケースによって意外と間違った結果を返す場合もあります。具体的な例を出そうと思ってます。
$ posts = Post.all
Post Load (1.7ms) SELECT `posts`.* FROM `posts`
$ posts.length
=> 3
$ posts.size
=> 3
$ post.count
(1.0ms) SELECT COUNT(*) FROM `posts`
=> 3
以上の例を見るとコレクションが既にロードされたらcount
は無駄なクエリを実行しまったとわかります。だが、ロードされたコレクションが変更したらどうなりますか。
$ posts = Post.all
Post Load (1.7ms) SELECT `posts`.* FROM `posts`
$ Post.last.destroy!
=> # 消されたエレメントを返す
$ posts.length
=> 3
$ posts.size
=> 3
$ post.count
(1.0ms) SELECT COUNT(*) FROM `posts`
=> 2
つまり、新しいクエリを実行しないと変更があるかどうか分からなくて間違った結果を返してしまいました。なので、この場合はcount
しか使えません。
結論(TLDR)
- クエリから取ったデータをもう使わなかったら、
count
を利用すると良い - クエリから取ったデータを既に使ったか使う可能性があったら、
length
を利用すると良い - Railsに任せたかったら
size
を利用することもできる。しかし、意外と間違った結果が返される可能性があるのでよく分析してからsize
を代わりに適当なcount
かlength
を使用したら良い
終わりに
これは自分が実装したときどっちの方がいいのか悩んで調べた内容からまとめておいたのです。具体的な説明とか他の例を見たい方は以下の記事でも参考になると思います。ここまで読んで頂いて感謝いたします。