Skip to content

Commit

Permalink
Merge pull request #1184 from itsalongstory/feature/issue_1125
Browse files Browse the repository at this point in the history
Add ability to config PostgreSQL ORDER BY ... NULLS FIRST or NULLS LAST
  • Loading branch information
scarroll32 authored Dec 21, 2020
2 parents a9e84e1 + 2f5c6f6 commit 978c369
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 1 deletion.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,20 @@ the order indicator arrow by passing `hide_indicator: true` in the sort link:
default_order: { last_name: 'asc', first_name: 'desc' }) %>
```

#### PostgreSQL's sort option

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.

You may want to configure it like this:

```rb
Ransack.configure do |c|
c.postgres_fields_sort_option = :nulls_first # or :nulls_last
end
```

See this feature: https://www.postgresql.org/docs/13/queries-order.html

### Advanced Mode

"Advanced" searches (ab)use Rails' nested attributes functionality in order to
Expand Down
7 changes: 7 additions & 0 deletions lib/ransack/adapters/active_record/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ def evaluate(search, opts = {})
if scope_or_sort.is_a?(Symbol)
relation = relation.send(scope_or_sort)
else
case Ransack.options[:postgres_fields_sort_option]
when :nulls_first
scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS FIRST" : "#{scope_or_sort.to_sql} NULLS LAST"
when :nulls_last
scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS LAST" : "#{scope_or_sort.to_sql} NULLS FIRST"
end

relation = relation.order(scope_or_sort)
end
end
Expand Down
18 changes: 17 additions & 1 deletion lib/ransack/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def []=(key, value)
:up_arrow => '▼'.freeze,
:down_arrow => '▲'.freeze,
:default_arrow => nil,
:sanitize_scope_args => true
:sanitize_scope_args => true,
:postgres_fields_sort_option => nil
}

def configure
Expand Down Expand Up @@ -141,6 +142,21 @@ def sanitize_custom_scope_booleans=(boolean)
self.options[:sanitize_scope_args] = boolean
end

# 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.
#
# User may want to configure it like this:
#
# Ransack.configure do |c|
# c.postgres_fields_sort_option = :nulls_first # or :nulls_last
# end
#
# See this feature: https://www.postgresql.org/docs/13/queries-order.html
#
def postgres_fields_sort_option=(setting)
self.options[:postgres_fields_sort_option] = setting
end

# By default, Ransack displays sort order indicator arrows in sort links.
# The default may be globally overridden in an initializer file like
# `config/initializers/ransack.rb` as follows:
Expand Down
10 changes: 10 additions & 0 deletions spec/ransack/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,15 @@ module Ransack
.to eq false
end
end

it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
default = Ransack.options.clone

Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }

expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first

Ransack.options = default
end
end
end
21 changes: 21 additions & 0 deletions spec/ransack/search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,27 @@ def remove_quotes_and_backticks(str)
@s.sorts = 'id asc'
expect(@s.result.first.id).to eq 1
end

it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
default = Ransack.options.clone

s = Search.new(Person, s: 'name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC"

Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
s = Search.new(Person, s: 'name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST"
s = Search.new(Person, s: 'name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST"

Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
s = Search.new(Person, s: 'name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST"
s = Search.new(Person, s: 'name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST"

Ransack.options = default
end
end

describe '#method_missing' do
Expand Down

0 comments on commit 978c369

Please sign in to comment.