diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ca3010..2d56810f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### 🚀 Features * Add Rails 7.1.0 support by @yuki24 in https://github.com/activerecord-hackery/ransack/pull/1439 +* Add ability to config any database (apart from MySQL) ORDER BY ... NULLS FIRST or NULLS LAST by @tttffff in https://github.com/activerecord-hackery/ransack/pull/1463 ### 🐛 Bug Fixes diff --git a/docs/docs/getting-started/simple-mode.md b/docs/docs/getting-started/simple-mode.md index 01c7872e..ba7b6b91 100644 --- a/docs/docs/getting-started/simple-mode.md +++ b/docs/docs/getting-started/simple-mode.md @@ -236,15 +236,16 @@ Ransack's `sort_url` helper is like a `sort_link` but returns only the url default_order: { last_name: 'asc', first_name: 'desc' }) %> ``` -### PostgreSQL's sort option +### Fields sort option +Does not work for MySQL. 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: ```ruby Ransack.configure do |c| - c.postgres_fields_sort_option = :nulls_first # or :nulls_last + c.fields_sort_option = :nulls_first # or :nulls_last end ``` @@ -252,7 +253,7 @@ To treat nulls as having the lowest or highest value respectively. To force null ```ruby Ransack.configure do |c| - c.postgres_fields_sort_option = :nulls_always_first # or :nulls_always_last + c.fields_sort_option = :nulls_always_first # or :nulls_always_last end ``` diff --git a/lib/ransack/adapters/active_record/context.rb b/lib/ransack/adapters/active_record/context.rb index 48a6ea80..6a308b52 100644 --- a/lib/ransack/adapters/active_record/context.rb +++ b/lib/ransack/adapters/active_record/context.rb @@ -43,15 +43,15 @@ 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] + case Ransack.options[:fields_sort_option] when :nulls_first - scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") + scope_or_sort = scope_or_sort.direction == :asc ? scope_or_sort.nulls_first : scope_or_sort.nulls_last when :nulls_last - scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") + scope_or_sort = scope_or_sort.direction == :asc ? scope_or_sort.nulls_last : scope_or_sort.nulls_first when :nulls_always_first - scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") + scope_or_sort = scope_or_sort.nulls_first when :nulls_always_last - scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") + scope_or_sort = scope_or_sort.nulls_last end relation = relation.order(scope_or_sort) diff --git a/lib/ransack/configuration.rb b/lib/ransack/configuration.rb index 08a0b540..d9003fd7 100644 --- a/lib/ransack/configuration.rb +++ b/lib/ransack/configuration.rb @@ -34,7 +34,7 @@ def []=(key, value) down_arrow: '▲'.freeze, default_arrow: nil, sanitize_scope_args: true, - postgres_fields_sort_option: nil, + fields_sort_option: nil, strip_whitespace: true } @@ -162,13 +162,13 @@ def sanitize_custom_scope_booleans=(boolean) # User may want to configure it like this: # # Ransack.configure do |c| - # c.postgres_fields_sort_option = :nulls_first # or e.g. :nulls_always_last + # c.fields_sort_option = :nulls_first # or e.g. :nulls_always_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 + def fields_sort_option=(setting) + self.options[:fields_sort_option] = setting end # By default, Ransack displays sort order indicator arrows in sort links. diff --git a/spec/ransack/configuration_spec.rb b/spec/ransack/configuration_spec.rb index 9d232143..b9b1c701 100644 --- a/spec/ransack/configuration_spec.rb +++ b/spec/ransack/configuration_spec.rb @@ -188,12 +188,12 @@ module Ransack end end - it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do + it "fields sort option" do default = Ransack.options.clone - Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first } + Ransack.configure { |c| c.fields_sort_option = :nulls_first } - expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first + expect(Ransack.options[:fields_sort_option]).to eq :nulls_first Ransack.options = default end diff --git a/spec/ransack/search_spec.rb b/spec/ransack/search_spec.rb index 6426a20a..170a0eaa 100644 --- a/spec/ransack/search_spec.rb +++ b/spec/ransack/search_spec.rb @@ -614,19 +614,19 @@ def remove_quotes_and_backticks(str) expect(@s.result.first.id).to eq 1 end - it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do + it "fields sort option", if: ::ActiveRecord::Base.connection.adapter_name != "Mysql2" 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 } + Ransack.configure { |c| c.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 } + Ransack.configure { |c| c.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') @@ -635,31 +635,31 @@ def remove_quotes_and_backticks(str) Ransack.options = default end - it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do + it "fields sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name != "Mysql2" do default = Ransack.options.clone s = Search.new(Person, s: 'doubled_name asc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC" - Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first } + Ransack.configure { |c| c.fields_sort_option = :nulls_first } s = Search.new(Person, s: 'doubled_name asc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST" s = Search.new(Person, s: 'doubled_name desc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST" - Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last } + Ransack.configure { |c| c.fields_sort_option = :nulls_last } s = Search.new(Person, s: 'doubled_name asc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST" s = Search.new(Person, s: 'doubled_name desc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST" - Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first } + Ransack.configure { |c| c.fields_sort_option = :nulls_always_first } s = Search.new(Person, s: 'doubled_name asc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST" s = Search.new(Person, s: 'doubled_name desc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST" - Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last } + Ransack.configure { |c| c.fields_sort_option = :nulls_always_last } s = Search.new(Person, s: 'doubled_name asc') expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST" s = Search.new(Person, s: 'doubled_name desc')