Rails 7 adds ActiveRecord::Relation#structurally_compatible?


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.