2011-12-14 31 views
9

我有一个ID数组,存储在一些外部存储(rails缓存或redis)中。在控制器的动作我使用它,即ActiveRecord order by external array

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

获取这个数据,然后选择对象我也希望它可以给你开的相同array_of IDS入侵检测系统:

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

至于解决方法我用排序MySQL的场函数:

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

但因为它是MySQL的具体和大ids阵列的情况下产生很长的查询,我不喜欢这种方式。 有没有更好的,DB不可知的方式来做到这一点?从数据库中提取未分类的数据并在红宝石一侧进行排序是不理想的,因为它资源昂贵且难以用于分页。

谢谢。

+1

的可能的复制[ActiveRecord.find(阵列\ _of \ _ids),维持秩序(https://开头计算器。 com/questions/1680627/activerecord-findarray-of-id-preserving-order) – MatayoshiMariano 2017-08-11 19:32:52

回答

0

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

MyModel.order( “cached_order”)

+0

不幸的是它不是。 ID数组变化很快。例如,它可以是上次访问的#show页面的列表,存储在Redis中。 – Ineu 2011-12-14 14:16:04

4

如果你不介意接收阵列,而不是一个ActiveRecord集合,你可以使用:

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

我刚刚发布了一个宝石(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 
+1

这是一个非常聪明的解决方案。我没有添加宝石,而是为我的情况编写了一个简单的单行代码,用于执行我所需的操作:`sql = ids.map.with_index {| id,index | index == ids.length - 1? “ID ='#{id}'DESC”:“ID ='#{id}'DESC,”} .join('')`。感谢您的灵感! – ACIDSTEALTH 2017-04-30 18:28:38