如何在 Rails 中使用 ICU 排序规则作为我的默认排序规则?
How do I use an ICU collation as my default in Rails?
我正在开发 Rails / Postgres 应用程序。我在 Mac 上开发。其他人使用 Linux。产品在 Heroku 上。
排序规则在 Mac 上被破坏,所以我得到的排序与 Linux 和 Heroku 略有不同。这会导致涉及排序的测试偶尔会失败或行为不一致。解决方法是使用 ICU 归类来获得一致的排序,但我不知道如何将其设为默认值。
Postgres will not create a database with an ICU collation。如果我将排序规则设置为 en-US-x-icu
in database.yml...
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV['DB_HOST'] || 'localhost' %>
username: <%= ENV['DB_USER'] || 'postgres' %>
password: <%= ENV['DB_PASSWORD'] || '' %>
collation: en-US-x-icu
我收到一个错误,invalid locale name: "en-US-x-icu"
,尽管它出现在 pg_collation
。
$ rails db:migrate:reset
Dropped database 'email_integrator_development'
Dropped database 'email_integrator_test'
PG::WrongObjectType: ERROR: invalid locale name: "en-US-x-icu"
Couldn't create 'email_integrator_development' database. Please check your configuration.
rails aborted!
ActiveRecord::StatementInvalid: PG::WrongObjectType: ERROR: invalid locale name: "en-US-x-icu"
也许有一种方法可以让 Rails 设置每个连接或每个 table 的集合?
我正在使用 Rails 6 和 Postgres 11,但如果需要我可以转移到 Postgres 12。
恐怕您必须将排序规则添加到每个字符串列定义中。
目前,您不能将 ICU 归类用作数据库默认值。 There have been efforts to improve that,但还没有任何可提交的结果。
但之后修复数据库很容易。使用 psql
,您可以 运行
SELECT format(
'ALTER TABLE %I.%I ALTER %I TYPE %I%s;',
table_schema,
table_name,
column_name,
data_type,
'(' || character_maximum_length || ')'
)
FROM information_schema.columns
WHERE data_type IN ('character', 'character varying', 'text')
AND table_schema NOT IN ('pg_catalog', 'information_schema', 'pg_toast') \gexec
您可以对 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 for a column if not set explicitly
options[:collation] = "en-US-x-icu" if options[:collation].blank? && (type == :string || type == :text)
create_column_definition(name, type, options)
end
end
end
end
end
我正在开发 Rails / Postgres 应用程序。我在 Mac 上开发。其他人使用 Linux。产品在 Heroku 上。
排序规则在 Mac 上被破坏,所以我得到的排序与 Linux 和 Heroku 略有不同。这会导致涉及排序的测试偶尔会失败或行为不一致。解决方法是使用 ICU 归类来获得一致的排序,但我不知道如何将其设为默认值。
Postgres will not create a database with an ICU collation。如果我将排序规则设置为 en-US-x-icu
in database.yml...
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV['DB_HOST'] || 'localhost' %>
username: <%= ENV['DB_USER'] || 'postgres' %>
password: <%= ENV['DB_PASSWORD'] || '' %>
collation: en-US-x-icu
我收到一个错误,invalid locale name: "en-US-x-icu"
,尽管它出现在 pg_collation
。
$ rails db:migrate:reset
Dropped database 'email_integrator_development'
Dropped database 'email_integrator_test'
PG::WrongObjectType: ERROR: invalid locale name: "en-US-x-icu"
Couldn't create 'email_integrator_development' database. Please check your configuration.
rails aborted!
ActiveRecord::StatementInvalid: PG::WrongObjectType: ERROR: invalid locale name: "en-US-x-icu"
也许有一种方法可以让 Rails 设置每个连接或每个 table 的集合?
我正在使用 Rails 6 和 Postgres 11,但如果需要我可以转移到 Postgres 12。
恐怕您必须将排序规则添加到每个字符串列定义中。
目前,您不能将 ICU 归类用作数据库默认值。 There have been efforts to improve that,但还没有任何可提交的结果。
但之后修复数据库很容易。使用 psql
,您可以 运行
SELECT format(
'ALTER TABLE %I.%I ALTER %I TYPE %I%s;',
table_schema,
table_name,
column_name,
data_type,
'(' || character_maximum_length || ')'
)
FROM information_schema.columns
WHERE data_type IN ('character', 'character varying', 'text')
AND table_schema NOT IN ('pg_catalog', 'information_schema', 'pg_toast') \gexec
您可以对 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 for a column if not set explicitly
options[:collation] = "en-US-x-icu" if options[:collation].blank? && (type == :string || type == :text)
create_column_definition(name, type, options)
end
end
end
end
end