Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ransack, Postgres - sort on column from associated table with distinct: true

I have an app that uses the Ransack gem and I'm converting it from Mysql to Postgres.

In an instance where the sort column is from an associated table and the distinct option is set to true, Postgres throws this error:

PG::InvalidColumnReference: ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list

The Ransack github page says that, in a case like this, "you're on your own."

What's the best - any! - strategy for handling this scenario?

q = Contact.includes(:contact_type).search
q.sorts = ['contact_type_name asc']
q.result(distinct: true)
PG::InvalidColumnReference: ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list

Thanks!

like image 434
ricsrock Avatar asked Nov 03 '13 01:11

ricsrock


2 Answers

There is a simpler way to solve this problem. Use an ActiveRecord joins query or select query to add the columns needed, for example:

q = Contact.search
q.sorts = ['contact_type_name asc']
q.result(distinct: true).
  includes(:contact_type).
  joins(:contact_type)

Alternatively, if you only want to select a few columns, you could do:

q = Contact.search
q.sorts = ['contact_type_name asc']
q.result(distinct: true).
  select('contacts.*, contact_type.name')

I've done a pull request to update Ransack's readme.

like image 107
Mikel Lindsaar Avatar answered Oct 11 '22 21:10

Mikel Lindsaar


I've just faced with the same problem and a quick and dirty fix which works for single column sorting would be adding an initializer as follows. This monkey patch adds the missing sorting column into select statement.

module Ransack
  module Adapters
    module ActiveRecord
      class Context < ::Ransack::Context
        def evaluate(search, opts = {})
          viz = Visitor.new
          relation = @object.where(viz.accept(search.base))
          if search.sorts.any?
            _vaccept = viz.accept(search.sorts)
            table  = _vaccept.first.expr.relation.name
            column = _vaccept.first.expr.name
            relation = relation.except(:order).reorder(_vaccept).select("#{@default_table.name}.*, #{table}.#{column}")
          end
          opts[:distinct] ? relation.distinct : relation
        end
      end
    end
  end
end
like image 22
Eren CAY Avatar answered Oct 11 '22 20:10

Eren CAY