0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ransack】sort_linkで重複のある値をソートするときに気をつけるべきこと【Rails】

Posted at

ユースケース

railsでよく行う実装として、ransackで検索・絞り込み・ソートを行ったオブジェクトをviewのindexにレンダリングするというケースが多いと思います。
例えば商品一覧(products)をテーブル形式で表示した際、テーブルのヘッダーにsort_link @q, :selling_price, 'Price'という記述を加えて、価格の高い順・安い順で並べ替えるなんてことがよくあるかと思います。
今回はこのようなsort_linkを使用した場合に起きえる問題点をご紹介します。

問題点

上記で説明したsort_link @q, :selling_price, 'Price'というケースですとあまり起き得ないかもしれませんが、例えば以下のように在庫の数量で同じ実装をしたとします。
sort_link @q, :quantity, 'Quantity'

products_controller.rb
class ProductsController
  before_action :set_products, only: :index

  PER = 30

  def index; end

  private

  def set_products
    @q = Product.ransack(params[:q])
    page_per = params[:page_per].present? ? params[:page_per] : PER
    @products = @q.result.page(params[:page]).per(page_per)
  end
end

この場合にquantityのソートを実行すると発行されるSQLは以下のようになります。

SELECT
	`products`.*
FROM
	`products`
ORDER BY
	`products`.`quantity` DESC
LIMIT 30 OFFSET 0;

一見問題なさそうに見えますが、このようにページネーションがあるかつ、ソートする値(quantity)に同数のものが多数存在する場合、1ページ目と2ページ目に同じ商品が表示されてしまうというケースが起きえます(1ページ目にあった商品Aが2ページ目にも表示されているというような状態)。

原因

なぜこのようなことが起きるのかというと、ORDER BY句の並び順が一意でないからです。
例えば1ページあたりの表示数を30件(PER = 30)としている場合にquantityが1のものが100件あるとします。
その状態でページネーションにより2ページ目以降にアクセスした場合、複数のレコードが同じquantityの値を持っており、これらのレコードの並び順はベースエンジンによって任意に決定されます。
また、OFFSETを使うとき、どのレコードが次のページに含まれるかが曖昧になるため、同じレコードが異なるページに現れることがあるようです。

対策

並び順が一意となるセカンダリソートキーを設定しましょう。
例えば以下のようにidで一位性を保つなどの方法があります。
sort_link @q, :quantity, [:quantity, 'id asc'], 'Quantity'

SELECT
	`products`.*
FROM
	`products`
ORDER BY
	`products`.`quantity` ASC,
	`products`.`id` ASC
LIMIT 30 OFFSET 0;

もしくは以下のようにransackのソート条件の後に作成時刻(ほぼ一位と思われる)などで並べ替えすればデータの並び順が安定し、ページングを移動しても問題はなくなります。

products_controller.rb
  def set_products
    @q = Product.ransack(params[:q])
    page_per = params[:page_per].present? ? params[:page_per] : PER
    @products = @q.result.order(created_at: :desc).page(params[:page]).per(page_per)
  end

さいごに

この問題に直面した際、最初は同じデータが重複して作成されていることを疑ったのですが、そうではなく並び替えの一位性が原因でした。
同じような問題に直面した方の解決の助けになれば幸いです。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?