LoginSignup
31
11

More than 3 years have passed since last update.

irbでデータを取得したらLIMIT 11される件

Last updated at Posted at 2020-06-11

普段は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で発行されるクエリーに注目してください。↓に当該部分の画像を貼っておきます。
スクリーンショット 2020-06-11 18.03.38.png

なんで何もしてないのにLIMIT 11やねん。ここから原因調査の旅が始まります。

RailsのリポジトリからLIMIT 11を探した件

十中八九Railsが何かやっているのだろうと思ったのでGithubのRailsリポジトリで愚直にLIMIT 11で検索してみました。
スクリーンショット 2020-06-11 18.16.27.png

35件見つかりました。目視でいけるレベルなのでざっとみてみると、、、

それっぽいコードがありました!
スクリーンショット 2020-06-11 18.20.18.png

該当のメソッドを転記しました(Githubへのリンクはこちら

activerecord/lib/active_record/relation.rb
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の動作そのものですね。

そこで"irb" "inspect"でぐぐってみると、
スクリーンショット_2020-06-11_23_05_36.png

結果出力に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の方が見やすいということがわかったことが今後の役に立つ成果だったかもしれません。

31
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
11