1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails7から追加された簡単にレコードを任意の順番に並び替えられる「in_order_of」

Last updated at Posted at 2024-09-04

はじめに

業務で「何かのレコードを特定のid順や数字で並べたい」みたいな要件に出会った経験ある人もいるのではないでしょうか?

当時エンジニア歴2年目の時に詰まった記憶があります笑

そのような時はどうやって実装しますか??

色々書き方はあると思いますが、Rails7から追加されたin_order_ofというメソッドが便利だったので紹介します。

in_order_ofを使わない方法

まずはin_order_ofを使用しない方法を考えようかと思います。

in_order_ofの説明だけ見たい人はここは飛ばしてください。

*前提条件

モデル: User
並び替えカラム: id
並び順: [3,2,1]
PostgreSQL使用

ids = [3,2,1]

*実際のクエリは変数を使っているのでin ($1,・・・)みたいになってますが、本題とは関係ないので簡略化しています。

1.whereを使って実装

まずは普通に絞り込みます。

User.where(id: ids)
=>
[#<User:0x0000ffff700ba660
  id: 1,
  name: "A",
 #<User:0x0000ffff700ba520
  id: 2,
  name: "B",
 #<User:0x0000ffff700ba3e0
  id: 3,
  name: "C"]

whereをそのまま使うと

SELECT "users".* FROM "users" WHERE "users"."id" IN (3, 2, 1)

というクエリになります。

このクエリは、usersテーブルからidが3、2、1のいずれかであるレコードを取得するだけです。

なので結果の順序はSQLクエリに依存します。

そのため、where時実現するのであれば、

User.where(id: [ids).order(Arel.sql("CASE id WHEN 3 THEN 1 WHEN 2 THEN 2 WHEN 1 THEN 3 END"))
=>
[#<User:0x0000ffff700ba660
  id: 3,
  name: "C",
 #<User:0x0000ffff700ba520
  id: 2,
  name: "B",
 #<User:0x0000ffff700ba3e0
  id: 1,
  name: "A"]

SELECT "users".* FROM "users" WHERE "users"."id" IN (3, 2, 1) ORDER BY CASE "users"."id"
  WHEN 3 THEN 1
  WHEN 2 THEN 2
  WHEN 1 THEN 3
END

一例ですがこんな感じになるかなと思います。

2.orderを使って実装

User.order(Arel.sql("CASE id WHEN #{ids[0]} THEN 1 WHEN #{ids[1]} THEN 2 WHEN #{ids[2]} THEN 3 END"))
SELECT "users".* 
FROM "users" 
ORDER BY CASE "users"."id"
  WHEN 3 THEN 1
  WHEN 2 THEN 2
  WHEN 1 THEN 3
END

こんな感じで取得が出来ます。

whereよりは短くなりましたがなかなか面倒です。

3.SQLクエリを直接使用

User.find_by_sql(["SELECT * FROM users WHERE id IN (?) ORDER BY CASE id WHEN ? THEN 1 WHEN ? THEN 2 WHEN ? THEN 3 END", ids, ids[0], ids[1], ids[2]])
SELECT * 
FROM users
WHERE id IN (3,2,1)
ORDER BY CASE id WHEN 3 THEN 1 WHEN 2 THEN 2 WHEN 1 THEN 3
END

ここまでくるとrubyっぽくないですね。

4.findを使う

個人的にはこの中ではこれが一番良いかなと思いました。
*idの場合

User.find(ids)

所感

こんな感じで割と取得するのが面倒な感じになっています。

それを踏まえて「in_order_of」を使用してみます。

in_order_ofを使う

User.in_order_of(:id, ids)
SELECT "users".* 
FROM "users" 
WHERE "users"."id" IN (3, 2, 1) 
ORDER BY CASE "users"."id" 
  WHEN 3 THEN 1 
  WHEN 2 THEN 2 
  WHEN 1 THEN 3 
  END

一発ですw

id以外のカラムも

user.rb
enum role: {
 admin: 1,
 general: 2,
 guest: 3
}
User.in_order_of(:role, %i(guest admim general))

みたいに使えます。

まとめ

まだRails6の環境のサービスも多いとは思いますが、時期にサポートが切れてRails7を使うことになります。

頭の片隅に本記事のことが残っていると良いかなと思います。

「任意の順に並べたい Rails メソッド」とかでググってこれ出てくると良いんですけどね〜

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?