关于最后has_many关系属性的ActiveRecord排序模型

时间:2017-05-02 01:37:40

标签: ruby-on-rails ruby activerecord

我已经为此寻找了一段时间......我无法找到优雅的解决方案。我有loansloans has_many :decisionsdecisions有一个我关心的属性,称为risk_rating

我希望根据最近的决定(基于created_at,按常规)排序loans,但是risk_rating

Loan.includes(:decisions).references(:decisions).order('decisions.risk_rating DESC')不起作用......

我想要贷款......按照他们最近的决定风险排序。这似乎应该比它更容易。

我目前正在这样的数据库之外这样做,但它正在咀嚼时间和记忆:

Loan.all.sort do |x,y| 
  x.decisions.last.try(:risk_rating).to_f <=> y.decisions.last.try(:risk_rating).to_f
end

我希望展示我对拟议答案的表现以及不准确的表现......

Benchmark.bm do |x|
  x.report{ Loan.joins('LEFT JOIN decisions ON decisions.loan_id = loans.id').group('loans.id').order('MAX(decisions.risk_rating) DESC').limit(10).map{|l| l.decisions.last.try(:risk_rating)} }
end

    user     system      total        real
0.020000   0.000000   0.020000 ( 20.573096)

=> [0.936775, 0.934465, 0.932088, 0.922352, 0.921882, 0.794724, 0.919432, 0.918385, 0.916952, 0.914938]

订单不对。那0.794724不合适。

就此而言......我只是在建议的答案中看到一个属性。我没有看到连接= /

2 个答案:

答案 0 :(得分:2)

好吧,看起来我今晚工作到很晚,因为我忍不住跳了进去:

class Loan < ApplicationRecord
  has_many :decisions
  has_one :latest_decision, -> { merge(Decision.latest) }, class_name: 'Decision'
end

class Decision < ApplicationRecord
  belongs_to :loan

  def latest
    t1 = arel_table
    t2 = arel_table.alias('t2')

    # Self join based on `loan_id` prefer latest `created_at`
    join_on = t1[:loan_id].eq(t2[:loan_id]).and(
      t1[:created_at].lt(t2[:created_at]))

    where(t2[:loan_id].eq(nil)).joins(
      t1.create_join(t2, t1.create_on(join_condition), Arel::Nodes::OuterJoin)
    )
  end
end

Loan.includes(:latest_decision)

这不排序,只提供每笔贷款的最新决定。由于表别名,抛出引用order的{​​{1}}会使事情变得混乱。我现在没有时间解决这个问题,但我敢打赌,如果你查看Arel上的一些优秀资源以及如何将它与ActiveRecord一起使用,你可以弄明白。我真的很喜欢this one

答案 1 :(得分:1)

首先让我们编写sql-query,它将选择必要的数据。 SO包含一个可能有帮助的问题:Select most recent row with GROUP BY in MySQL。我最好的版本:

SELECT loans.*
FROM loans
LEFT JOIN (
  SELECT loan_id, MAX(id) as id
  FROM decisions
  GROUP BY loan_id) d ON d.loan_id = loans.id
LEFT JOIN decisions ON decisions.id = d.id
ORDER BY decisions.risk_rating DESC

此代码假设MAX(id)给出了组中最近一行的id

您可以通过此Rails代码执行相同的查询:

sub_query = 
  Decision.select('loan_id, MAX(id) as id').
    group(:loan_id).to_sql

Loan.
  joins("LEFT JOIN (#{sub_query}) d ON d.loan_id = loans.id").
  joins("LEFT JOIN decisions ON decisions.id = d.id").
  order("decisions.risk_rating DESC")

不幸的是,我手头没有MySQL,我无法试用这段代码。希望它能奏效。