Rails 7: ActiveRecord::Relation#structurally_compatible?が追加(翻訳)
Railsの優れている点のひとつは、SQLコードをまったく書かずにデータベースオブジェクトやクエリを簡単に扱えることです。これが可能なのは、Railsのコアライブラリの中で間違いなく最高峰であるActive Recordライブラリのおかげです。
Active Recordクエリインターフェイスを使えば、findやgroupやjoinsといったさまざまなクエリ操作を実行できます。
また、whereやnotやor操作を用いてリレーションをチェインすることも可能です。ただしorやand操作については、チェインする2つのリレーションに構造上の互換性を持たせておく必要があります。
2つのリレーションに構造上の互換性を持たせるには、両者が同じモデルを対象とし、かつwhere(groupが定義されていない場合)またはhaving(groupが存在する場合)だけが異なっていなければなりません。
従来は、2つのリレーションが構造上の互換性を持っているかどうかを簡単に確かめる方法がありませんでした。リレーションの値をイテレーションするか、orやandで発生するArgumentErrorをキャッチする必要がありました。
しかし、クエリでorやandを実行する前に2つのリレーションが構造上の互換性を持っているかどうかを簡単にチェックできるActiveRecord::Relation#structurally_compatible?がRails 7に追加されました(#41841)。
たとえばPostモデルに :content、:user_id、statusという属性があり、Commentモデルに:post_id、:user_id、:contentという属性があるとしましょう。Postはhas_many :commentsを持ち、Commentはbelongs_to :postを持っています。
構造上の互換性を持つリレーションが2つあるとします。
relation_1 = Post.where(status: 'active')
relation_2 = Post.where(id: current_user.id)
また、構造上の互換性を持たないリレーションも2つあるとします。
relation_3 = Post.where(status: 'active')
relation_4 = Post.joins(:comments).where(comments: { user_id: current_user.id})
変更前
これらのリレーション間でorクエリを実行しなければならなくなった場合、ArgumentErrorをキャッチします。
relations = [[relation_1, relation_2], [relation_3, relation4]]
relations.each do |relation|
left = relation.first
right = relation.last
begin
left.or(right)
rescue ArgumentError
# 構造上の互換性を持たないリレーションが失敗したらArgumentErrorをrescueする
end
end
変更後
Rails 7では、リレーション同士が構造上の互換性を持つかどうかをクエリ実行前にチェックできます。
relations = [[relation_1, relation_2], [relation_3, relation4]]
relations.each do |relation|
left = relation.first
right = relation.
if left.structurally_compatible?(right)
left.or(right)
end
end
これは、例外をキャッチせずにリレーションを手軽にクエリできる素晴らしい改善です。このような小さなヘルパー機能が豊富にあるからこそ、Active Recordが優秀なライブラリとなり、Railsフレームワークが開発者にとってより使いやすくなっています。
概要
原著者の許諾を得て翻訳・公開いたします。
参考: 週刊Railsウォッチ20210830『
ActiveRecord::Relation#structurally_compatible?が追加』