Joel Grannas
Joel Grannas

Reputation: 2016

building a dynamic where clause in model with ruby, iterating over activerecord object

I have a model with an has_many association: Charts has_many ChartConditions

charts model has fields for:

The chart_conditions model has fields for

Basically my Chart tells us which model (using the table_name field) I want to run a dynamic query on. Then the chart_conditions for the Chart will tell us which fields in that model to sort on.

So In my models that will be queried, i need to dynamically build a where clause using multiple chart_conditions.

Below you can see that i do a joins first based on all the object's assoc_name fields

Example of what I came up with. This works, but not with a dynamic operator for the name/value and also throws a deprecation warning.

  def self.dynamic_query(object)
    s = joins(object.map{|o| o.assoc_name.to_sym})

    #WORKS BUT GIVES DEPRECATED WARNING (RAILS4)
    object.each do |cond|
      s = s.where(cond.assoc_name.pluralize.to_sym => {cond.name.to_sym => cond.value})
    end
  end

How can i then add in my dynamic operator value to this query? Also why can I not say:

s = s.where(cond.assoc_name.pluralize : {cond.name : cond.value})

I have to use the => and .to_sym to get it to work. The above syntax errors: syntax error, unexpected ':' ...ere(cond.assoc_name.pluralize : {cond.name : cond.value}) ... ^

Upvotes: 0

Views: 2243

Answers (1)

zwippie
zwippie

Reputation: 15515

What if you store the query in a variable and append to that?

def self.dynamic_query(object)
  q = joins(object.map{|o| o.assoc_name.to_sym})
  object.each do |cond|
    q = q.where(cond.assoc_name.pluralize : {cond.name : cond.value})
  end
  q # returns the full query
end

Another approach might be the merge(other) method. From the API Docs:

Merges in the conditions from other, if other is an ActiveRecord::Relation. Returns an array representing the intersection of the resulting records with other, if other is an array.

Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
# Performs a single join query with both where conditions.

That could be useful to knot all the conditions together.

Upvotes: 2

Related Questions