Skip to content

Commit 978c369

Browse files
authored
Merge pull request #1184 from itsalongstory/feature/issue_1125
Add ability to config PostgreSQL ORDER BY ... NULLS FIRST or NULLS LAST
2 parents a9e84e1 + 2f5c6f6 commit 978c369

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
@@ -251,6 +251,20 @@ the order indicator arrow by passing `hide_indicator: true` in the sort link:
251251
default_order: { last_name: 'asc', first_name: 'desc' }) %>
252252
```
253253

254+
#### PostgreSQL's sort option
255+
256+
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.
257+
258+
You may want to configure it like this:
259+
260+
```rb
261+
Ransack.configure do |c|
262+
c.postgres_fields_sort_option = :nulls_first # or :nulls_last
263+
end
264+
```
265+
266+
See this feature: https://www.postgresql.org/docs/13/queries-order.html
267+
254268
### Advanced Mode
255269

256270
"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
@@ -533,6 +533,27 @@ def remove_quotes_and_backticks(str)
533533
@s.sorts = 'id asc'
534534
expect(@s.result.first.id).to eq 1
535535
end
536+
537+
it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
538+
default = Ransack.options.clone
539+
540+
s = Search.new(Person, s: 'name asc')
541+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC"
542+
543+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
544+
s = Search.new(Person, s: 'name asc')
545+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST"
546+
s = Search.new(Person, s: 'name desc')
547+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST"
548+
549+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
550+
s = Search.new(Person, s: 'name asc')
551+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST"
552+
s = Search.new(Person, s: 'name desc')
553+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST"
554+
555+
Ransack.options = default
556+
end
536557
end
537558

538559
describe '#method_missing' do

0 commit comments

Comments
 (0)