在Rails 6.1中,假设有两个模型:
PeriodicJob: has_many :executions
Execution
,state
字段为succeeded
或failed
我想运行查询:
给我最后一次(最高ID)执行状态为
succeed
的所有PeriodicJobs
。
一个可能的解决方案是使用带有子查询的原始SQL,正如在另一个Stackoverflow问题中指出的那样:在最后一个has_many关联中搜索具有特定值的实例
然而,对于这样一个简单的英语问题,这似乎过于复杂的代码。考虑到Rails的强大功能,我希望看到这样的内容:PeriodicJob.joins(:executions).where(cool_trick_im_yet_unaware_of_to_get_last_ordered_by_id_execution: { state: 'succeeded' }
这样的东西在Rails中存在吗?它将如何应用到这个例子中?
优化读取的一种方法是设置一个单独的外键列和关联作为快捷键。到最近一次执行:
class AddLatestExecutionToProducts < ActiveRecord::Migration[6.0]
def change
add_reference :latest_execution, :execution
end
end
class PeriodicJob < ApplicationRecord
has_many :executions,
after_add: :set_latest_execution
belongs_to :latest_execution,
optional: true,
class_name: 'Execution'
private
def set_latest_execution(execution)
update_attribute(:latest_execution_id, execution.id)
end
end
这允许您执行PeriodicJob.eager_load(:latest_execution)
并避免N+1查询和从执行表中加载所有记录。如果每个周期作业有很多执行,这一点尤其重要。
成本是每次创建一个执行时都需要一个额外的写查询。
如果您想将此限制为仅包含最近的成功/失败,您可以添加两列:
class AddLatestExecutionToProducts < ActiveRecord::Migration[6.0]
def change
add_reference :latest_successful_execution, :execution
add_reference :latest_failed_execution, :execution
end
end
class Execution < ApplicationRecord
enum state: {
succeeded: 'succeeded',
failed: 'failed'
}
end
class PeriodicJob < ApplicationRecord
has_many :executions,
after_add: :set_latest_execution
belongs_to :latest_successful_execution,
optional: true,
class_name: 'Execution'
belongs_to :latest_failed_execution,
optional: true,
class_name: 'Execution'
private
def set_latest_execution(execution)
if execution.succeeded?
update_attribute(:latest_successful_execution_id, execution.id)
else
update_attribute(:latest_failed_execution_id, execution.id)
end
end
end
如果使用的是常规数字id,则可以为关系添加条件。这将在模型上设置一个自定义关系,该关系将返回成功的最新执行。有一个宏将限制'集合'为一个。
has_one :current_succeeded_execution, -> { where(state: "succeeded").reorder(id: :desc) }, class_name: 'Execution'
如果你使用uuid,我会在这些模型上设置一个默认作用域,以按asc创建的顺序排序,以确保第一个总是最老的,即第一个创建的。然后你可以重新排序以获取最新的一个
has_one :current_succeeded_execution, -> { where(state: "succeeded").reorder(created_at: :desc) }, class_name: 'Execution'