Ruby on rails 提取Rails中满足模型函数的记录

Ruby on rails 提取Rails中满足模型函数的记录,ruby-on-rails,activerecord,arel,Ruby On Rails,Activerecord,Arel,在名为CashTransaction的模型中,我有以下方法 def is_refundable? self.amount > self.total_refunded_amount end def total_refunded_amount self.refunds.sum(:amount) end 现在我需要提取满足上述函数的所有记录,即返回true的记录 我通过使用以下语句实现了这一点: CashTransaction.all.map{| x | x如果x.u可退款?}

在名为
CashTransaction
的模型中,我有以下方法

def is_refundable?
    self.amount > self.total_refunded_amount
end

def total_refunded_amount
   self.refunds.sum(:amount)
end
现在我需要提取满足上述函数的所有记录,即返回
true
的记录

我通过使用以下语句实现了这一点:

CashTransaction.all.map{| x | x如果x.u可退款?}

但是结果是一个
数组
。我正在寻找
ActiveRecord\u Relation
对象,因为我需要对结果执行
join

我觉得我在这里遗漏了一些东西,因为它看起来并不那么难。不管怎样,它让我陷入困境。建设性的建议会很好

注意:Just
amount
是一个
CashTransaction

编辑

下面的
SQL
执行此任务。如果我可以将其更改为
ORM
,它仍然可以完成工作

SELECT `cash_transactions`.* FROM `cash_transactions` INNER JOIN `refunds` ON `refunds`.`cash_transaction_id` = `cash_transactions`.`id` WHERE (cash_transactions.amount > (SELECT SUM(`amount`) FROM `refunds` WHERE refunds.cash_transaction_id = cash_transactions.id GROUP BY `cash_transaction_id`));
共享进度

我通过以下方式使其工作:

CashTransaction
  .joins(:refunds)
  .group('cash_transactions.id')
  .having('cash_transactions.amount > sum(refunds.amount)')
但我实际上看到的是:

CashTransaction.joins(:refunds).where(is_refundable? : true)
哪里可以退款?是一个模型函数。起初我认为设置
是可以退款的?
因为
attr\u accesor
会起作用。但我错了

只是想一想,使用Arel可以优雅地解决问题吗?有两种选择

1) Finish,您已经开始的内容(在处理大量数据时效率极低,因为在处理之前所有数据都会被放入内存中):

因此,获取ID:

ids = CashTransaction.all.map(&:is_refundable?).map(&:id)
现在,要获取ActiveRecord关系:

CashTransaction.where(id: ids) # will return a relation
2) 将计算移动到SQL:

CashTransaction.where('amount > total_refunded_amount')
第二种选择是尽一切可能更快、更高效

当您处理数据库时,尝试在数据库级别上处理它,尽可能少地使用Ruby

编辑 根据编辑的问题,您将如何实现预期结果:

CashTransaction.joins(:refunds).where('amount > SUM(refunds.amount)')
编辑#2 至于你的更新问题-我真的不明白,为什么你把
作为一个实例方法,可以在查询中使用,这在AR中基本上是不可能的,但是

我的建议是创建一个可退款的范围:

scope :is_refundable, -> { CashTransaction
  .joins(:refunds)
  .group('cash_transactions.id')
  .having('cash_transactions.amount > sum(refunds.amount)')
}
现在,它有以下几种简短的表示法:

CashTransaction.is_refundable
比目标更短更清晰

CashTransaction.where('is_refundable = ?', true)

您可以这样做:

cash_transactions = CashTransaction.all.map { |x| x if x.is_refundable? } # Array
CashTransaction.where(id: cash_transactions.map(&:id)) # ActiveRecord_Relation
但是,正如其他回答者也提到的那样,这是一种有效的方法

如果
金额
总退款金额
是数据库中
现金交易
表的列,则可以使用SQL执行此操作,这将更加高效和高效:

CashTransaction.where('amount > total_refunded_amount')

但是,如果
amount
total\u returned\u amount
不是数据库中的实际列,则不能这样做。然后,我想你应该用另一种方法来做,这比使用原始SQL更有效。

我认为当使用回调更新
现金交易和他的退款(假定
有很多
?)时,你应该预先计算可退款结果(在新列中):

class CashTransaction
  before_save :update_is_refundable
  def update_is_refundable
    is_refundable = amount > total_refunded_amount
  end
  def total_refunded_amount
     self.refunds.sum(:amount)
  end
end

class Refund
  belongs_to :cash_transaction
  after_save :update_cash_transaction_is_refundable
  def update_cash_transaction_is_refundable
    cash_transaction.update_is_refundable
    cash_transaction.save!
  end
end
注意:以上代码必须进行优化,以防止出现某些查询

您可以查询“是否可退款”列:

CashTransaction.where(is_refundable: true)

我认为对两个查询而不是一个连接表(类似这样的)执行此操作是不错的

def refundable
  where('amount < ?', total_refunded_amount)
end
def可退款
其中('金额<?',总退款金额)
结束

这将执行一个求和查询,然后在第二个查询中使用求和,当表变大时,您可能会发现这比在数据库中执行联接要快。

我编辑了这个问题
total\u returned\u amount
不是表中的一列。@abhinavmsra如果
total\u returned\u amount
不是数据库中的实际列,则不能使用SQL执行此操作。请看我的答案。@abhinavmsra那么你希望我现在做什么?我如何向您提供更多信息?我已经展示了如何用我们的方式来做。如果您想查看SQL解决方案,请向我展示
total\u returned\u amount
方法,可能在SQL中可以进行计算。@Andreydeneko我实际上在寻找类似
CashTransaction的内容。其中('is\u returnable=?',true)
,如果可能的话cource@AndreyDeineko,我找到了一份工作。你能看看我的编辑并分享你的观点吗?嘿,我的回答有用吗?你让它工作了吗?
def refundable
  where('amount < ?', total_refunded_amount)
end