カテゴリ: Rails 更新日: 2026/02/03

Railsの検索とフィルタの設計!初心者でもわかるParamsとクエリオブジェクトの使い方

検索・フィルタの設計:クエリオブジェクト/Paramsオブジェクト導入法
検索・フィルタの設計:クエリオブジェクト/Paramsオブジェクト導入法

先生と生徒の会話形式で理解しよう

生徒

「Railsで一覧表示の検索や絞り込みをしたいんですが、どこに書けばいいのかわかりません…」

先生

「それなら、コントローラをスッキリ保ちながら検索を実装できる方法がありますよ。」

生徒

「パラメータをどう処理するのかもよく分からなくて…」

先生

「それでは、Paramsオブジェクトとクエリオブジェクトを使って、検索とフィルタ処理を分かりやすく設計する方法を学びましょう!」

1. Railsで検索・フィルタをする場面とは?

1. Railsで検索・フィルタをする場面とは?
1. Railsで検索・フィルタをする場面とは?

Webアプリケーションでは、データの一覧画面で検索フィルタ(絞り込み)が必要になる場面がよくあります。たとえば、ブログ記事をタイトルで検索したり、カテゴリーで絞り込んだりするケースです。

Railsでは、こういった機能をコントローラに直接書くこともできますが、それではコードが長くなり、メンテナンスがしづらくなってしまいます。そこで、検索専用の処理を切り出す「クエリオブジェクト」や、パラメータを整理して使いやすくする「Paramsオブジェクト」が登場します。

2. パラメータとは?Paramsの基本

2. パラメータとは?Paramsの基本
2. パラメータとは?Paramsの基本

パラメータとは、ユーザーが検索フォームなどから送信する「条件」のことです。Railsでは、この情報はparamsというハッシュの形でコントローラに渡されます。

たとえば、タイトルを検索するフォームで「Ruby」と入力した場合、以下のようなリクエストになります。


<form action="/posts" method="get">
  <input type="text" name="title" value="Ruby">
  <button type="submit">検索</button>
</form>

このフォームを送信すると、URLには?title=Rubyのようにパラメータが付きます。Railsではこれを次のように受け取ります。


params[:title]  #=> "Ruby"

3. フィルタ処理をコントローラに書くとどうなる?

3. フィルタ処理をコントローラに書くとどうなる?
3. フィルタ処理をコントローラに書くとどうなる?

検索や絞り込みの処理をすべてコントローラに書くと、次のようにゴチャゴチャしてしまいます。


def index
  posts = Post.all
  posts = posts.where("title LIKE ?", "%#{params[:title]}%") if params[:title].present?
  posts = posts.where(category: params[:category]) if params[:category].present?
  render :index, locals: { posts: posts }
end

このように、条件が増えるとコントローラの中身がどんどん複雑になっていきます。これでは、コードの再利用やテストがしづらくなります。

4. クエリオブジェクトで検索処理を整理しよう

4. クエリオブジェクトで検索処理を整理しよう
4. クエリオブジェクトで検索処理を整理しよう

そこで登場するのがクエリオブジェクトです。検索専用のクラスを作ることで、ロジックをスッキリまとめることができます。

次のようなPostSearchクラスを作成します。


class PostSearch
  def initialize(params)
    @params = params
  end

  def results
    scope = Post.all
    scope = scope.where("title LIKE ?", "%#{@params[:title]}%") if @params[:title].present?
    scope = scope.where(category: @params[:category]) if @params[:category].present?
    scope
  end
end

コントローラ側は次のようにシンプルになります。


def index
  search = PostSearch.new(params)
  render :index, locals: { posts: search.results }
end

これにより、ロジックと表示の分離ができ、保守性の高いコードになります。

5. Paramsオブジェクトでパラメータの取り扱いを安全に

5. Paramsオブジェクトでパラメータの取り扱いを安全に
5. Paramsオブジェクトでパラメータの取り扱いを安全に

さらに一歩進めて、Paramsオブジェクトを使うことで、パラメータの安全性や意図の明確化ができます。

次のように、Strong Parametersの仕組みを使って安全なパラメータだけを許可します。


def search_params
  params.permit(:title, :category)
end

これをクエリオブジェクトに渡せば、不正なパラメータが混入するのを防げます。


search = PostSearch.new(search_params)

6. 検索フォームとの連携

6. 検索フォームとの連携
6. 検索フォームとの連携

クエリオブジェクトとParamsオブジェクトを使えば、検索フォームともスムーズに連携できます。

例えば、indexページに以下のようなフォームを用意します。


<form action="/posts" method="get" class="mb-3">
  <input type="text" name="title" placeholder="タイトルで検索" class="form-control mb-2">
  <select name="category" class="form-select mb-2">
    <option value="">すべてのカテゴリ</option>
    <option value="tech">技術</option>
    <option value="life">生活</option>
  </select>
  <button type="submit" class="btn btn-primary">検索</button>
</form>

検索条件を入力して送信すると、コントローラがクエリオブジェクトで結果を取得し、ビューに表示する仕組みです。

7. 使い方の工夫と応用

7. 使い方の工夫と応用
7. 使い方の工夫と応用

このような設計にすると、次のようなメリットがあります。

  • テストしやすくなる(クエリオブジェクトを単体でテスト可能)
  • 条件の追加や変更がしやすい
  • 複数の画面で共通の検索処理を使い回せる

Railsでよくある検索機能フィルタ機能を実装する際は、このように「責務を分ける設計」を意識すると、アプリ全体が読みやすく、拡張しやすくなります。

まとめ

まとめ
まとめ

ここまで、Ruby on Railsにおける検索機能とフィルタ機能の設計について詳しく解説してきました。Webアプリケーションを開発する際、データが増えるにつれて「特定の条件で情報を絞り込む」という機能は欠かせない存在になります。しかし、そのロジックを安易にコントローラへ書き並べてしまうと、プログラムの可読性が著しく低下し、バグの温床となってしまいます。

そこで重要になるのが、今回のテーマである「クエリオブジェクト(Query Object)」「Paramsオブジェクト」の活用です。これらを導入することで、コントローラは「リクエストを受け取り、結果をビューに渡す」という本来の役割に専念でき、複雑なSQLの発行条件やパラメータの加工処理を別クラスに切り出すことができます。これはオブジェクト指向における「単一責任の原則」に基づいた非常にクリーンな設計手法です。

検索機能におけるSQLの挙動を確認しよう

実際にデータベースに対してどのようなクエリが投げられているのか、具体的なデータ構造を例に見てみましょう。まず、検索実行前のpostsテーブルの状態を想定します。


id | title                | category | created_at
---+----------------------+----------+--------------------
1  | Rails入門ガイド       | tech     | 2026-01-01 10:00:00
2  | 週末のキャンプ記録    | life     | 2026-01-05 12:00:00
3  | Rubyの便利なメソッド  | tech     | 2026-01-10 15:00:00
4  | 美味しいカフェ巡り    | life     | 2026-01-15 09:00:00
5  | Dockerで環境構築      | tech     | 2026-01-20 18:00:00

ここで、ユーザーがタイトルに「Ruby」を含み、かつカテゴリが「tech」のものを検索したとします。このとき、内部で実行されるSQLとクエリオブジェクトの挙動は以下のようになります。


SELECT "posts".*
FROM "posts"
WHERE (title LIKE '%Ruby%')
  AND "posts"."category" = 'tech';

このSQLが実行された結果、次のようなレコードが抽出されます。


id | title                | category | created_at
---+----------------------+----------+--------------------
3  | Rubyの便利なメソッド  | tech     | 2026-01-10 15:00:00

クエリオブジェクトの実装を深掘りする

クエリオブジェクト内では、ActiveRecord::Relationオブジェクトをメソッドチェーンで繋いでいくのが一般的です。これにより、条件が指定されていない場合は全件取得のスコープを維持しつつ、条件がある場合のみwhere句を追加していく柔軟な処理が可能になります。


class PostSearch
  def initialize(params)
    # paramsはStrong Parametersでフィルタリング済みであることを想定
    @params = params
  end

  def results
    scope = Post.all
    scope = filter_by_title(scope)
    scope = filter_by_category(scope)
    scope
  end

  private

  def filter_by_title(scope)
    return scope if @params[:title].blank?
    scope.where("title LIKE ?", "%#{@params[:title]}%")
  end

  def filter_by_category(scope)
    return scope if @params[:category].blank?
    scope.where(category: @params[:category])
  end
end

上記のようにプライベートメソッドとして切り出すことで、検索条件が増えた際(例えば「作成日での絞り込み」など)も、メソッドを追加するだけで対応できるようになります。

フロントエンド(View)での実装ポイント

Railsのform_withヘルパーを使用する場合、検索フォームはmethod: :getで作成するのが基本です。検索はデータの更新を伴わないため、URLにパラメータを含めることで、検索結果のページをブックマークしたり、URLを共有したりすることが可能になります。


<%= form_with url: posts_path, method: :get, local: true do |f| %>
  <div class="field">
    <%= f.label :title, "キーワード" %>
    <%= f.text_field :title, value: params[:title] %>
  </div>
  <div class="field">
    <%= f.label :category, "カテゴリ" %>
    <%= f.select :category, [['技術', 'tech'], ['生活', 'life']], include_blank: "選択してください", selected: params[:category] %>
  </div>
  <%= f.submit "検索する" %>
<% end %>

このように、value: params[:title]のように指定しておくことで、検索実行後も入力した値がフォームに残るようになり、ユーザー体験が向上します。

さらなる応用:並び替え(ソート)機能

検索条件だけでなく、並び替え機能もクエリオブジェクトに追加するとより強力です。例えば、「新着順」「古い順」などの切り替えも、パラメータ一つで制御できます。


def results
  scope = Post.all
  scope = filter_by_title(scope)
  scope = filter_by_category(scope)
  scope = sort_scope(scope)
  scope
end

private

def sort_scope(scope)
  case @params[:sort]
  when 'recent'
    scope.order(created_at: :desc)
  when 'old'
    scope.order(created_at: :asc)
  else
    scope.order(id: :desc)
  end
end

このように、クエリオブジェクトという「データの問い合わせ担当者」を用意することで、Railsアプリケーションのコードは格段に読みやすく、そして強固なものへと進化します。最初は難しく感じるかもしれませんが、中規模以上の開発では必須のテクニックですので、ぜひマスターしてください。

先生と生徒の振り返り会話

生徒

「先生、ありがとうございました!クエリオブジェクトを使うことで、コントローラが驚くほどスッキリしました。今まではif params[:xxx].present?をコントローラに羅列していたので、コードを見るのが苦痛だったんです。」

先生

「そうですね。コントローラにビジネスロジックや複雑なクエリが入り込むと、いわゆる『Fat Controller(太ったコントローラ)』になってしまいます。クエリオブジェクトに切り出すことで、テストも書きやすくなったと思いませんか?」

生徒

「はい!これなら『検索ロジックだけをテストする』ということができますね。データベースの実行結果もSQLのログを見ると、ちゃんと意図した通りにANDで結合されていて安心しました。」

先生

「その通りです。また、今回はParamsオブジェクトについても触れましたが、Strong Parametersをしっかり通すことで、意図しないカラムを検索条件に入れられる脆弱性を防ぐことも重要です。セキュリティ面でもこの設計は有利なんですよ。」

生徒

「なるほど。ただ、小さな個人開発でもクエリオブジェクトは作ったほうがいいんでしょうか?」

先生

「検索項目が1つや2つなら、モデルのscopeを使うだけでも十分な場合があります。でも、項目が3つを超えたり、並び替えやページネーションが絡んでくるなら、最初からクエリオブジェクトを導入しておくと、後々の拡張が楽になりますよ。大事なのは、そのコードが『どこに何が書いてあるか一目でわかるか』という点です。」

生徒

「分かりました。まずは今のプロジェクトの検索機能を、このクエリオブジェクト形式にリファクタリングしてみます!」

関連記事:
カテゴリの一覧へ
新着記事
New1
Ruby
“すべてはオブジェクト”を体感!初心者向けRubyのオブジェクト指向入門【irbで学ぶ】
New2
Ruby
Rubyの標準入出力を完全ガイド!puts・print・pの違いとデバッグ活用法
New3
Ruby
Gemとは?RubyGemsとBundlerを初心者向けに完全解説!依存関係管理も図解でわかりやすく理解
New4
Ruby
Rubyの文字エンコーディング入門!UTF-8・マジックコメント・外部/内部エンコーディングを完全解説
人気記事
No.1
Java&Spring記事人気No1
Ruby
Rubyのreduceとinject入門!合計計算や集計を初心者向けに分かりやすく解説
No.2
Java&Spring記事人気No2
Ruby
Rubyの文字列エンコーディング完全ガイド!Encoding・force_encoding・encodeを初心者向け解説
No.3
Java&Spring記事人気No3
Ruby
Rubyの始め方ガイド:インストールから最初のHello Worldまで(Windows/Mac/Linux)
No.4
Java&Spring記事人気No4
データベース
PostgreSQLのWHERE句を徹底解説!初心者でもわかるSQLデータ抽出の基本
No.5
Java&Spring記事人気No5
Ruby
Rubyのfind/detect/find_indexを徹底解説!目的のデータを素早く探す方法
No.6
Java&Spring記事人気No6
Ruby
Rubyで比較演算子を完全解説!==・===・<=>・eql? の使い分け
No.7
Java&Spring記事人気No7
Ruby
Rubyのselect/reject/filterの使い方を完全解説!初心者向けの条件抽出レシピ
No.8
Java&Spring記事人気No8
データベース
PostgreSQLで順位付け!ROW_NUMBER関数の使い方を初心者向けに徹底解説