Question

I'm having a problem writing a scope to return records where ALL has_many associations match a criteria.

I have these models:

class Product
  has_many :listings
end

class Listing
  belongs_to :product
  belongs_to :issue
end

class Issue
  has_many :listings
end

Basically a product can be listed in several different issues. I want to be able to get all products that have NO listings in a particular issue. So far I have this scope in my Product model:

scope :not_listed_in, lambda { |issue|
  joins(:listings)
    .where("listings.issue_id != ?", issue.id)
}

This doesn't work since it will find any products where at least one listing is not in the issue. I need some way of asking for all products that have NO listings within a particular issue.

Was it helpful?

Solution

Assuming you're using ActiveRecord, you can achieve this by finding all products and removing products in the issue. That would normally result in an array, so in the code below, I did one additional database query to have it return a scoped result so you can cascade other "where" clauses to the result.

class Product < ActiveRecord::Base
  has_many :listings
  scope :not_listed_in, lambda { |issue|
    id_list = Product.pluck(:id) - issue.products.pluck(:id)
    Product.where(id:id_list)
  }
end

class Listing < ActiveRecord::Base
  belongs_to :product
  belongs_to :issue
end

class Issue < ActiveRecord::Base
  has_many :listings
  has_many :products, through: :listings
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top