← Back to Upcase

Lazy Evaluation?


(Austin Wang) #1

There’s a part of my application where a table of contacts is displayed and each of the contact’s name and (many to many) tags are displayed. I have one big query to load in the data (in one query instead of a bunch). Then, the data is further filtered through session variables set by the user.

Each contact can have many tags.

contacts =  contacts.
              joins{tags.outer}.
              group('contacts.id').
              select(
                "contacts.name, 
                array_to_string(array_agg(tags.name), ',') as tag_list, 
              )
  
filter_tags = session[:filter_tags] if session[:filter_tags]
if filter_tags
    contacts = contacts.where{(taggings.tag_id >> filter_tags)
end

The list of tags is displayed for each contact with:

"#{contact.tag_list.split(',').uniq.join(", ")}"

When the session[filter] is not set, the table works great. It displays all of a contact’s tags.

However, when the session[filter] is set, the table only displays the tag for which it is filtering. I would like it to only show contacts tagged with the selected tags, but for those contacts, show all the other tags they are tagged with. Each contact should already have the whole list of tags aggregated as tag_list, but that is not the case.

My only guess is that rails is Lazy Evaluating (?), and so only when it must does it call all the joins and wheres all at once, so the filter_tags part happens at the same time as the initial query.

Is this the case? How can i force an evaluation so that the original tags are not filtered away? Is there another/better approach?


(Sean Griffin) #2

You’re correct. where, select, order, group, and other query methods return an instance of ActiveRecord::Relation, which represents the results of a SQL query. It doesn’t run the query until you try and access one of the records returned by it. If you want to force the query to run, you can call .to_a to return an array with the results. If you still want a relation, but force it to load, there’s not really a nice way to do this in Rails 3.2. The reload method exists, though that will run the query a second time had it already been loaded. In Rails 4, there’s a load method you can call.


(Austin Wang) #3

Thanks @seangriffin – good to know! any ideas on an alternative way to structure this query so I can still get all the tags without iterating through each contact’s tags? My original Array_agg seems kind of like a hacky way to do it, but I haven’t found/thought of a better way.