Reputation: 2411
I can't believe I haven't come up with a good solution to this common problem after 5 years of Rails-programming. Also, I assume there are 100's of answers to this particular question but I don't know the definitions (relations? associations? etc) to search it well. So here we go:
I have two models: Categories and Products and a CategoryProductRelation-model to tie them together.
Category-model:
has_many :category_product_relations
has_many :products, :through => :category_product_relations
CategoryProductRelation-model:
has_many :products
has_many :categories
Category typically has some 1000 products.
Very often I need to do searches like "All products in the Food type of categories" i.e. Categories: 'Fruits' (1), 'Cereal' (2), 'Meat'(3).
Basically, what I would like to do is something like Category.where(:id => [1,2,3]).products
which obviously doesn't work.
What I often do is something like this:
products = Product.where(:id => CategoryProductRelation.where(:category_id => [1,2,3]).pluck(:product_id))
which works, mostly fine as well. However, this is very slow if the amount of products is large resulting in 400ms queries!
There has to be a smarter way, right? This feels like Rails 101 but I can't figure it out in a way that is simple and fast!
Lastly, iterating through Category like:
Category.where(:id => [1,2,3]).each do |category|
category.products
#...and so on
end
seems to be equally slow.
Is there a magic bullet to this common problem?
Upvotes: 1
Views: 93
Reputation: 3407
What about merge
?
Product.joins(:categories).merge(Category.where(:id => [1,2,3])).pluck(:id)
http://apidock.com/rails/ActiveRecord/SpawnMethods/merge
Merges in the conditions from other, if other is an ActiveRecord::Relation
Upvotes: 1
Reputation: 3880
The magic word is database join. To use it, you need to think a little in the opposite direction. You ask for products with conditions on a joined table.
http://guides.rubyonrails.org/active_record_querying.html#joining-tables
specificly look for: "12.3. 12.3 Specifying Conditions on the Joined Tables"
So it is something like
Products.joins(:category_product_relations).
where( category_product_relations: {category_id: [1,2,3]} )
Upvotes: 1
Reputation: 1668
I am not sure if this works in your case. I use mongoid so i do something like this.
Product.where(:category_id.in => [1,2,3])
This gives me all the products with the categories whose ids are 1,2,3.
For Product model
belongs_to :category
For Category model
has_many :products
I hope you get an idea.
For two queries you can do something like this.
category_product_relations_ids = CategoryProductRelation.where(:category_ids => [1,2,3]).pluck(:id)
Product.where(:category_product_relation_id => category_product_relations_ids)
Upvotes: 1