问题 ActiveRecord按外部数组排序


我有一组id,存储在一些外部存储(rails cache或redis)中。在控制器的操作中,我获取此数据并使用它选择对象,即

ids = [5, 1, 17, 84] # really fetched from external source
result = MyModel.where(:id => ids)

我也希望它的顺序与array_of id中的id相同:

ids == result.map(&:id) # => true

作为解决方法,我使用mysql FIELD函数排序:

MyModel.where(:id => ids).order("FIELD(id, #{ids.join ', '})")

但我不喜欢这种方法,因为它是特定于mysql的,并且在大的情况下创建非常长的查询 ids 阵列。 有没有更好的,与DB无关的方法呢?从数据库获取未分类的数据并在ruby端进行排序是不可取的,因为它资源昂贵并且难以与分页一起使用。

谢谢。


8887
2017-12-14 13:13


起源

可能重复 ActiveRecord.find(array_of_ids),保留顺序 - MatayoshiMariano


答案:


我刚刚发布了一颗宝石(order_as_specified)允许您像这样执行本机SQL排序:

MyModel.where(id: ids).order_as_specified(id: ids)

它返回一个ActiveRecord关系,因此可以与其他方法链接:

MyModel.where(id: ids).order_as_specified(id: ids).limit(3)

如果你很好奇,那么它正在构建:

... ORDER BY ID='5' DESC, ID='1' DESC, ID='17' DESC, ID='84'  DESC

11
2018-03-13 18:17



这是一个非常聪明的解决方案。而不是添加一个宝石,我只是为我的案例写了一个简单的单行程,做我需要的: sql = ids.map.with_index { |id, index| index == ids.length - 1 ? "ID='#{id}' DESC" : "ID='#{id}' DESC, " }.join('')。感谢您的灵感! - ACIDSTEALTH
很高兴它对你有用@ACIDSTEALTH! - JacobEvelyn


如果您不介意接收数组而不是ActiveRecord Collection,您可以使用:

result = MyModel.find(ids).sort_by {|m| ids.index(m.id)}

4
2018-03-13 14:16





如果数组的顺序始终相同,则可以将缓存的订单列添加到数据库表中。

MyModel.order( “cached_order”)


0
2017-12-14 13:17



不幸的是它不是。 id数组变化很快。例如,它可以是存储在Redis中的最后访问过的#show页面的列表。 - Ineu