Question

I am using a polymorphic association of two different models to a tagging model. Great, easy. But, one of these two models also belongs to the other model.

class Facility < ActiveRecord::Base
  has_many :units
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
end
class Unit < ActiveRecord::Base
  belongs_to :facility
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
end
class Tagging < ActiveRecord::Base
  belongs_to :taggable, :polymorphic => true
  belongs_to :tag
end
class Tag < ActiveRecord::Base
  has_many :taggings
end

I am trying to write a scope to retrieve all of the Units for a certain tag, including those Units belonging to a Facility with that tag. This does not work:

named_scope :by_tag_id, lambda {|tag_id| 
{ 
  :select => "DISTINCT units.*",
  :joins => [:taggings, {:facility => :taggings}],
  :conditions => ["taggings.tag_id = ? OR taggings_facilities.tag_id = ?", 
    tag_id.to_i, tag_id.to_i] 
}}

For that named_scope, I only get those units that have the tag.

Any thoughts? Am I missing something obvious here?

Update

I am, unfortunately, on Rails 2.3.x, Ruby 1.8.7.

I am trying right now some techniques with more explicitly written joins.

Update 2

I was not able to answer my own question because I have no reputation. Oops. I should really contribute more...

Well, I guess I just needed to poke and push a bit more. Here is what I have working:

named_scope :by_tag_id, lambda {|tag_id| 
  { 
    :select => "DISTINCT units.*",
    :joins => [
        "INNER JOIN facilities as bti_facilities ON units.facility_id = bti_facilities.id",
        "LEFT JOIN taggings AS bti_facilities_taggings 
          ON bti_facilities_taggings.taggable_id = bti_facilities.id AND bti_facilities_taggings.taggable_type = 'Facility'",
        "LEFT JOIN taggings AS bti_units_taggings ON bti_units_taggings.taggable_id = units.id AND bti_units_taggings.taggable_type = 'Unit'",
      ],
    :conditions => ["bti_units_taggings.tag_id = ? OR bti_facilities_taggings.tag_id = ?", tag_id.to_i, tag_id.to_i] 
  }
}

I am using some intentionally obfuscated table aliases in order to try to avoid collision with other named scopes. (I did not have facilities aliased to start and I ran into a SQL error on the query with I tried to chain with another scope that referenced the facilities table.)

If anyone has a better answer, or some feedback on my method, I would love to hear it.

Was it helpful?

Solution

I see that you've already answered your question, but if you were to use :include => instead of :join => in the original query, it would generate a LEFT OUTER JOIN. That said, you may not need to eager load the association, only query on it, so your mileage may vary.

OTHER TIPS

I wonder if you could write it something like this?

named_scope :by_tag_id, lambda {|tag_id| 
  { 
    :select     => "DISTINCT units.*",
    :include    => [:tags, {:facility => :taggings}],
    :conditions => { :tags => { :tag_id => tag_id } }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top