2011-12-14 36 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