ActiveRecord条件,其顺序基于属性的特殊顺序



使用阵列

@valid_ts = [669, 668, 667, 34, 35, 36, 37, 38, 39, 40, 41, 61, 62, 63, 64, 65, 66, 130, 131, 132, 133, 134, 135, 136, 137, 157, 158, 159, 160, 161, 162, 163, 164]
=> [669, 668, 667, 34, 35, 36, 37, 38, 39, 40, 41, 61, 62, 63, 64, 65, 66, 130, 131, 132, 133, 134, 135, 136, 137, 157, 158, 159, 160, 161, 162, 163, 164]
slots = Timeslot.where('id IN (?)', @valid_ts).all
[34, 35, 36, 37, 38, 39, 40, 41, 61, 62, 63, 64, 65, 66, 130, 131, 132, 133, 134, 135, 136, 137, 157, 158, 159, 160, 161, 162, 163, 164, 667, 668, 669]

Rails基于其默认属性updated_at运行查询,而目标是按照所提供数组的顺序维护结果。

slots = Timeslot.where('id IN (?)', @valid_ts).sort_by { |valid_ts| @valid_ts.index valid_ts }.pluck('id')
[669, 35, 36, 37, 38, 39, 40, 41, 61, 62, 63, 64, 65, 66, 130, 131, 132, 133, 134, 135, 136, 137, 157, 158, 159, 160, 161, 162, 163, 164, 667, 668, 34]

失败,只反转数组的第一个和最后一个元素。在此基础上,通过Ajedi32的回答,给出了正确的方向。。。

slots = Timeslot.find(@valid_ts).sort_by { |valid_ts| @valid_ts.index valid_ts }.pluck('id')
Timeslot Load (0.7ms)  SELECT "timeslots".* FROM "timeslots" WHERE "timeslots"."id" 
IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33)  
[["id", 669], ["id", 668], ["id", 667], ["id", 34], ["id", 35], ["id", 36], ["id", 37], ["id", 38], ["id", 39], ["id", 40], ["id", 41], ["id", 61], ["id", 62], ["id", 63], ["id", 64], ["id", 65], ["id", 66], ["id", 130], ["id", 131], ["id", 132], ["id", 133], ["id", 134], ["id", 135], ["id", 136], ["id", 137], ["id", 157], ["id", 158], ["id", 159], ["id", 160], ["id", 161], ["id", 162], ["id", 163], ["id", 164]]

然而,似乎可以正确查询,就像在上面的情况中一样,返回的数组的索引减少了一(即最后一个元素被放在队列的头(

[164, 668, 667, 34, 35, 36, 37, 38, 39, 40, 41, 61, 62, 63, 64, 65, 66, 130, 131, 132, 133, 134, 135, 136, 137, 157, 158, 159, 160, 161, 162, 163, 669]

如何正确排序此数组以反映提交的数组?

Justin Weiss在他的博客中写到了这个问题。

告诉数据库首选顺序并直接从数据库加载按该顺序排序的所有记录是一种很好的方法。他的博客文章示例:

# in config/initializers/find_by_ordered_ids.rb
module FindByOrderedIdsActiveRecordExtension
extend ActiveSupport::Concern
module ClassMethods
def find_ordered(ids)
order_clause = "CASE id "
ids.each_with_index do |id, index|
order_clause << "WHEN #{id} THEN #{index} "
end
order_clause << "ELSE #{ids.length} END"
where(id: ids).order(order_clause)
end
end
end
ActiveRecord::Base.include(FindByOrderedIdsActiveRecordExtension)

这允许你写:

Object.find_ordered([2, 1, 3]) # => [2, 1, 3]

如果使用Mysql,则可以使用field函数。

Timeslot.where(id: @valid_ts).order("field(id, #{ids.join ','})")

如果您正在使用Postgres,则可以使用position功能。-

Timeslot.where(id: @valid_ts).order("position(id::text in '#{ids.join(',')}')")

但是,如果使用rails >= 5.2.0,则ActiveRecord可以同时执行这两种操作,因为它是从该版本添加的。最有可能的是,它也在rails 5.0中反向移植。pull请求、提交和文档。

Timeslot.find(@valid_ts)

来自find-的文档

find(*args)
Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). If one or more records cannot be found for the requested ids, then ActiveRecord::RecordNotFound will be raised. If the primary key is an integer, find by id coerces its arguments by using to_i.
Person.find(1)          # returns the object for ID = 1
Person.find("1")        # returns the object for ID = 1
Person.find("31-sarah") # returns the object for ID = 31
Person.find(1, 2, 6)    # returns an array for objects with IDs in (1, 2, 6)
Person.find([7, 17])    # returns an array for objects with IDs in (7, 17)
Person.find([1])        # returns an array for the object with ID = 1
Person.where("administrator = 1").order("created_on DESC").find(1)
NOTE: The returned records are in the same order as the ids you provide. If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where method and provide an explicit ActiveRecord::QueryMethods#order option. But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.

查看此SO帖子了解更多想法。

最新更新