【问题标题】:Combining a 'where' clause with a method将“where”子句与方法相结合
【发布时间】:2024-01-23 07:58:01
【问题描述】:

在我的应用程序中,Team belongs_to :hunt。一旦Hunt 得到确认,那么所有相关的团队都准备好了。

这是我的team.rb 文件中的一个示例,我使用ready? 方法检查team.hunt 是否被确认。

#team.rb

def ready?
  hunt.confirmed? ? true : false
end

我希望在team.rb 文件中有一个范围,以便我可以调用Teams.all.ready.count 来显示准备好的团队数量。

如何在不向我的数据库添加任何内容或遍历数组等的情况下编写方法或范围来实现上述行为?

【问题讨论】:

  • 您使用哪个版本的导轨?
  • @guitarman 当前为 Rails 4.2.5.1
  • @MikeHolford confirmed?hunts 数据库表上的列吗?
  • @aBadAssCowboy 在这种情况下是的,但我很想知道一个解决方案,如果它不是太因为这对我来说是一个常见问题

标签: ruby-on-rails ruby scope


【解决方案1】:

更新:

感谢@TomLord 的洞察力,您宁愿执行下面的解决方案 1 而不是解决方案 2。另外,添加示例 SQL 以显示比较。

解决方案 1

class Team < ApplicationRecord
  belongs_to :hunt
  scope :ready, -> { joins(:hunt).where(hunts: { confirmed: true }) }
end

用法:

Team.ready # or: Team.all.ready
# SELECT  "teams".* FROM "teams" INNER JOIN "hunts" ON "hunts"."id" = "teams"."hunt_id" WHERE "hunts"."confirmed" = ? LIMIT ?  [["confirmed", "t"], ["LIMIT", 11]]

或者,解决方案 2

class Team < ApplicationRecord
  belongs_to :hunt
end

class Hunt < ApplicationRecord
  scope :confirmed, -> { where(confirmed: true) }
end

用法:

# you can also move the logic below as a method/scope inside `Team` model (i.e. as `ready` method/scope)

# Example 1 (using JOINS):
Team.joins(:hunt).where(hunts: { id: Hunt.confirmed })
# SELECT  "teams".* FROM "teams" INNER JOIN "hunts" ON "hunts"."id" = "teams"."hunt_id" WHERE "hunts"."id" IN (SELECT "hunts"."id" FROM "hunts" WHERE "hunts"."confirmed" = ?) LIMIT ?  [["confirmed", "t"], ["LIMIT", 11]]

# Example 2 (same as Example 1 above but faster and more efficient):
Team.where(hunt_id: Hunt.confirmed)
# SELECT  "teams".* FROM "teams" WHERE "teams"."hunt_id" IN (SELECT "hunts"."id" FROM "hunts" WHERE "hunts"."confirmed" = ?) LIMIT ?  [["confirmed", "t"], ["LIMIT", 11]]

【讨论】:

  • 强烈更喜欢上面的选项 1。选项 2 性能较差,并且无法扩展。 (想想在拥有数百万 Hunt 记录的大型应用程序中会发生什么!)
  • @TomLord 非常好。我会更新我的答案 :) 如果范围很复杂,有时我会使用选项 2,并且我宁愿希望范围的逻辑是模块化的,方法是将它们作为范围放在相应的模型中,但承认这会影响性能。尽管我不知道它会“极大地”影响性能/内存使用,正如您刚才所说。感谢您提供此信息!我将进行一些测试以检查此问题的潜在影响 :) 哦,我想我可以进一步优化解决方案 2。我正在更新我的答案。
  • 性能问题只有在大型系统上才会真正发挥作用。 (我的日常工作是处理具有数百万活跃用户和数十亿数据库记录的应用程序!!)对于个人/小型项目,您可能不会注意到差异,但仍然需要注意这一点。
  • @TomLord 啊,我明白了!我会在大数据应用程序中充分考虑这一点;谢谢你! :)
【解决方案2】:

解决方案:如果Hunts#confirmed 是数据库列:

class Team < ApplicationRecord
  belongs_to :hunt
  scope :ready, -> { joins(:hunt).where(hunts: { confirmed: true }) }
end

在这种情况下,Team.ready 将返回 ActiveRecord::Relation

解决方案:如果Hunts#confirmed? 不是数据库列:

class Team < ApplicationRecord
  belongs_to :hunt
  scope :ready, -> { includes(:hunts).select(&:ready?) }
end

在这种情况下,Team.ready 将返回 Array

您需要注意,第二个解决方案是在第一个解决方案执行数据库查询时循环调用每个团队记录上的 ready?。首先是更高效。

【讨论】: