New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add NULL LAST to sorts #443

Closed
justin808 opened this Issue Oct 12, 2014 · 11 comments

Comments

Projects
None yet
5 participants
@justin808

I need NULL results to go last.

This is simple to do in a SQL statement. Just list "NULL LAST" right after the order predicate.

Where is the right place to put this?

Also, in the code for evaluate, I don't think relation.except(:order) is necessary, as the reorder takes care of removing the old order.

        def evaluate(search, opts = {})
          viz = Visitor.new
          relation = @object.where(viz.accept(search.base))
          if search.sorts.any?
            relation = relation.except(:order).reorder(viz.accept(search.sorts))
          end
          opts[:distinct] ? relation.distinct : relation
        end
@jonatack

This comment has been minimized.

Show comment
Hide comment
@jonatack

jonatack Oct 12, 2014

Member

Hi @justin808, you could do it with something like this:

controller#index
@admin_posts = @q.result.except(:order).order("#{@q.sorts.first} NULLS LAST")

I have a longer method called set_sort_order that all my controllers call to do this, though with the new multiple sort feature in the sort_link helper, I could simplify it.

Perhaps we could come up with an API for allowing specifying custom queries like nullif and nulls last.

Member

jonatack commented Oct 12, 2014

Hi @justin808, you could do it with something like this:

controller#index
@admin_posts = @q.result.except(:order).order("#{@q.sorts.first} NULLS LAST")

I have a longer method called set_sort_order that all my controllers call to do this, though with the new multiple sort feature in the sort_link helper, I could simplify it.

Perhaps we could come up with an API for allowing specifying custom queries like nullif and nulls last.

@justin808

This comment has been minimized.

Show comment
Hide comment
@justin808

justin808 Oct 13, 2014

@jonatack That syntax does not work for the latest ransack, although I don't know what @q is for sure.

This prints out an object reference:

#{@q.sorts.first}

I'm guessing that it's what I have as @search: Here's what I have:

class Manager::IndexPresenter
  def initialize(q, page, exclude_null_aum)
    # ransack conflicts with pagination if no query and going to page 2
    if q.present? && q.is_a?(Hash) && (q.values.all? { |v| v.blank? })
      q = nil
    end
    @q = q
    @page = page
    @exclude_null_aum = exclude_null_aum
  end

  def search
    @search ||= begin
      rel = Manager.joins(:most_recent_form_adv).where(simulated: false).
        by_filed_at_within_one_year
      rel = rel.where.not(form_advs: { aum: nil}) if @exclude_null_aum
      search = rel.search(@q)
      search.sorts = 'most_recent_form_adv_aum desc' if search.sorts.empty?
      search
    end
  end

  def managers
    @managers ||= @search.result.page @page
  end

@jonatack That syntax does not work for the latest ransack, although I don't know what @q is for sure.

This prints out an object reference:

#{@q.sorts.first}

I'm guessing that it's what I have as @search: Here's what I have:

class Manager::IndexPresenter
  def initialize(q, page, exclude_null_aum)
    # ransack conflicts with pagination if no query and going to page 2
    if q.present? && q.is_a?(Hash) && (q.values.all? { |v| v.blank? })
      q = nil
    end
    @q = q
    @page = page
    @exclude_null_aum = exclude_null_aum
  end

  def search
    @search ||= begin
      rel = Manager.joins(:most_recent_form_adv).where(simulated: false).
        by_filed_at_within_one_year
      rel = rel.where.not(form_advs: { aum: nil}) if @exclude_null_aum
      search = rel.search(@q)
      search.sorts = 'most_recent_form_adv_aum desc' if search.sorts.empty?
      search
    end
  end

  def managers
    @managers ||= @search.result.page @page
  end
@jonatack

This comment has been minimized.

Show comment
Hide comment
@jonatack

jonatack Oct 13, 2014

Member

Sorry, I've given this same reply several times here in Ransack issues, I meant to write:

result.except(:order).order("#{@search.sorts.first.name} #@search.sorts.first.dir} NULLS LAST")

In the Rails console, if you type @search.sorts.first.methods, you'll see that the first two methods are :name and :dir, which would lead you to poke around and try @search.sorts.first.name and @search.sorts.first.dir.

If you have further questions, have a look at the Ransack source code in Ransack::Helpers::FormHelper#sort_link.

Member

jonatack commented Oct 13, 2014

Sorry, I've given this same reply several times here in Ransack issues, I meant to write:

result.except(:order).order("#{@search.sorts.first.name} #@search.sorts.first.dir} NULLS LAST")

In the Rails console, if you type @search.sorts.first.methods, you'll see that the first two methods are :name and :dir, which would lead you to poke around and try @search.sorts.first.name and @search.sorts.first.dir.

If you have further questions, have a look at the Ransack source code in Ransack::Helpers::FormHelper#sort_link.

@jonatack jonatack closed this Oct 13, 2014

@jonatack

This comment has been minimized.

Show comment
Hide comment
@jonatack

jonatack Oct 13, 2014

Member

P.S. With will_paginate I don't encounter any conflicts with Ransack like you mention above.

Member

jonatack commented Oct 13, 2014

P.S. With will_paginate I don't encounter any conflicts with Ransack like you mention above.

@jonatack jonatack reopened this Oct 13, 2014

@jonatack jonatack closed this Oct 13, 2014

@justin808

This comment has been minimized.

Show comment
Hide comment
@justin808

justin808 Oct 14, 2014

Hi @jonatack, thanks for the help. @search.sorts.first.name yields this query:

SELECT  "managers".* FROM "managers" INNER JOIN "form_advs" ON "form_advs"."id" = "managers"."most_recent_form_adv_id" WHERE "managers"."simulated" = 'f' AND (form_adv_filled_at >= '2013-10-14 08:51:40.351824')  ORDER BY most_recent_form_adv_aum desc NULLS LAST LIMIT 25 OFFSET 0

The problem is that the sort column is really the ransack alias for the joined column.

Join is on "most_recent_form_adv".

Column to sort on join table is "aum".

So I had to do this, using attr_name

       except(:order).order("#{@search.sorts.first.attr_name} #{@search.sorts.first.dir} NULLS LAST").

Hi @jonatack, thanks for the help. @search.sorts.first.name yields this query:

SELECT  "managers".* FROM "managers" INNER JOIN "form_advs" ON "form_advs"."id" = "managers"."most_recent_form_adv_id" WHERE "managers"."simulated" = 'f' AND (form_adv_filled_at >= '2013-10-14 08:51:40.351824')  ORDER BY most_recent_form_adv_aum desc NULLS LAST LIMIT 25 OFFSET 0

The problem is that the sort column is really the ransack alias for the joined column.

Join is on "most_recent_form_adv".

Column to sort on join table is "aum".

So I had to do this, using attr_name

       except(:order).order("#{@search.sorts.first.attr_name} #{@search.sorts.first.dir} NULLS LAST").
@jonatack

This comment has been minimized.

Show comment
Hide comment
@jonatack

jonatack Oct 14, 2014

Member

Interesting info with attr_name and glad you got your problem sorted ;)
It might be a good idea to set up an API for adding custom orders like this, if anyone is interested in doing a pull request for that.

Member

jonatack commented Oct 14, 2014

Interesting info with attr_name and glad you got your problem sorted ;)
It might be a good idea to set up an API for adding custom orders like this, if anyone is interested in doing a pull request for that.

@justin808

This comment has been minimized.

Show comment
Hide comment
@justin808

justin808 Oct 15, 2014

I'd agree that this feature at least should be documented, and an API would be better. I'm not familiar enough with Ransack to comment right now. I inherited the Ransack code from another developer.

I'd agree that this feature at least should be documented, and an API would be better. I'm not familiar enough with Ransack to comment right now. I inherited the Ransack code from another developer.

@barnett

This comment has been minimized.

Show comment
Hide comment
@barnett

barnett Nov 17, 2015

Any progress on this? Would be interested in how others solved this issue.

barnett commented Nov 17, 2015

Any progress on this? Would be interested in how others solved this issue.

@3du4

This comment has been minimized.

Show comment
Hide comment
@3du4

3du4 Nov 17, 2015

@justin808 it worked for me also with attr_name. Thank you!

3du4 commented Nov 17, 2015

@justin808 it worked for me also with attr_name. Thank you!

@jonatack

This comment has been minimized.

Show comment
Hide comment
@jonatack

jonatack Nov 17, 2015

Member

Please see this new blog post by @radar, our co-maintainer: http://ryanbigg.com/2015/11/open-source-work/

This is free, DIY, OSS software that no one is paid to work on, yet many companies and people use it.

The issue tracker is not for feature requests or personal support. It is for reporting bugs.

If there was "progress" on this feature request, you would see it here.

If the feature is important to you, contribute a PR or pay someone to do it.

Please do not request progress on feature requests without doing anything, because it wastes people's time and makes maintainers want to quit.

Maintainers are short of time to test and fix issues and look at PRs. New features are not a priority. If you want them, roll up your sleeves and contribute them, remembering that the maintainer has to maintain them afterward for free in their spare time.

Sorry for ranting, this is a result of many small things adding up and not just this one, and thank you for your understanding.

Member

jonatack commented Nov 17, 2015

Please see this new blog post by @radar, our co-maintainer: http://ryanbigg.com/2015/11/open-source-work/

This is free, DIY, OSS software that no one is paid to work on, yet many companies and people use it.

The issue tracker is not for feature requests or personal support. It is for reporting bugs.

If there was "progress" on this feature request, you would see it here.

If the feature is important to you, contribute a PR or pay someone to do it.

Please do not request progress on feature requests without doing anything, because it wastes people's time and makes maintainers want to quit.

Maintainers are short of time to test and fix issues and look at PRs. New features are not a priority. If you want them, roll up your sleeves and contribute them, remembering that the maintainer has to maintain them afterward for free in their spare time.

Sorry for ranting, this is a result of many small things adding up and not just this one, and thank you for your understanding.

@aldrinmartoq

This comment has been minimized.

Show comment
Hide comment
@aldrinmartoq

aldrinmartoq May 30, 2017

Contributor

The solution given by @jonatack does not work if the sort column is an association (in my case a self association).

I got it working with the following code for Postgresql:

search = Model.ransack(…)
…
result = search.result
orders = result.orders.map { |order| order.direction == :asc ? "#{order.to_sql} NULLS FIRST" : "#{order.to_sql} NULLS LAST" }
result = result.except(:order).order(orders.join(", ")) if orders.count > 0

Hope this helps, cheers.

Contributor

aldrinmartoq commented May 30, 2017

The solution given by @jonatack does not work if the sort column is an association (in my case a self association).

I got it working with the following code for Postgresql:

search = Model.ransack(…)
…
result = search.result
orders = result.orders.map { |order| order.direction == :asc ? "#{order.to_sql} NULLS FIRST" : "#{order.to_sql} NULLS LAST" }
result = result.except(:order).order(orders.join(", ")) if orders.count > 0

Hope this helps, cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment