Rails
MySQL
ActiveRecord

Rails5でID順にorder byしたい時にActiveRecord::IrreversibleOrderErrorが発生することがある

TL;DR

  • ActiveRecordFIELD関数を使っているときにreverseをすると、ActiveRecord::IrreversibleOrderErrorが発生します。
  • 今回は、 order_as_specified gemを使ってこの問題を回避しました。

GitHub - panorama-ed/order_as_specified: Add arbitrary ordering to ActiveRecord queries.

起こったこと

タイムラインとかフィードを持つコンテンツや、ランキングなどの順番を別のロジックで生成している場合などに、指定したID順でレコードを取得したいケースがあると思います。

MySQLであれば、 FIELD関数が使えますし、Rails5を使っていれば下記のように記述できます。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 12.5 文字列関数

ids = somothing_ordered_ids
Content.order(['field(`contents`.`id`, ?)', ids])

これと併用して、 reverseを使ってしまうと、ActiveRecord::IrreversibleOrderErrorが発生します。

単純にreverse できないようなクエリを実行しようとすると、 ActiveRecord::IrreversibleOrderErrorthrow します。ちなみに、last などと併用しても内部的にはreverseを使っているので、この例外がthrowされます。
rails commit log流し読み(2016/01/28) - なるようになるブログ

上記コードで生成されるクエリは、下記のようになっているので単純にreverseができないようです。

SELECT `contents`.* FROM `contents` WHERE `contents`.`id` IN (9, 8, 3, 4, 2, 1) ORDER BY field(`contents`.`id`, 9,8,3,4,2,1)

どうしたか

order_as_specifiedgemを使って回避しました。

ruby - rails のwhere句の結果を指定の順番で取り出す方法は? - スタック・オーバーフロー
GitHub - panorama-ed/order_as_specified: Add arbitrary ordering to ActiveRecord queries.

order_as_specified gemを使うと、FIELD関数ではないアプローチでID順に並べようとします。

SELECT `contents`.* FROM `contents` ORDER BY `contents`.`id`=9 DESC, `contents`.`id`=8 DESC, `contents`.`id`=3 DESC, `contents`.`id`=4 DESC, `contents`.`id`=
2 DESC, `contents`.`id`=1 DESC 

このクエリであれば、単純にreverseできるクエリを発行することができるので、 例外が発生しなくなりました。