普段はpryを使っているので全然気づかなかったのですが、とある時にirbを使うと謎にlimit 11
がついてしまう事象に出会いました。
LIMIT 11
ってなんやねん!
気になって仕方ないので、雑にぐぐったりしたのですがわからず、、、うーん気になる。
どうしても気になるので調べたところ、LIMIT 11
がつく理由がわかったので記事にしてみました。
事象の確認
このようなモデルがあるとします。
class User < ApplicationRecord
has_many :reviews
end
class Review < ApplicationRecord
belongs_to :user
end
userの持っているreviewsを取得する処理をirbで実行してみます。
irb(main):006:0> user = User.find 1
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, name: "1234567890", created_at: "2019-12-12 05:43:52", updated_at: "2019-12-12 05:43:52">
irb(main):007:0> user.reviews
Review Load (1.0ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1 LIMIT 11
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, content: "hoge", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:27:55", updated_at: "2020-06-11 05:27:55">, #<Review id: 2, content: "fuga", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:12", updated_at: "2020-06-11 05:45:12">, #<Review id: 3, content: "fuga1", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:14", updated_at: "2020-06-11 05:45:14">, #<Review id: 4, content: "fuga12", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:15", updated_at: "2020-06-11 05:45:15">, #<Review id: 5, content: "fuga123", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:17", updated_at: "2020-06-11 05:45:17">, #<Review id: 6, content: "fuga1234]", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:18", updated_at: "2020-06-11 05:45:18">, #<Review id: 7, content: "fuga12345", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:20", updated_at: "2020-06-11 05:45:20">, #<Review id: 8, content: "fuga123456", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:22", updated_at: "2020-06-11 05:45:22">, #<Review id: 9, content: "fuga1234567", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:24", updated_at: "2020-06-11 05:45:24">, #<Review id: 10, content: "fuga12345678", user_id: 1, book_id: 0, status: "draft", created_at: "2020-06-11 05:45:27", updated_at: "2020-06-11 05:45:27">, ...]>
irb(main):008:0>
LIMIT 11
がついてますね!どこのことかわかりましたか?
user.reviews
で発行されるクエリーに注目してください。↓に当該部分の画像を貼っておきます。
なんで何もしてないのにLIMIT 11
やねん。ここから原因調査の旅が始まります。
RailsのリポジトリからLIMIT 11
を探した件
十中八九Railsが何かやっているのだろうと思ったのでGithubのRailsリポジトリで愚直にLIMIT 11
で検索してみました。
35件見つかりました。目視でいけるレベルなのでざっとみてみると、、、
該当のメソッドを転記しました(Githubへのリンクはこちら)
def inspect
subject = loaded? ? records : self
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
entries[10] = "..." if entries.size == 11
"#<#{self.class.name} [#{entries.join(', ')}]>"
end
メソッドの名前でピンとくると思いますが、inspect
を使った時にLIMIT 11
を付加しているようです。
処理をみるとデータ自体は10個返却して、11個目は"..."に変えているようですね。
ちなみにinspect
を知らない方はこちらを参照
オブジェクトを人間が読める形式に変換した文字列を返します。
というデバッグ時によく使うメソッドです。
使い慣れているpryでもinspect
をつけるとLIMIT 11
が発行されることがわかりました。
[12] pry(main)> user = User.find 1
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x00005599999f28a0
id: 1,
name: "1234567890",
created_at: Thu, 12 Dec 2019 05:43:52 UTC +00:00,
updated_at: Thu, 12 Dec 2019 05:43:52 UTC +00:00>
[13] pry(main)> user.reviews.inspect
Review Load (0.6ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1 LIMIT 11
=> "#<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, content: \"hoge\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:27:55\", updated_at: \"2020-06-11 05:27:55\">, #<Review id: 2, content: \"fuga\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:12\", updated_at: \"2020-06-11 05:45:12\">, #<Review id: 3, content: \"fuga1\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:14\", updated_at: \"2020-06-11 05:45:14\">, #<Review id: 4, content: \"fuga12\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:15\", updated_at: \"2020-06-11 05:45:15\">, #<Review id: 5, content: \"fuga123\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:17\", updated_at: \"2020-06-11 05:45:17\">, #<Review id: 6, content: \"fuga1234]\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:18\", updated_at: \"2020-06-11 05:45:18\">, #<Review id: 7, content: \"fuga12345\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:20\", updated_at: \"2020-06-11 05:45:20\">, #<Review id: 8, content: \"fuga123456\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:22\", updated_at: \"2020-06-11 05:45:22\">, #<Review id: 9, content: \"fuga1234567\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:24\", updated_at: \"2020-06-11 05:45:24\">, #<Review id: 10, content: \"fuga12345678\", user_id: 1, book_id: 0, status: \"draft\", created_at: \"2020-06-11 05:45:27\", updated_at: \"2020-06-11 05:45:27\">, ...]>"
[14] pry(main)>
繰り返しになりますが、inspect
がやってくれることは「オブジェクトを人間が読める形式に変換した文字列を返します。」です。
確かに大量にデータが出力されても目視じゃ読む気がなくなるのでキリよく最大10個表示するように制御してくれているのですね!
ここまででそれっぽい処理が見つかりましたが、まだ解決ではありません。
なぜなら、irbではinspect
をつけていないのになぜかLIMIT 11
がついているのです。
irbでLIMIT 11
される件
次にirbでコマンドを実行した時の出力結果に着目してみます。
LIMIT 11
された時の11個目のデータの表示が"..."に変換されています。
これはもう完全に先ほど調べたinspect
の動作そのものですね。
結果出力にInspectを用いるだと!
早速クリックしてみると、下記のように書かれていました。
https://docs.ruby-lang.org/ja/latest/library/irb.html#inspect_mode
irb のプロンプト中では conf.inspect_mode で、.irbrc 中では IRB.conf[:INSPECT_MODE] に以下のいずれかの値を設定する事で、結果 出力の方式を変更する事ができます。
false, :to_s, :raw
出力結果を to_s したものを表示します。
true, :p, :inspect
出力結果を inspect したものを表示します。
:pp, :pretty_inspect
出力結果を pretty_inspect したものを表示します。
:yaml, :YAML
出力結果を YAML 形式にしたものを表示します。
:marshal, :Marshal, :MARSHAL, Marshal
出力結果を Marshal.#dump したものを表示します。
早速デフォルト値を調べてみるると、
irb(main):004:0> conf.inspect_mode
=> true
「出力結果をinspectしたものを表示します。」ですね。
これの影響でinspect
をつけなくても常にinspect
がついている状態になるようです。
試しに別の設定(:pretty_inspect)にして試してみました。
irb(main):010:0> conf.inspect_mode = :pp
=> :pp
- !ruby/object:Review
concise_attributes:
irb(main):012:0> user = User.find 1
User Load (0.8ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x000055fcbd833780
id: 1,
name: "1234567890",
created_at: Thu, 12 Dec 2019 05:43:52 UTC +00:00,
updated_at: Thu, 12 Dec 2019 05:43:52 UTC +00:00>
irb(main):013:0> user.reviews
Review Load (0.8ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> [#<Review:0x000055fcbd87b120
id: 1,
content: "hoge",
user_id: 1,
book_id: 0,
status: "draft",
created_at: Thu, 11 Jun 2020 05:27:55 UTC +00:00,
updated_at: Thu, 11 Jun 2020 05:27:55 UTC +00:00>,
#<Review:0x000055fcbd87af68
id: 2,
・・・(長いので省略)
LIMIT 11
はついてないようです。
これにて調査完了です。ちゃんと原因がわかるとすっきりしますね!
まとめ
今回の調査の結果、「irbでデータを取得したらLIMIT 11される件」の原因は、
-
inspect
でデータ取得すると人間が読みやすいように自動的にLIMIT 11
が追加される - irbをデフォルト設定で使うとデータ取得時に
inspect
が自動で追加される
でした。
てか、LIMIT 11
の原因より、irbではinspect
よりpretty_inspect
の方が見やすいということがわかったことが今後の役に立つ成果だったかもしれません。