Skip to content

Commit 2f5c6f6

Browse files
committed
Add ability to config PostgreSQL ORDER BY ... NULLS FIRST or NULLS LAST, close #1125
1 parent 150eeb2 commit 2f5c6f6

File tree

5 files changed

+69
-1
lines changed

5 files changed

+69
-1
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,20 @@ the order indicator arrow by passing `hide_indicator: true` in the sort link:
250250
default_order: { last_name: 'asc', first_name: 'desc' }) %>
251251
```
252252

253+
#### PostgreSQL's sort option
254+
255+
The `NULLS FIRST` and `NULLS LAST` options can be used to determine whether nulls appear before or after non-null values in the sort ordering.
256+
257+
You may want to configure it like this:
258+
259+
```rb
260+
Ransack.configure do |c|
261+
c.postgres_fields_sort_option = :nulls_first # or :nulls_last
262+
end
263+
```
264+
265+
See this feature: https://www.postgresql.org/docs/13/queries-order.html
266+
253267
### Advanced Mode
254268

255269
"Advanced" searches (ab)use Rails' nested attributes functionality in order to

lib/ransack/adapters/active_record/context.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ def evaluate(search, opts = {})
4242
if scope_or_sort.is_a?(Symbol)
4343
relation = relation.send(scope_or_sort)
4444
else
45+
case Ransack.options[:postgres_fields_sort_option]
46+
when :nulls_first
47+
scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS FIRST" : "#{scope_or_sort.to_sql} NULLS LAST"
48+
when :nulls_last
49+
scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS LAST" : "#{scope_or_sort.to_sql} NULLS FIRST"
50+
end
51+
4552
relation = relation.order(scope_or_sort)
4653
end
4754
end

lib/ransack/configuration.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ def []=(key, value)
3333
:up_arrow => '▼'.freeze,
3434
:down_arrow => '▲'.freeze,
3535
:default_arrow => nil,
36-
:sanitize_scope_args => true
36+
:sanitize_scope_args => true,
37+
:postgres_fields_sort_option => nil
3738
}
3839

3940
def configure
@@ -141,6 +142,21 @@ def sanitize_custom_scope_booleans=(boolean)
141142
self.options[:sanitize_scope_args] = boolean
142143
end
143144

145+
# The `NULLS FIRST` and `NULLS LAST` options can be used to determine
146+
# whether nulls appear before or after non-null values in the sort ordering.
147+
#
148+
# User may want to configure it like this:
149+
#
150+
# Ransack.configure do |c|
151+
# c.postgres_fields_sort_option = :nulls_first # or :nulls_last
152+
# end
153+
#
154+
# See this feature: https://www.postgresql.org/docs/13/queries-order.html
155+
#
156+
def postgres_fields_sort_option=(setting)
157+
self.options[:postgres_fields_sort_option] = setting
158+
end
159+
144160
# By default, Ransack displays sort order indicator arrows in sort links.
145161
# The default may be globally overridden in an initializer file like
146162
# `config/initializers/ransack.rb` as follows:

spec/ransack/configuration_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,5 +173,15 @@ module Ransack
173173
.to eq false
174174
end
175175
end
176+
177+
it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
178+
default = Ransack.options.clone
179+
180+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
181+
182+
expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first
183+
184+
Ransack.options = default
185+
end
176186
end
177187
end

spec/ransack/search_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,27 @@ def remove_quotes_and_backticks(str)
507507
@s.sorts = 'id asc'
508508
expect(@s.result.first.id).to eq 1
509509
end
510+
511+
it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
512+
default = Ransack.options.clone
513+
514+
s = Search.new(Person, s: 'name asc')
515+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC"
516+
517+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
518+
s = Search.new(Person, s: 'name asc')
519+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST"
520+
s = Search.new(Person, s: 'name desc')
521+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST"
522+
523+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
524+
s = Search.new(Person, s: 'name asc')
525+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST"
526+
s = Search.new(Person, s: 'name desc')
527+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST"
528+
529+
Ransack.options = default
530+
end
510531
end
511532

512533
describe '#method_missing' do

0 commit comments

Comments
 (0)