やりたいこと
- Rails+Postgresqlの環境で、ransackの
sort_link
を使ってソートを実装している。 - PostgresqlはDESCでソートするとNULLが先頭にきてしまう。NULLを後方に並べたい。
-
ORDER BY (number IS NULL), number DESC
というかんじのクエリを組み立てたい。
こうなった
user.rb
scope :reorder_by_is_null, ->(column, direction) { select('*', "(#{column} IS NULL) AS is_null").reorder('is_null', column => direction) }
users_controller.rb
def index
@q = User.ransack(params[:q])
@users = @q.result(distinct: true).reorder_by_is_null(sort_column, sort_direction)
# => SELECT DISTINCT *, (id IS NULL) AS is_null FROM "users" ORDER BY is_null, "users"."id" ASC
end
private
def sort_column
User.column_names.include?(sort_params[:column]) ? sort_params[:column] : 'id'
end
def sort_direction
%w(asc desc).include?(sort_params[:direction]) ? sort_params[:direction] : 'asc'
end
def sort_params
return {} if params[:q].blank? || params[:q][:s].blank?
s = params[:q][:s].split(' ')
{ column: s[0], direction: s[1] }
end
reorder_by_is_null
- Ransackにより
ORDER BY column direction
というクエリが作られてしまうため、reorder
で上書きする必要がある。 - DISTINCTするとSELECTにないカラムをORDER BYに指定できないため、SELECT に
(column IS NULL) AS is_null
を付け加えている。
sort_params
-
sort_link
を使うと:q => { :s => "id asc" }
みたいなパラメータが来るので、カラム名とASC/DESCに分けて取り出している。
sort_column, sort_direction
-
sort_link
でソートパラメータが設定されたない場合のデフォルトのORDER BYがid ASC
になるようにしてある。
そして
- とりあえず意図した通りにソートされるようになった。
- もっと良いやり方ありそう。