Active Recordのallが持つ条件を引き継ぐ性質と動的クエリ構築への応用 24 views Post @wakairo 17 hours22 Feb, 2026 06:32 +00:00 RailsのActive Recordのallメソッドは、 その名前から「テーブルに存在する全レコードを取得するメソッド」と理解されがちです。 しかし実際には「その時点までに積み上げられた条件に該当するものを全て取得する」という実務上とても重要な性質を持っています。 本記事では、このallの性質の基本と、動的にデータベースクエリを組み立てる際の実践的な応用テクニックについて解説します。 allは「構築済みの条件」を引き継ぐ データベースにPenモデルのレコードが3件保存されているとします。 以下のようにPenクラスに対して直接allを呼び出すと、テーブルに存在する全てのレコードが取得されます。 test(dev):000> Pen.all Pen Load (1.2ms) SELECT "pens".* FROM "pens" /* loading for pp */ LIMIT 11 => [#<Pen:0x00007cdf641ba6f0 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007cdf66b23208 id: 2, color: "blue", price: 80, created_at: "2026-02-20 05:53:03.457025000 +0000", updated_at: "2026-02-20 05:53:03.457025000 +0000">, #<Pen:0x00007cdf66b230c8 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] では、別の条件に続けてallを呼び出すとどうなるでしょうか。 以下の実行例を見ると分かるように、allは「全て」ではなく、「それまでの条件を満たすもの全て」を返します。 ここではcolor: "red"を満たすレコードだけが取得されています。 test(dev):000> Pen.where(color: "red").all Pen Load (0.1ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11 => [#<Pen:0x00007cdf66b20648 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007cdf66b20508 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] このように、allは単に全件取得する機能だけでなく、 手前に条件がある場合には「そこまでに構築されたクエリ条件(Relationオブジェクト)を引き継ぎ、そのまま返す」という機能を担う側面があります。 応用例:動的クエリ構築においてallを起点にしてメソッドチェーン可能にする この「そこまでに構築された条件を引き継ぐ」というallの性質は、 「URLのクエリパラメータが存在する場合だけ条件を追加する」といった動的なデータベースクエリ構築において役立ちます。 例えば、クエリパラメータを処理するfilter_byというスコープを定義することを考えてみましょう。 このスコープは、以下のように「1年以内に作成されたレコード」という前提条件(where)のあとにメソッドチェーンで呼び出される想定です。 test(dev):000> Pen.where(created_at: 1.year.ago..).filter_by(color: "red", max_price: 100) Pen Load (0.4ms) SELECT "pens".* FROM "pens" WHERE "pens"."created_at" >= '2025-02-21 05:50:22.982591' AND "pens"."color" = 'red' AND "pens"."price" <= 100 /* loading for pp */ LIMIT 11 => [#<Pen:0x000072d2c03393d8 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">] この挙動を実現するfilter_byスコープの実装は以下のようになります。 最初にallを呼び出して現在のRelation(既に構築済みのwhere条件など)を変数pensに格納し、 そのpensに対してパラメータの有無をチェックしながら条件を継ぎ足しています。 class Pen < ApplicationRecord scope :filter_by, ->(params) do pens = all pens = pens.where(color: params[:color]) if params[:color].present? pens = pens.where(price: ..params[:max_price]) if params[:max_price].present? pens end end ここで重要になるのが、filter_byの中で最初にallを呼んで変数に入れている点です。 allは「何も条件がない状態」ではなく、 「このスコープが呼ばれた時点のRelationをそのまま受け取る」役割を果たしています。 そのため、Pen.where(created_at: …)のように手前で条件を付けていても、 それをリセットすることなくさらに条件を積み上げられます。 つまり、allを変数に入れることで、 手前の条件を維持した上でさらに条件を積み上げていくための「起点」を作ることができるのです。 なお、クエリパラメータが全て空で条件追加がない場合でも、allが返した元のRelationがpensに入っているため、 メソッドチェーンをそのまま継続できます。 (参考)個別スコープへの切り出しとそのタイミング 先ほどの例ではfilter_byの内部で変数に再代入しながら条件を追加していましたが、 もしcolorやpriceによる絞り込みをアプリケーション内の他の場所で単独で利用したい場合は、 それぞれを個別のスコープとして切り出すことでコードをよりキレイに書くことができます。 Active Recordのスコープは、ブロックの評価結果がnilまたはfalseになった場合、 自動的に元のRelationをそのまま返す(条件を追加せずにスルーする)という便利な仕様になっています。 そのため、以下のようにメソッドチェーンでそのままつなぐだけで、条件分岐をスコープの中に自然にカプセル化できます。 class Pen < ApplicationRecord scope :with_color, ->(color) { where(color: color) if color.present? } scope :with_max_price, ->(max_price) { where(price: ..max_price) if max_price.present? } scope :filter_by, ->(params) do with_color(params[:color]).with_max_price(params[:max_price]) end end なお、YAGNI原則の観点からは、 これらの個別スコープ(with_colorやwith_max_price)が他の場所で「本当に必要になるまで」は、 無理に切り出さない方が良いでしょう。 つまり、最初から再利用性を過剰に意識してスコープを量産するよりは、 まずは前述のpens = allを使うアプローチで1箇所にまとめておき、 個別スコープが本当に必要になったタイミングで切り出す方が、 結果的には開発コストを減らせるはずです。 Write Preview How to write in Markdown
@wakairo 17 hours22 Feb, 2026 06:32 +00:00 RailsのActive Recordのallメソッドは、 その名前から「テーブルに存在する全レコードを取得するメソッド」と理解されがちです。 しかし実際には「その時点までに積み上げられた条件に該当するものを全て取得する」という実務上とても重要な性質を持っています。 本記事では、このallの性質の基本と、動的にデータベースクエリを組み立てる際の実践的な応用テクニックについて解説します。 allは「構築済みの条件」を引き継ぐ データベースにPenモデルのレコードが3件保存されているとします。 以下のようにPenクラスに対して直接allを呼び出すと、テーブルに存在する全てのレコードが取得されます。 test(dev):000> Pen.all Pen Load (1.2ms) SELECT "pens".* FROM "pens" /* loading for pp */ LIMIT 11 => [#<Pen:0x00007cdf641ba6f0 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007cdf66b23208 id: 2, color: "blue", price: 80, created_at: "2026-02-20 05:53:03.457025000 +0000", updated_at: "2026-02-20 05:53:03.457025000 +0000">, #<Pen:0x00007cdf66b230c8 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] では、別の条件に続けてallを呼び出すとどうなるでしょうか。 以下の実行例を見ると分かるように、allは「全て」ではなく、「それまでの条件を満たすもの全て」を返します。 ここではcolor: "red"を満たすレコードだけが取得されています。 test(dev):000> Pen.where(color: "red").all Pen Load (0.1ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11 => [#<Pen:0x00007cdf66b20648 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007cdf66b20508 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] このように、allは単に全件取得する機能だけでなく、 手前に条件がある場合には「そこまでに構築されたクエリ条件(Relationオブジェクト)を引き継ぎ、そのまま返す」という機能を担う側面があります。 応用例:動的クエリ構築においてallを起点にしてメソッドチェーン可能にする この「そこまでに構築された条件を引き継ぐ」というallの性質は、 「URLのクエリパラメータが存在する場合だけ条件を追加する」といった動的なデータベースクエリ構築において役立ちます。 例えば、クエリパラメータを処理するfilter_byというスコープを定義することを考えてみましょう。 このスコープは、以下のように「1年以内に作成されたレコード」という前提条件(where)のあとにメソッドチェーンで呼び出される想定です。 test(dev):000> Pen.where(created_at: 1.year.ago..).filter_by(color: "red", max_price: 100) Pen Load (0.4ms) SELECT "pens".* FROM "pens" WHERE "pens"."created_at" >= '2025-02-21 05:50:22.982591' AND "pens"."color" = 'red' AND "pens"."price" <= 100 /* loading for pp */ LIMIT 11 => [#<Pen:0x000072d2c03393d8 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">] この挙動を実現するfilter_byスコープの実装は以下のようになります。 最初にallを呼び出して現在のRelation(既に構築済みのwhere条件など)を変数pensに格納し、 そのpensに対してパラメータの有無をチェックしながら条件を継ぎ足しています。 class Pen < ApplicationRecord scope :filter_by, ->(params) do pens = all pens = pens.where(color: params[:color]) if params[:color].present? pens = pens.where(price: ..params[:max_price]) if params[:max_price].present? pens end end ここで重要になるのが、filter_byの中で最初にallを呼んで変数に入れている点です。 allは「何も条件がない状態」ではなく、 「このスコープが呼ばれた時点のRelationをそのまま受け取る」役割を果たしています。 そのため、Pen.where(created_at: …)のように手前で条件を付けていても、 それをリセットすることなくさらに条件を積み上げられます。 つまり、allを変数に入れることで、 手前の条件を維持した上でさらに条件を積み上げていくための「起点」を作ることができるのです。 なお、クエリパラメータが全て空で条件追加がない場合でも、allが返した元のRelationがpensに入っているため、 メソッドチェーンをそのまま継続できます。 (参考)個別スコープへの切り出しとそのタイミング 先ほどの例ではfilter_byの内部で変数に再代入しながら条件を追加していましたが、 もしcolorやpriceによる絞り込みをアプリケーション内の他の場所で単独で利用したい場合は、 それぞれを個別のスコープとして切り出すことでコードをよりキレイに書くことができます。 Active Recordのスコープは、ブロックの評価結果がnilまたはfalseになった場合、 自動的に元のRelationをそのまま返す(条件を追加せずにスルーする)という便利な仕様になっています。 そのため、以下のようにメソッドチェーンでそのままつなぐだけで、条件分岐をスコープの中に自然にカプセル化できます。 class Pen < ApplicationRecord scope :with_color, ->(color) { where(color: color) if color.present? } scope :with_max_price, ->(max_price) { where(price: ..max_price) if max_price.present? } scope :filter_by, ->(params) do with_color(params[:color]).with_max_price(params[:max_price]) end end なお、YAGNI原則の観点からは、 これらの個別スコープ(with_colorやwith_max_price)が他の場所で「本当に必要になるまで」は、 無理に切り出さない方が良いでしょう。 つまり、最初から再利用性を過剰に意識してスコープを量産するよりは、 まずは前述のpens = allを使うアプローチで1箇所にまとめておき、 個別スコープが本当に必要になったタイミングで切り出す方が、 結果的には開発コストを減らせるはずです。
RailsのActive Recordの
allメソッドは、 その名前から「テーブルに存在する全レコードを取得するメソッド」と理解されがちです。 しかし実際には「その時点までに積み上げられた条件に該当するものを全て取得する」という実務上とても重要な性質を持っています。本記事では、この
allの性質の基本と、動的にデータベースクエリを組み立てる際の実践的な応用テクニックについて解説します。allは「構築済みの条件」を引き継ぐデータベースに
Penモデルのレコードが3件保存されているとします。 以下のようにPenクラスに対して直接allを呼び出すと、テーブルに存在する全てのレコードが取得されます。では、別の条件に続けて
allを呼び出すとどうなるでしょうか。 以下の実行例を見ると分かるように、allは「全て」ではなく、「それまでの条件を満たすもの全て」を返します。 ここではcolor: "red"を満たすレコードだけが取得されています。このように、
allは単に全件取得する機能だけでなく、 手前に条件がある場合には「そこまでに構築されたクエリ条件(Relationオブジェクト)を引き継ぎ、そのまま返す」という機能を担う側面があります。応用例:動的クエリ構築において
allを起点にしてメソッドチェーン可能にするこの「そこまでに構築された条件を引き継ぐ」という
allの性質は、 「URLのクエリパラメータが存在する場合だけ条件を追加する」といった動的なデータベースクエリ構築において役立ちます。例えば、クエリパラメータを処理する
filter_byというスコープを定義することを考えてみましょう。 このスコープは、以下のように「1年以内に作成されたレコード」という前提条件(where)のあとにメソッドチェーンで呼び出される想定です。この挙動を実現する
filter_byスコープの実装は以下のようになります。 最初にallを呼び出して現在のRelation(既に構築済みのwhere条件など)を変数pensに格納し、 そのpensに対してパラメータの有無をチェックしながら条件を継ぎ足しています。ここで重要になるのが、
filter_byの中で最初にallを呼んで変数に入れている点です。allは「何も条件がない状態」ではなく、 「このスコープが呼ばれた時点のRelationをそのまま受け取る」役割を果たしています。 そのため、Pen.where(created_at: …)のように手前で条件を付けていても、 それをリセットすることなくさらに条件を積み上げられます。 つまり、allを変数に入れることで、 手前の条件を維持した上でさらに条件を積み上げていくための「起点」を作ることができるのです。 なお、クエリパラメータが全て空で条件追加がない場合でも、allが返した元のRelationがpensに入っているため、 メソッドチェーンをそのまま継続できます。(参考)個別スコープへの切り出しとそのタイミング
先ほどの例では
filter_byの内部で変数に再代入しながら条件を追加していましたが、 もしcolorやpriceによる絞り込みをアプリケーション内の他の場所で単独で利用したい場合は、 それぞれを個別のスコープとして切り出すことでコードをよりキレイに書くことができます。Active Recordのスコープは、ブロックの評価結果が
nilまたはfalseになった場合、 自動的に元のRelationをそのまま返す(条件を追加せずにスルーする)という便利な仕様になっています。 そのため、以下のようにメソッドチェーンでそのままつなぐだけで、条件分岐をスコープの中に自然にカプセル化できます。なお、YAGNI原則の観点からは、 これらの個別スコープ(
with_colorやwith_max_price)が他の場所で「本当に必要になるまで」は、 無理に切り出さない方が良いでしょう。 つまり、最初から再利用性を過剰に意識してスコープを量産するよりは、 まずは前述のpens = allを使うアプローチで1箇所にまとめておき、 個別スコープが本当に必要になったタイミングで切り出す方が、 結果的には開発コストを減らせるはずです。