高级ActiveRecord查询:通过嵌套关联筛选总和

时间:2016-08-25 18:45:28

标签: ruby-on-rails postgresql activerecord

想象一下,我是一个具有以下数据库结构的电子商务网站:

  • 订单has_many OrderItems
  • OrderItem belongs_to Product
  • order_items表有一列,数量。
  • 产品表有一个 专栏,价格。

我想在我的订单模型中创建一个能够按总价格过滤订单的范围。这是订单的总和(order_item数量* order_item'产品价格)。

预期用途:

#Get orders that have a total cost greater than 10
Order.filter_cost('gt', 10)

这是我到目前为止所形成的,但它似乎并没有完全奏效。我认为我不应该分组,但如果我不使用分组,则会出错。使用以下范围,我得到一个响应,但返回的集合不准确。

scope :filter_cost, (lambda do |*args|
  if args[0] && %w(gt lt et).include?(args[0]) && args[1]
    operator_map = { 'gt' => '>', 'lt' => '<', 'et' => '=' }
    joins(order_items: :product).group('orders.id').group('order_items.id').group('products.id').having("SUM(products.price * order_items.quantity) #{operator_map[args[0]]} ?", args[1].to_f)
  end
end)

我可以就我的查询有什么问题获得一些指导吗?

1 个答案:

答案 0 :(得分:0)

def self.total_cost(direction, amount)
  select(orders.*, SUM(products.price * order_items.quantity) AS total_price).
  joins(order_items: :product).
  where(SUM(products.price * order_items.quantity) #{direction} #{amount})
end

现在您可以像Order.total_cost(:>, 10)一样使用它 附: - 您甚至可以通过将其作为此查询返回的实例上的方法来访问total_amount

Order.total_cost(:>, 10).first.total_cost一样(返回该项目的总费用)

我认为使用符号代替gtlt会更好,因为对于任何人来说这个方法更直接+你不必做不必要的映射。

可能不是最优雅的解决方案,但它确实有效。