Search code examples
ruby-on-railsreflectionrails-activerecordarel

How can I get the columns used in the where expression from an existing ActiveRecord_Relation?


Say a lot of my models implement a class method self.foo which returns a ::ActiveRecord_Relation.

From elsewhere in the code, I have access to the Model or the ::ActiveRecord_Relation returned by that method, and I would like to find out which columns are used in where expressions in the query.

If the query is simple, I can get it from where_values_hash on the ActiveRecord_Relation, but I'm having trouble figuring out how to get all the columns in more complex queries.

class A < ApplicationRecord
  def self.foo(bar)
    where(column1: bar)
  end
end

class B < ApplicationRecord
  def self.foo(bar)
    joins(:c).
    merge(C.where(column2: bar))
  end
end

elsewhere

# this evaluates to ["column1"] - success!
A.foo(bar).where_values_hash.keys 

# How can I get "c" or C and "column2" from B or B.foo(bar)?
# B or B.foo(bar).??? -> ["c", "column2"]

I could always get it from parsing the relation's .to_sql string, but there's got to be a better way.


Solution

  • The where_values_hash method takes a parameter which is the table you want the where clause columns from. If you would like all of them you can do some stuff with Arel to get all of the joined tables where_values_hash. The only trick will be that you likely want to keep the context of the table the columns come from.

    def join_where_values(relation)
      relation.arel.join_sources.map do |r|
        { r.left.name => relation.where_values_hash(r.left.name) }
      end
    end
    
    { 'b' => B.foo(bar).where_values_hash, 'joins' => join_where_values(B.foo(bar)) }