Reputation: 4433
I have four models in question: User
, Product
, Purchase
, and WatchedProduct
.
I am trying to get a list of products that meet one of the following criteria:
This is what I have so far:
class User < ActiveRecord::Base
...
def special_products
product_ids = []
# products I created
my_products = Product.where(:user_id => self.id).pluck(:id)
# products I purchased
my_purchases = Purchase.where(:buyer_id => self.id).pluck(:product_id)
# free products I watched
my_watched = WatchedProduct.where(:user_id =>self.id).joins(:product).where(products: { price: 0 }).pluck(:product_id)
product_ids.append(my_products)
product_ids.append(my_purchases)
product_ids.append(my_watched)
product_ids # yields something like this => [[1, 2], [], [2]]
# i guess at this point i'd reduce the ids, then look them up again...
product_ids.flatten!
product_ids & product_ids
products = []
product_ids.each do |id|
products.append(Product.find(id))
end
products
end
end
What I am trying to do is get a list of Product
models, not a list of IDs or a list of ActiveRecord Relations. I am very new to joins, but is there a way to do all of this in a single join instead of 3 queries, reduce, and re lookup?
Upvotes: 1
Views: 390
Reputation: 1034
Although i am not sure about your model associations but yes you can do all these things in single query somehow like this:
Product.joins([{:user => :watched_products}, :buyer, :purchases]).where(["users.id = :current_buyer && buyers.id = :current_buyer && watched_products.user_id = :current_buyer && purchases.buyer_id = :current_buyer, products.price = 0", :current_buyer => self.id])
I am assuming
Product belongs_to user and buyer
Product has_many purchases
User has_many watched_products
Upvotes: 0
Reputation: 42919
First I like adding few scopes
class Product < ActiveRecord::Base
scope :free, -> { where(price: 0) }
scope :bought_or_created_by, lambda do |user_id|
where('user_id = :id OR buyer_id = :id', id: user_id)
end
end
class WatchedProduct < ActiveRecord::Base
scope :by_user, ->(user_id) { where(user_id: user_id) }
end
Then the queries
special_products = (Product.bought_or_created_by(id) + Product.joins(:watched_products).free.merge(WatchedProduct.by_user(id)).uniq
This will return an array of unique products using 2 queries.
Upvotes: 1