その1 – その4 まで、Arelの内部構造について説明してきました。
魔法のような技術に思われるArelも、意外と単純な構造をしていました。
今回は、Arelの内部構造をGraphvizで出力する方法を紹介します。
図で構造を出力してみると、クエリがどういうノードで構成されているのかイメージでき、Arelへの理解がもっと深まるかと思います。
以下、目次となります。
その2 – その4 までArelの内部構造を図で表現してきました。
例えば、
product = Arel::Table.new(:products)
product.project('*').to_sql
# select * from products;
のArel内部構造は、
のような図で表現してきました。
実はこの図、Arelで出力可能です。
上記コードをArel to_dotで出力すると、以下のような図が取得可能です。
Arel内部構造をGraphvizで出力するためには、graphvizをインストールしている必要があります。
macをお使いなら、
brew install graphviz
でGraphvizのインストールが可能です。
インストール後、Gemfileに以下を追加して、bundle installしてください。
gem 'ruby-graphviz'
これでgraphvizの準備は完了です。
Arelの構造はto_dotと呼ばれる関数で出力可能です。
product = Arel::Table.new(:products)
product.project('*').to_dot
このto_dotで出力した結果をGraphvizに食わせてあげれば良い訳です。
require 'graphviz'
def gviz(arel)
GraphViz.parse_string(arel.to_dot).output(png: 'arel_internal.png')
end
product = Arel::Table.new(:products)
gviz(product.project('*'))
今回が最終回ですが、紹介しきれなかったクエリ達も多く存在します。
ほんの一部ですが、紹介できてないクエリをgraphvizで出力してみます。
興味のある方は、ご自身でクエリを組み立て、to_dotして構造を眺めてみてはいかがでしょうか。
product = Arel::Table.new(:products)
product
.project('*')
.order(product[:price])
.take(3)
.skip(3)
.to_sql
# mysqlの場合の出力
# "SELECT * FROM `products`
# ORDER BY `products`.`price` LIMIT 3 OFFSET 3"
product = Arel::Table.new(:products)
product
.project('*')
.where(product[:price].eq(1000).or(product[:price].gt(2000)))
# "SELECT * FROM `products`
# WHERE (`products`.`price` = 1000 OR `products`.`price` > 2000)"
product = Arel::Table.new(:products)
corporation = Arel::Table.new(:corporations)
exist_query = product
.where(product[:corporation_id].eq(corporation[:id]))
.project('1')
.exists
corporation
.where(exist_query)
.project('*')
.to_sql
# "SELECT * FROM `corporations`
# WHERE EXISTS (
# SELECT 1 FROM `products`
# WHERE `products`.`corporation_id` = `corporations`.`id`
# )"
その1からその5まで、Arelの内部構造を見てきました。
いかがでしたでしょうか。
こうしてみると、意外と構造も単純だし、ソースもそんなに長くないので読みやすいです。
ActiveRecordは内部でArelを利用しています。
ただ、Arelがうまい具合にActiveRecordに融合されているかというと、そうでもないのが現状です。
時として、Arelを強く意識したコードを書かないといけない時もありますし、そもそもActiveRecordでサポートしていない記述も存在します。
(例を挙げると、union, union all等はActiveRecordレベルではサポートしていないので、Arelでクエリを作る必要があります)
Arelを理解していれば、ActiveRecordを使って複雑なクエリを組み立てるときに役立ちます。
ActiveRecord自体の拡張や、独自のO/R Mapperなんかも作れたりしそうです。
色々な可能性のあるArel, 現在も rails/arel にて活発に開発が行われています。
今後も注目していきたいプロダクトです。