🐘

【Rails】Arel とはなにか

2024/03/13に公開

概要

Arel::Table は Ruby on Rails の ActiveRecord の機能の一部です。
SQL テーブルを Ruby のオブジェクトとして表現することができます。

複雑なクエリや、外部サービスに向けてクエリを発行するときに便利です。
https://api.rubyonrails.org/classes/Arel.html

詳細

前提知識

例えば以下のようなクエリを実行したいとします。

SELECT `articles`.* FROM `articles`

普通に ActiveRecord を使うと、以下のように書けます。

Article.all

to_sqlメソッドで発行されるクエリを確認できます。

> Article.all.to_sql
=> "SELECT `articles`.* FROM `articles`"

このクエリを、Article モデルを使わずに実行する場合、以下のように書けます。

> ActiveRecord::Base.connection.exec_query("SELECT `articles`.* FROM `articles`")
=>
#<ActiveRecord::Result:0x0000ffffb8b65770
 @column_types={},
 @columns=["id", "title", "body", "is_published", "created_at", "updated_at"],
 @hash_rows=nil,
 @rows=[[1, "title", "ああああ", 0, 2024-03-13 05:39:29.082136 UTC, 2024-03-13 05:39:29.082136 UTC]]>

この実行結果として、ActiveRecord::Result のインスタンスが返ります。

クエリの実行結果を扱いやすい形にしています。ここでは詳しい説明は端折りますが、
rows に結果のレコードが配列で格納されていることに注目してください。
https://api.rubyonrails.org/classes/ActiveRecord/Result.html

Arel はクエリを組み立てるときに便利なクラス

上記のように、いわゆる生クエリを組み立てるときに便利なのが Arel クラスです。

シンプルなクエリならまだ問題ないですが、複雑なクエリになってくる(join したり)とコードの見通しも悪くなりますし、SQL インジェクションのリスクも高くなります。

Arel クラスを使うことで、メソッドチェーンすることができるため、書きやすくなります。
以下がその例です。

Arel::Table.new(:articles).project(Arel.star)

to_sqlで確認すると、目的のクエリが作成できています。

> Arel::Table.new(:articles).project(Arel.star).to_sql
=> "SELECT * FROM `articles`"

Arel::Table.new(:articles) でテーブルのオブジェクトを作成しています。
projectSLECTArel.star*を意味しています。

必要なカラムだけ欲しいときは、project の引数に以下のように指定すると良いです。

> Arel::Table.new(:articles).project(table[:title], table[:body]).to_sql
=> "SELECT `articles`.`title`, `articles`.`body` FROM `articles`"

Arel で作成したクエリを、先程のように ActiveRecord::Base.connection.exec_query で実行すると、同じ結果が得られることがわかります。

> sql = Arel::Table.new(:articles).project(Arel.star).to_sql
=> "SELECT `articles`.`title`, `articles`.`body` FROM `articles`"

> ActiveRecord::Base.connection.exec_query(sql)
=>
#<ActiveRecord::Result:0x0000ffffb947bdf0
 @column_types={},
 @columns=["id", "title", "body", "is_published", "created_at", "updated_at"],
 @hash_rows=nil,
 @rows=[[1, "title", "ああああ", 0, 2024-03-13 05:39:29.082136 UTC, 2024-03-13 05:39:29.082136 UTC]]>

以下のようにメソッドチェーンでクエリを作成していけます。

hogehoge_table = Arel::Table.new(:hogehoge)
fugafuga_table = Arel::Table.new(:hogehoge)

hogehoge_table
  .join(fugafuga_table)
  .on(hogehoge_table[:id].eq(fugafuga_table[:hogehoge_id]))
  .project(
    hogehoge_table[:id], hogehoge_table[:title], hogehoge_table[:body]
  ).to_sql
=> "SELECT `hogehoge`.`id`, `hogehoge`.`title`, `hogehoge`.`body` FROM `hogehoge` INNER JOIN `hogehoge` ON `hogehoge`.`id` = `hogehoge`.`hogehoge_id`"

外部サービス(Amazon Redshift)などに投げるクエリを Arel で作成するといった使い方ができます。

まとめ

Arel について簡単な使い方を書いていきました!
Rails を使っていると ActiveRecord が便利すぎて SQL に慣れないことがありますが、
Arel を使うと少しはアレルギーを解消できると思います!

Discussion