はじめに
業務で「何かのレコードを特定の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以外のカラムも
enum role: {
admin: 1,
general: 2,
guest: 3
}
User.in_order_of(:role, %i(guest admim general))
みたいに使えます。
まとめ
まだRails6の環境のサービスも多いとは思いますが、時期にサポートが切れてRails7を使うことになります。
頭の片隅に本記事のことが残っていると良いかなと思います。
「任意の順に並べたい Rails メソッド」とかでググってこれ出てくると良いんですけどね〜