【问题标题】:Rails collection ordering doesn't works as expected with UTF-8 stringRails 集合排序不能按预期使用 UTF-8 字符串
【发布时间】:2015-07-28 05:38:50
【问题描述】:

Rails 集合排序无法按预期使用 UTF-8 字符串:

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

应该是

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

我错过了什么?

带有 ruby​​ 2.1.5p273 的 Rails 4.1.8(2014-11-13 修订版 48405)[x86_64-darwin14.0]

【问题讨论】:

标签: ruby-on-rails ruby postgresql ruby-on-rails-4


【解决方案1】:

这很可能是 PostgreSQL 的 collation settings issue

排序功能允许指定数据每列甚至每个操作的排序顺序和字符分类行为。这缓解了数据库的 LC_COLLATE 和 LC_CTYPE 设置在创建后无法更改的限制。

您可以尝试使用 Rails 迁移修复列的排序规则。像这样的:

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

您可能还应该将整理信息添加到您的database.yml

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

这里是database.yml设置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

【讨论】:

  • 你的答案更好。 @Stepan:在命令行上运行 locale -a 以查看系统支持的所有语言环境,以防您需要的不是 ru_RU.utf8
  • 感谢您的回复!但不幸的是,设置排序规则ru_RU.UTF-8 没有帮助
  • 过失,设置排序规则确实有帮助,但仅适用于新创建的记录。如何更新我的记录的排序规则?
  • @StepanKuzmin 您确定在迁移更改列的排序规则后完全重新启动了您的应用程序吗?它应该影响所有数据。但是,您应该也可以像这样在查询中指定排序规则:University.order('abbr asc COLLATE "ru_RU"')。我现在没有 pgsql,所以我无法测试它,但它应该可以这样工作。
  • 您也可以尝试重新索引整个数据库以确保它不是索引问题:REINDEX DATABASE my_rails_db;
【解决方案2】:

如果您在排序时使用相同的语言环境,请尝试使用 ffi-icu gem。 This 部分文档与您最相关。

还有sort_alphabetical gem,但它依赖于NFD decomposition,因此它可能不适用于所有字符。

【讨论】:

    【解决方案3】:

    您可以在 PostgreSQL 中使用“ru-RU-x-icu”排序规则,并将其显式添加到每一列,或者猴子补丁 ActiveRecord 以添加默认排序规则(如果未定义):

    module ActiveRecord
      module ConnectionAdapters
        module PostgreSQL
          module ColumnMethods
            def new_column_definition(name, type, **options) # :nodoc:
              if integer_like_primary_key?(type, options)
                type = integer_like_primary_key_type(type, options)
              end
              type = aliased_types(type.to_s, type)
              options[:primary_key] ||= type == :primary_key
              options[:null] = false if options[:primary_key]
              # set default collation if it's not defined explicitly
              options[:collation] = "ru-RU-x-icu" if options[:collation].blank? && (type == :string || type == :text)
              create_column_definition(name, type, options)
            end
          end
        end
      end
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-09
      • 2017-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-15
      • 2023-03-27
      • 2015-06-02
      相关资源
      最近更新 更多