【问题标题】:ActiveRecord includes doesn't include all parent records?ActiveRecord 包括不包括所有父记录?
【发布时间】:2018-05-26 07:13:13
【问题描述】:

我想获取属于“饮料”类别的所有客户及其产品。

Here are my relations:

Customer has_many orders
Order has_many products through order_details
Product has_many Orders through order_details
Product belongs to Category

我试过这个,但它并没有给所有客户。它只为订购饮料的客户提供服务。

 Customer.includes(orders: {products: :category}).where(categories: { category_name: "Beverages"})

注意:我不想过滤客户记录。我需要对产品应用过滤器,因为在这种情况下加载所有记录是无用的。

【问题讨论】:

  • 我从未尝试过这么长的链条,但也许:Category.where(category_name: "Beverages").first.products.orders.customers
  • @iGian 这不会急于加载记录。此外,我们需要所有客户,而不是拥有饮料类别的客户。
  • 是的,对不起,我读的太匆忙了,真丢脸。我错过了筑巢。我现在仔细阅读,您的代码可以完美地吸引订购饮料的客户。但我不清楚您真正想要实现什么“..它并没有给所有客户。” 您还打算获取每个客户购买的同类别饮料的其他产品吗?

标签: ruby-on-rails ruby-on-rails-4 activerecord rails-activerecord


【解决方案1】:

很遗憾,Active Record 没有 API 来执行此操作。

如果你只需要几个已知的类别,你可以通过添加自定义关联来实现:

class Customer ..
  has_many :beverage_orders, -> { includes(products: :categories).where(categories: { category_name: "Beverages" }) }, class_name: "Order"

(然后是Customer.includes(:beverage_orders)

如果您需要支持任意类别,您的选择会变得更糟......如果客户数量足够少,我会考虑跳过包含并接受 N+1 查询。

否则,我可以建议的下一个最糟糕的选择是自己进行查询:

customers = Customer.all # or whatever
beverage_orders = Order.includes(products: :categories).
                        where(categories: { category_name: "Beverages" }).
                        where(customer_id: customers).
                        group_by(&:customer_id)

customers.each do |customer|
  puts "#{customer.name} ordered #{beverage_orders[customer.id].size} beverages"
  # i.e., use `beverage_orders[customer.id]` instead of `customer.orders`
end

【讨论】:

  • 谢谢 - 自定义关联选项非常好。但我需要传递参数以使最有用。我知道我们没有这样的规定,但正在寻找替代方案。
【解决方案2】:

如果您需要过滤记录,为什么要使用左连接? 试试

Customer.joins(:orders).where(order: { product_id: Category.find_by(name: 'Beverages').products.pluck(:id) })

更新: 由于where 子句将过滤基本记录,因此它应该过滤您的Customer 模型,您需要自定义joins 以选择合适的产品而不是过滤客户。

category_id = Category.find_by(name: "Beverages").id
Customer.joins(orders).joins("LEFT OUTER JOIN products ON products.id = orders.product_id AND products.category_id = #{category_id}")

【讨论】:

  • left_outer_joins 只是表现为包含。我的意思是两个结果集是相同的。我知道联接有不同的目的,它的作用类似于内部联接。
  • 如果需要过滤记录,为什么不使用内连接? Customer.joind(:orders).where(order: { product_id: Category.find_by(name: 'Beverages').products.pluck(:id) })
  • 我需要所有客户,但只预加载特定类别下的产品。例如,如果我有两个客户,一个点了饮料,另一个点了不同的产品。然后我需要与第一个相关的客户和产品。希望这是有道理的。
  • 据我了解,您需要加入所有产品,但是当产品不符合过滤器时,它应该是 nil 在这种情况下,您需要自定义您的 join 请求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-14
  • 2015-10-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多