regroup! が重複カラムを除去するように修正
ActiveRecord の regroup! が同一カラムの重複指定を自動で除去し、公式ドキュメントが示す unscope(:group).group と同等の振る舞いを提供します。これにより、GROUP BY 句の冗長さが解消されます。
背景
ドキュメントでは regroup が unscope(:group).group(fields) の省略形として説明されていますが、インプレース版の regroup! は引数をそのまま group_values に代入していたため、重複カラムが残る不整合がありました。一方 group メソッドは group_values |= args によって自動的に重複除去を行います。この差異は同一クエリビルダーで期待通りの GROUP BY が生成されない原因となっていました。
実際の挙動は次のように異なります。
Post.regroup(:author_id, :author_id).group_values
# => [:author_id, :author_id]
Post.all.unscope(:group).group(:author_id, :author_id).group_values
# => [:author_id]
結果として、生成される SQL に余分な GROUP BY 列が含まれ、データベース側の結果や可読性に影響を与える可能性が指摘されていました。したがって、regroup! の挙動を group と揃える修正が必要と判断されました。
技術的な変更
修正は regroup! の引数配列に対して args.uniq! を呼び出すだけのシンプルな追加です。この操作により、受け取ったカラム名の重複がその場で除去され、以降の group_values 代入は重複のない配列になります。インターフェースや戻り値は変更されず、既存コードへの影響はありません。
@@
- def regroup!(*args) # :nodoc:
- self.group_values = args
- self
- end
+ def regroup!(*args) # :nodoc:
+ args.uniq!
+ self.group_values = args
+ self
+ end
テストスイートにも test_regroup_deduplication が追加され、Topic.regroup(:author_id, :author_id).group_values が [:author_id] になることを自動的に検証しています。
@@
def test_regroup_deduplication
topics = Topic.regroup(:author_id, :author_id)
assert_equal [:author_id], topics.group_values
end
これにより、修正が期待通り機能することが CI で保証されました。
設計判断
本修正は 最小侵入 を原則とし、既存ロジックに一行の配列操作を加えるだけで問題解決を図っています。新たなメソッドやオプションを導入せず、group と同様の振る舞いを保証することで後方互換性を最大限に保ちました。
同様の重複除去は reorder! でも実装されており、ActiveRecord のインプレース系メソッド群で一貫した設計方針が適用されていることが確認できます。統一的な実装により、開発者はメソッド間の違いを意識せずに利用でき、コードベース全体の可読性が向上します。
この設計選択は、機能追加のコストと既存ユーザーへの影響のトレードオフを最適化した結果と言えるでしょう。
まとめ
regroup! に重複除去ロジックを導入したことで、公式ドキュメント通りの挙動が保証され、API の一貫性と信頼性が回復しました。変更は局所的で後方互換性を保ちつつ、テストにより正しく機能することが確認されています。