为了学习Ruby on Rails,我正在编写一个Web应用程序,用于根据迄今为止的表现对锦标赛中的团队进行排序。
复杂的是,我希望每个锦标赛组织者(系统用户)能够以任意顺序使用各种指标。
表示为SQL(我的背景)我希望用户1能够选择:
ORDER BY
METRIC1
,METRIC2
,METRIC3
虽然用户2可以选择:
ORDER BY
METRIC2
,METRIC3
,METRIC1
我如何接受此用户输入并使用它在Team表上创建查询?
编辑1 忽略提及(抱歉)指标本身是即时计算的。目前它们是实例方法(例如@team.metric1
等)。到目前为止,我所做的失败尝试都涉及尝试将用户字符串转换为看似错误的方法名称(而且我还没有能够让它工作)。
编辑2 teams_controller.rb中的一些示例代码:
class Team < ApplicationRecord
belongs_to :tournament
has_many :matches
def score_for
matches.sum(:score_for)
end
def score_diff
matches.sum(:score_for) - matches.sum(:score_against)
end
end
答案 0 :(得分:0)
ActiveRecord允许将多个参数传递给order方法。所以你可以这样做:
Team.order(:metric2, :metric3, metric1: :desc)
另一个选项是您也可以使用ActiveRecord动态构建查询。 ActiveRecord查询被懒惰地评估,因此在您调用需要加载记录的操作之前,SQL不会被执行。
例如,你可以像这样在Team上构建一个范围:
class Team < ApplicationRecord
scope :custom_order, lambda { |sorting_order|
sorting_order.each do |metric|
order(metric)
end
}
end
然后,您只需要按照您希望执行的order by子句的顺序输入属性集合。例如:
Team.custom_order([:metric2, :metric3, :metric1])
答案 1 :(得分:0)
一个可行但可能很糟糕的解决方案:
class Tournament < ApplicationRecord
has_many :teams
serialize :tiebreaker, Array
TIEBREAKER_WHITELIST = %w[score opponent_score possession].freeze
def sorted_teams
list = teams.shuffle
(TIEBREAKER_WHITELIST & tiebreaker).reverse.each do |metric|
list = list.sort_by { |team| [team.send(metric), list.find_index(team)] }
end
list.reverse
end
end
每个tournament
有很多teams
。 tournament
实例具有一个称为tiebreaker
的序列化字段。它包含一个类似于["score", "possession"]
的字符串数组,其中每个字符串都与team
上的公共实例方法的名称匹配。每个方法都返回一个数字。
tiebreaker
字段的优先级从高到低,因此对于上面的示例,我只希望possession
影响具有相等score
的团队的排序。
list = teams.shuffle
-如果团队与以下所有决胜局并列,则此列表会随机列出。
(TIEBREAKER_WHITELIST & tiebreaker)
-仅返回同时出现在tiebreaker
字段和白名单常量中的字符串,以防止最终用户运行任意方法。
.reverse.each do |metric|
-这会颠倒指标数组,以便该列表首先按照最低优先级指标进行排序。
[team.send(metric), list.find_index(team)]
-这是每个指标的排序方式。 send
将字符串转换为方法调用。我发现find_index
对于保留先前排序的排序顺序很有必要。也就是说,如果我第一次为possession
排序,这将保留具有相同score
的团队的订单。
list.reverse
-反转列表然后返回。这是因为我想让得分更高/拥有更多人的团队首先出现在我的名单上,并且sort_by
排名上升。
我希望一些度量按升序(opponent_score
)排序,而其他度量按降序(score
排序,因此我在相应的方法中进行了处理,例如返回opponent_score
的负值。
我对解决方案并不完全满意,但是它确实可以正常工作!