views:

140

answers:

1

Right now I'm dealing with an issue regarding an intense acts_as_tree MySQL query via rails. The model I am querying is Foo. A Foo can belong to any one City, State or Country. My goal is to query Foos based on their location. My locations table is set up like so:

  • I have a table in my database called locations
  • I use a combination of acts_as_tree and polymorphic associations to store each individual location as either a City, State or Country. (This means that my table consists of the rows id, name, parent_id, type)

Let's say for instance, I want to query Foos in the state "California". Beside Foos that directly belong to "California", I should get all Foos that belong every City in "California" like Foos in "Los Angeles" and "San Francisco". Not only that, but I should get any Foos that belong to the Country that "California" is in, "United States".

I've tried a few things with associations to no avail. I feel like I'm missing some super-helpful Rails-fu here. Any advice?

A: 

I'm sure you've found an solution for this by now....

For stuff like this I find I resort to denormalizing the grand ancestors and beyond. While its not best DB practice it makes the use cases much easier with just a little bit of overhead.

So for example your locations table would have a city_id, state_id and country_id column and in the cases where the type is "state" the city_id is null. and for type="country" city_id and state_id are null. Then at any point your query to find all of the children of a given node is simple you just have to know what node type you're after.

A simple before_save can do the denormalizing.

belongs_to :city, :class_name => "Location", :foreign_key => :city_id
belongs_to :state, :class_name => "Location", :foreign_key => :state_id
belongs_to :country, :class_name => "Location", :foreign_key => :country_id

#might want to throw in some validation that one of the ids is set...
def before_save
  self.state_id = self.city.state_id if city_id && city
  self.country_id = self.state.country_id if state_id && state
end

Alternatively you may want to look at something like the nested set implementation that with the addition of two integer fields (left and right) lets you get all the subnodes for any node in the hierarchy my favourite one is http://github.com/collectiveidea/awesome_nested_set

Corey