Search code examples
ruby-on-railsrubypostgresqlruby-on-rails-4

Rails collection ordering doesn't works as expected with UTF-8 string


Rails collection ordering doesn't works as expected with UTF-8 string:

> University.order('abbr asc').map(&:abbr)
=> ["Б", "В", "А"]

It should be

> University.order('abbr asc').map(&:abbr)
=> ["А", "Б", "В"]

What am I missing?

Rails 4.1.8 with ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin14.0]


Solution

  • This is most likely a collation settings issue with PostgreSQL:

    The collation feature allows specifying the sort order and character classification behavior of data per-column, or even per-operation. This alleviates the restriction that the LC_COLLATE and LC_CTYPE settings of a database cannot be changed after its creation.

    You can try and fix the collation of a column with a Rails migration. Something like this:

    class FixCollationForAbbr < ActiveRecord::Migration
      def up
        execute 'ALTER TABLE universities ALTER COLUMN abbr TYPE varchar COLLATE "ru_RU";' 
      end
    end
    

    You should probably also add collation information to your database.yml:

    defaults: &defaults
      adapter: postgresql
      encoding: utf8
      collation: ru_RU.utf8
      ctype: ru_RU.utf8
    

    Here is how the database.yml settings affect table creation with PostgreSQL:

    def create_database(name, options = {})
      options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
    
      option_string = options.inject("") do |memo, (key, value)|
        memo += case key
        ...snip...
        when :encoding
          " ENCODING = '#{value}'"
        when :collation
          " LC_COLLATE = '#{value}'"
        when :ctype
          " LC_CTYPE = '#{value}'"
        ...snip...
        end
      end
    
      execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
    end