基于枚举值的自定义顺序
Custom order based on Enum values
我为角色定义了枚举:
enum role: {ordinary: 0, manager: 1, admin: 2}
我想按以下顺序订购一组对象:
admin (first all admins)
ordinary (then all ordinaries)
manager (and lastly all managers)
这可能吗?
解决方案:
class YourModel < ActiveRecord::Base
ROLE_ORDERS = [2, 0, 1]
scope :order_by_role, -> {
order_by = ['CASE']
ROLE_ORDERS.each_with_index do |role, index|
order_by << "WHEN role=#{role} THEN #{index}"
end
order_by << 'END'
order(order_by.join(' '))
}
end
那么你的查询就很简单了:
YourModel.order_by_role
生成的查询是:
SELECT * from your_models
ORDER BY ( CASE
WHEN role=2 THEN 0
WHEN role=0 THEN 1
WHEN role=1 then 2
END
)
来自 this
的良好参考
感谢 this answer 我想到了这个:
order("role = 0 DESC, role = 1 DESC, role = 2 DESC")
或者,作为具有可选参数的范围:
scope :order_by_roles, -> (first = :admin, second = :ordinary, third = :manager) {
order("role = #{User.roles[first]} DESC, role = #{User.roles[second]} DESC, role = #{User.roles[third]} DESC")
}
从 Rails 6.0 开始, 将抛出此弃用警告:"Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s) [..] Non-attribute arguments will be disallowed in Rails 6.1. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql()."
因此,在他的回答和 的基础上,我建议:
class YourModel < ActiveRecord::Base
ROLE_ORDERS = [2, 0, 1]
scope :order_by_role, -> { order(Arel.sql(ROLE_ORDERS.map{ |role| "role=#{role} DESC" }.join(', '))) }
end
然后在您的代码中使用它,就像 ...
YourModel.order_by_role
... 生成此查询:
SELECT * from your_models
ORDER BY role = 2 DESC, role = 0 DESC, role = 1 DESC
ActiveRecord::QueryMethods#in_order_of (Rails 7+)
从Rails7开始,有新方法ActiveRecord::QueryMethods#in_order_of.
来自官方 Rails 文档的引用:
in_order_of(column, values)
Allows to specify an order by a specific set of values. Depending on your adapter this will either use a CASE statement or a built-in function.
User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users" ORDER BY FIELD("users"."id", 1, 5, 3)
它也适用于模型枚举:
class Book < ApplicationRecord
enum status: [:proposed, :written, :published]
end
Book.in_order_of(:status, %w[written published proposed])
来源:
我为角色定义了枚举:
enum role: {ordinary: 0, manager: 1, admin: 2}
我想按以下顺序订购一组对象:
admin (first all admins)
ordinary (then all ordinaries)
manager (and lastly all managers)
这可能吗?
解决方案:
class YourModel < ActiveRecord::Base
ROLE_ORDERS = [2, 0, 1]
scope :order_by_role, -> {
order_by = ['CASE']
ROLE_ORDERS.each_with_index do |role, index|
order_by << "WHEN role=#{role} THEN #{index}"
end
order_by << 'END'
order(order_by.join(' '))
}
end
那么你的查询就很简单了:
YourModel.order_by_role
生成的查询是:
SELECT * from your_models
ORDER BY ( CASE
WHEN role=2 THEN 0
WHEN role=0 THEN 1
WHEN role=1 then 2
END
)
来自 this
的良好参考感谢 this answer 我想到了这个:
order("role = 0 DESC, role = 1 DESC, role = 2 DESC")
或者,作为具有可选参数的范围:
scope :order_by_roles, -> (first = :admin, second = :ordinary, third = :manager) {
order("role = #{User.roles[first]} DESC, role = #{User.roles[second]} DESC, role = #{User.roles[third]} DESC")
}
从 Rails 6.0 开始,
因此,在他的回答和
class YourModel < ActiveRecord::Base
ROLE_ORDERS = [2, 0, 1]
scope :order_by_role, -> { order(Arel.sql(ROLE_ORDERS.map{ |role| "role=#{role} DESC" }.join(', '))) }
end
然后在您的代码中使用它,就像
YourModel.order_by_role
... 生成此查询:
SELECT * from your_models
ORDER BY role = 2 DESC, role = 0 DESC, role = 1 DESC
ActiveRecord::QueryMethods#in_order_of (Rails 7+)
从Rails7开始,有新方法ActiveRecord::QueryMethods#in_order_of.
来自官方 Rails 文档的引用:
in_order_of(column, values)
Allows to specify an order by a specific set of values. Depending on your adapter this will either use a CASE statement or a built-in function.
User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users" ORDER BY FIELD("users"."id", 1, 5, 3)
它也适用于模型枚举:
class Book < ApplicationRecord
enum status: [:proposed, :written, :published]
end
Book.in_order_of(:status, %w[written published proposed])
来源: