One of the best things about Rails is the ease of handling database objects and queries without writing any SQL code. It is possible because of ActiveRecord library that is arguably one of the best core libraries of Rails.
With the use of ActiveRecord Query Interface
, we can perform various query operations such as Find, Group, Joins, and various other operations.
We could also chain relations with the help of where
, not
, or
operations.
But for or
and and
operations,
we need to ensure that the two chaining relations are structurally compatible.
For two relations to be structurally compatible, they must be scoping the same model, and they must differ only by #where (if no #group has been defined) or #having (if a #group is present).
Previously, there was no easy way of knowing if two relations are structurally compatible.
We either had to iterate over the values of the relation or catch ArgumentError thrown by or
or and
.
However,
Rails 7 adds ActiveRecord::Relation#structurally_compatible?
to provide an easy way to check if two relations are structurally compatible
before running or
or and
query on it.
Let’s assume we have a Post
model with attributes :content
, :user_id
, status
and another Comment
model with attributes :post_id
, :user_id
and :content
.
Also, Post
has_many :comments and Comment
belongs to :post.
We have two structurally compatible relations:
relation_1 = Post.where(status: 'active')
relation_2 = Post.where(id: current_user.id)
And two structurally incompatible relations:
relation_3 = Post.where(status: 'active')
relation_4 = Post.joins(:comments).where(comments: { user_id: current_user.id})
Before
If we had to run an or
query between these relations we would catch an 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
# Rescue ArgumentError when structurally incompatible relations fail
end
end
After
In Rails 7, before running the query, we can check if the relations are structurally compatible.
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
This is a great improvement as it makes it easier to query relations without having to catch exceptions. Small helper features like this are what makes ActiveRecord a great library and Rails, a better framework for developers.