I have a Product
model that has_and_belongs_to_many :taxons
, and I want to find all products that are in specific taxons.
For example, if a product belongs to both the "Ruby on Rails" and "Shirts" taxon, I want that product to be returned in the dataset, but not if it only belongs to either "Ruby on Rails" or "Shirts"
(INNER) JOIN : Returns records that have matching values in both tables. LEFT (OUTER) JOIN : Returns all records from the left table, and the matched records from the right table. RIGHT (OUTER) JOIN : Returns all records from the right table, and the matched records from the left table.
How to Select All Records from One Table That Do Not Exist in Another Table in SQL? We can get the records in one table that doesn't exist in another table by using NOT IN or NOT EXISTS with the subqueries including the other table in the subqueries.
The second way to find non-matching records between tables is to use NOT in conjunction with the IN operator. The IN operator allows you to specify multiple values in a WHERE clause, much like a string of ORs chained together.
I had this problem a while back, thankfully there is a nice solution.
def self.has_taxons(taxons)
id = arel_table[:id]
Product.joins(:taxons).where(taxons: { name: taxons }).group(id).having(id.count.eq(taxons.size))
end
This answer from @samuel is exactly what I was looking for, but I wanted to be able to still supply keywords to the search while filtering by Taxon1 AND Taxon2 and TaxonN. I don't ever need to do a Taxon1 OR Taxon2 search, so I made the following customizations. There might be a less hacky way to do this, but it works great for me.
I added a new product scope in /app/models/spree/product_decorator.rb
Spree::Product.class_eval do
add_search_scope :in_all_taxons do |*taxons|
taxons = get_taxons(taxons)
id = arel_table[:id]
joins(:taxons).where(spree_taxons: { id: taxons }).group(id).having(id.count.eq(taxons.size))
end
end
Then used the new scope by adding it to /app/models/spree/base_decorator.rb
Spree::Core::Search::Base.class_eval do
def get_base_scope
base_scope = Spree::Product.active
base_scope = base_scope.in_all_taxons(taxon) unless taxon.blank?
base_scope = get_products_conditions_for(base_scope, keywords)
base_scope = add_search_scopes(base_scope)
base_scope
end
end
Now I can use the standard search helper to retrieve products (which means I can still supply keywords, etc along with the multiple taxons):
# taxon_ids is an array of taxon ids
@searcher = build_searcher(params.merge(:taxon => taxon_ids))
@products = @searcher.retrieve_products
This works for me and felt pretty painless. However, I'm open to better options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With