简体   繁体   English

Rails 查询:根据最新的子记录获取所有父记录

[英]Rails query: get all parent records based on latest child records

An order has_many order_events.订单has_many order_events。

An order_event is a record of the state of the order, eg, pending_approval , confirmed , etc. order_event是订单状态的记录,例如, pending_approvalconfirmed等。

Changes to an order are tracked by creating order_events .通过创建order_events来跟踪对订单的更改。

I usually get the current state of an order by doing something like: order.order_events.last.try(:state) .我通常通过执行以下操作来获取订单的当前状态: order.order_events.last.try(:state)

I would like a query to get all of the orders with a given current state, eg, all orders where the current state is cancelable .我想要查询以获取具有给定当前状态的所有订单,例如,当前状态为cancelable所有订单。

I initially had a scope in the OrderEvent model:我最初在OrderEvent模型中有一个范围:

scope :cancelable, -> { where('state = ? OR state = ?', 'pending_approval', 'pending_confirmation') }

and then a scope in the Order model:然后是Order模型中的范围:

scope :with_dates_and_current_state_cancelable, -> { with_dates.joins(:order_events).merge(OrderEvent.cancelable) }

and simply used the latter for other purposes.并简单地将后者用于其他目的。

The problem here is that it returns all orders that are currently or have in the past satisfied the condition.这里的问题是它返回当前或过去满足条件的所有订单。

What is the best way to get all of the orders that currently satisfy the condition?获取当前满足条件的所有订单的最佳方法是什么?

I ended up using a query like this:我最终使用了这样的查询:

scope :with_dates_and_current_state_cancelable, -> {
  with_dates
  .joins(:order_events)
  .where('order_events.created_at = (SELECT MAX(order_events.created_at) FROM order_events WHERE order_events.order_id = orders.id)')
  .where('order_events.state = ? OR order_events.state = ?', 'pending_approval', 'pending_confirmation')
  .group('orders.id')
}

A bit hard to read, but it seems to work.有点难以阅读,但它似乎有效。

A classic solution here would be to use Rails enum .这里的一个经典解决方案是使用 Rails enum

Add this to your order model:将此添加到您的订单模型中:

class Order
  enum status: [ :pending_approval, :confirmed, etc.. ]
  ...
end

The status can be changed by doing the following:可以通过执行以下操作来更改状态:

# order.update! status: 0
order.pending_approval!
order.pending_approval? # => true
order.status  # => "pending_approval"

No need for the order_events model.不需要order_events模型。

To query all the orders that are pending approval:查询所有待审批的订单:

Order.where(status: :pending_approval)

Edit: Alternate solution when order_event has necessary columns.编辑:当order_event有必要的列时的替代解决方案。

Add a column to the order_event called archived which can either be set to 1 or 0. Set the default scope in the order_event model to this:向名为archivedorder_event添加一列,该列可以设置为 1 或 0。将order_event模型中的默认范围设置为:

default_cope where(:archived => 0)

Assuming 0 is not archived.假设 0 未归档。

Now, when you create a new order event set the old event to 1.现在,当您创建新订单事件时,将旧事件设置为 1。

old_event = OrderEvent.find(params[:order_id])
old_event.update(archived: 1)
new_event = OrderEvent.create(...archived: 0)

Whenever you query for pending review like so:每当您像这样查询待审核时:

OrderEvent.where(:status => pending_approval)

Only events that are not archived will be shown.仅显示未存档的事件。

I think I figured out a query that might work.我想我想出了一个可能有效的查询。 I didn't turn it in to ActiveRecord methods, but here it is:我没有将它转换为ActiveRecord方法,但它是:

SELECT t.order_id
FROM
  (SELECT MAX(created_at) AS created, order_id
  FROM order_events
  GROUP BY order_id) as t
INNER JOIN order_events
ON t.order_id = order_events.order_id AND
   t.created = order_events.created_at
WHERE order_events.state = 'whatever_state_you_want'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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