简体   繁体   中英

Rails Join based on multiple models

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:

  1. I created the product.
  2. I bought the product.
  3. The product is free, and I have "starred" or "watched" it.

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?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM