bknoles
bknoles

Reputation: 632

Rails active record query: Find records on one side of a has_many :through that haven't been used with a particular record on the other side

I have a has_many :through relationship set up like so

class Situation < ActiveRecord::Base
  has_many :notifications
  has_many :notiftypes, through: :notifications
end

class Notification < ActiveRecord::Base
  belongs_to :situation
  belongs_to :notiftype
end

class Notiftype < ActiveRecord::Base
  has_many :notifications
  has_many :situations, through: :notifications
end

So, a Situation has many Notifications, which can be of many types (Notiftype).

My problem is trying to query for the notiftypes that have not been set for a particular situation.

Want to find records with no associated records in Rails 3

The answers in that question get me close, but only to the point of finding Notiftypes that have not been set AT ALL.

If this were the standard :situation has_many :notiftypes I could just do a Left Outer Join like so

myquery = Notiftype.joins('LEFT OUTER JOIN situations ON situations.notiftype_id = notiftype.id').where('notiftype_id IS NULL')

but I'm really not sure how to do this with the intermediate table between them.

I have been trying consecutive joins but it's not working. I'm not sure how to join the two separated tables.

Can anyone explain the right way to query the db? I am using SQLite, Rails 3.1, Ruby 1.9.2 right now, but likely Postgresql in the future.

Upvotes: 1

Views: 727

Answers (1)

Harish Shetty
Harish Shetty

Reputation: 64363

Try this:

class Situation < ActiveRecord::Base
  # ...
  has_many :notiftypes, through: :notifications do

    def missing(reload=false)
      @missing_notiftypes = nil if reload
      @missing_notiftypes ||= proxy_owner.notiftype_ids.empty? ?
        Notiftype.all : 
        Notiftype.where("id NOT IN (?)", proxy_owner.notiftype_ids)
    end
  end
end

Now to get the missing Notiftype

situation.notiftypes.missing

If you want to further optimize this to use one SQL rather than two you can do the following:

class Situation < ActiveRecord::Base
  # ...
  has_many :notiftypes, through: :notifications do

    def missing(reload=false)
      @missing_notiftypes = nil if reload
      @missing_notiftypes ||= Notiftype.joins("
          LEFT OUTER JOIN (#{proxy_owner.notiftypes.to_sql}) A
          ON A.id = notiftypes.id").
        where("A.id IS NULL")
    end
  end
end

You can access the missing Notifytypes as:

situation.notiftypes.missing

Upvotes: 0

Related Questions