Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: Active Record APIクイズで最大の難問はどれか(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

参考: 週刊Railsウォッチ20230221: Active Record APIクイズ

Rails: Active Record APIクイズで最大の難問はどれか(翻訳)

RailsのActiveRecordモジュールは、このフレームワークの成功と普及に大きく貢献したコアコンポーネントの1つです。私たちは数か月前に、ActiveRecord APIの知識を問うクイズを公開しました。この短い記事では、このクイズの平均的な成績を確認して、どの問題が回答者にとって最も難しかったかを明らかにします。

はじめに

これまで最も人気が高かったクイズは、以下のクイズでした。

クイズは10問で、Railsエコシステムで重要な要素であるActiveRecordに関するさまざまな方面の知識をセルフテストできます。

本記事では、全回答者から寄せられた解答を集計して、どんな回答が寄せられたか、そしてどの問題が最も難しかったかを明らかにします。

成績別の内訳

本記事執筆時点では1,779件の回答が登録されており、平均点は35%でした。1人の回答者が何度も回答している可能性もありますが、結果には影響しないでしょう。得点分布を以下の棒グラフにまとめました。

点数を登録した回答者数を0~10点、10~20点、90~100点のバケットごとに振り分けた度数分布図

Rails ActiveRecord APIクイズ受験者の得点分布

平均点が35%と予想外に低かったのですが、棒グラフの最頻値が40〜49になっていることがわかります。平均値が低めになったのは、高得点者が少ないために、このデータの得点分布が低い方に偏ったからです。70%超えの方は95パーセンタイルに入っています。80%超えの方は上位1.5%入りです。

問題別の内訳

次に、個別の問題についてユーザーがどう回答したかを見てみましょう。以下の棒グラフのバーをクリックすると、その問題のスクリーンショットが表示されます(訳注: 動的な操作は元記事で行ってください)。

!問題別の内訳

最も難易度の高かった問題

最も難しかったのは問9で、正答率はわずか2.35%でした。この問9では、さまざまなプリロードやeager loadingの手法に関する知識が問われます。これらの手法について詳しくはBigBinaryの以下の記事で詳しく説明されています1

参考: Preload, Eagerload, Includes and Joins - BigBinary Blog

実際の問9は以下のようなものでした。

Screenshot of Qu 9 from Ruby ActiveRecord API quiz: Given the following models, regardless of the data in the database, which of the following patterns will issue a single SQL statement to the database?

Rails ActiveRecordクイズの最難問
DB内のデータにかかわらず、以下の中で単一のSQL文をデータベースに発行するパターンを選べ(複数回答可)

ドメインモデルは、Userクラスとhas_many :postsというかなりシンプルなものになっており、Postごとにtitle属性があります。

User.joinsパターン
User.joinsパターンは、関連付けされたレコードによってユーザーをフィルタするのに用いられますが、このとき、関連付けされたレコードを読み込まない点に注意が必要です。その結果、関連付けられるPostオブジェクトにアクセスしようとするたびにデータベースから読み込まなければならなくなります。これは、あの悪名高いN+1問題の原因になりがちです。
User.preloadパターン
User.preloadパターンでは、Userモデルと、関連付けられるPostモデルが両方ともメモリに読み込まれます。しかし、これは2つの異なるクエリによって実現されています。1つはUserレコードを読み込むクエリで、もう1つはそのユーザーに関連付けられているすべてのPostレコードを読み込むクエリです。preloadを使うと、この2段階読み込みが行われるため、関連付けられるレコードの属性でフィルタできなくなってしまいます。
User.eager_loadパターン
User.eager_loadパターンでは、Userモデルと、関連付けられるPostモデルを両方ともメモリに読み込みますが、このとき単一のLEFT OUTER JOINクエリを使います。つまりこれは正しい回答です。
User.includesパターン
User.includesパターンはUser.preloadと同じことを行いますが、関連付けられているテーブル内の値でクエリをフィルタできる点が異なります。関連付けられているテーブルをwhere句を適用する形でフィルタする場合、このincludesメソッドはUserPostのレコードを両方とも単一のLEFT OUTER JOINクエリで読み込みます。つまりこれも正しい回答です。

まとめ

本記事では、以前公開したRails ActiveRecord APIクイズに登録された結果を簡単に分析してみました。最も難易度が高かった問題は、Railsで関連付けされているレコードを読み込むときのpreloadeager_loadjoinsincludesの違いを理解しているかどうかを問われます。

短い記事ですが、お役に立てられれば幸いです。ご質問やお気づきの点がありましたら元記事末尾のコメント欄までどうぞ。本記事をお楽しみいただけましたら、新着記事をすぐ読めるようぜひ購読をご検討ください。お読みいただきありがとうございました。

参考情報

  1. 元のクイズ: Test Yourself on Rails ActiveRecord API
  2. Preload, Eagerload, Includes and Joins - BigBinary Blog
  3. preloadeager_loadjoinsincludesの違いを解説するBigBinaryの記事: preload, eager-load, join and includes

関連記事

Rubyオブジェクトモデルのクイズで最大の難問はどれか(翻訳)

Rubyの配列クイズで最大の難問はどれか(翻訳)


  1. 訳注: k0kubunさんによる記事『ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い - Qiita』もどうぞ。 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。