如何获取所有具有共同状态的订单?

How to get orders all of which have a common status?

我有一个订单和订单项目的实施。即每个订单都有一定数量的商品。

每个订单项都有一个由枚举“控制”的状态字段。

enum status: {
  opened: 0,
  checked: 1,
  spoiled: 2
}, _prefix: :status

你能告诉我如何只获取那些所有项目都处于“已检查”状态的订单吗?

我被困在这个:

left_outer_joins(:orders_items)
  .where(
    orders_items: {
      status: :checked
    }
  )
  .where(orders_items: { id: nil })
  # OR .where.missing(:orders_items)

或者:

left_outer_joins(:orders_items)
  .where(
    orders_items: {
      status: %i[
        opened
        spoiled
      ]
    }
  )
  .having('COUNT(orders_items.id) = 0')
  .group('orders.id')

据我了解,我需要以某种方式获得以下订单:

但是我不明白这是怎么做到的...希望得到你的帮助

有很多方法可以做到这一点。最简单的方法是 WHERE NOT IN 子查询:

Order.where.not(
  id: Order.joins(:order_items)
           .where.not(order_items: { status: :checked })
)

您也可以使用 NOT EXIST:

Order.where(
  OrderItem.select(:id)
      .where(
          # order_items.version_id = orders.id
          OrderItem.arel_table[:order_id].eq(Order.arel_table[:id])
       )
       .where.not(status: :checked)
       .arel
       .exists.not
)

这些都依赖于这样一个事实,即具有任何 order_item 而没有 status = 1 的订单不符合条件。如果你想要更接近段落 The number of items with the "checked" status is equal to the number of all items in the order. 的东西,你可以使用子查询:

orders, order_items =  Order.arel_table, OrderItem.arel_table
subquery = OrderItem.select(order_items[:*].count)
                     .where(order_items[:order_id].eq(order[:id]))
                
Order.select(
        orders[*],
        subquery.arel.as('order_items_count'), # can be replaced with a counter-cache
        subquery.where(status: :checked).arel.as('checked_count')
     )
     .where(orders[:order_items_count].eq(orders[:not_checked_count])
     

或聚合:

orders, order_items = Order.arel_table, OrderItem.arel_table
subquery = OrderItem.select(order_items[:*].count)
                     .where(order_items[:order_id].eq(order[:id]))
                
Order
    .left_joins(:order_items)
    .select(
        orders[*],
        subquery.where(status: :checked).arel.as('checked_count')
     )
     .group(:id)
     .having(
       order_items[:*].eq(orders[:checked_count]) 
     )