Migration helpers for creating PostgreSQL ICU collations.
PostgreSQL preloads ICU collations for plain locales, but not for
locales carrying a BCP 47 collation type such as de-u-co-phonebk
(German phonebook ordering) or zh-u-co-stroke (Chinese stroke
ordering). Those must be created once per database, which belongs
in a migration:
defmodule MyApp.Repo.Migrations.AddPhonebookCollation do
use Ecto.Migration
import Localize.Ecto.Migration
def change do
create_collation("de-u-co-phonebk")
end
endThe default collation name is the one Localize.Ecto.Collation
resolves the same locale to, so once created, the collation is used
automatically by Localize.Ecto.collate/2:
from p in Product, order_by: collate(p.name, "de-u-co-phonebk")The primary public API is create_collation/2 and
drop_collation/2. The SQL they execute is also available from
create_collation_sql/2 and drop_collation_sql/2.
create_collation/2 and drop_collation/2 are PostgreSQL-only:
a SQLite collation is a per-connection runtime registration rather
than a schema object, so there is nothing to create and the
tailorings above work there with no migration at all. collated/2
emits valid index syntax for both databases.
Summary
Functions
Returns a collated column expression for use in an index definition.
Creates an ICU collation in a migration.
Returns the CREATE COLLATION statement for a locale.
Drops an ICU collation in a migration.
Returns the DROP COLLATION statement for a locale.
Functions
Returns a collated column expression for use in an index definition.
PostgreSQL only uses an index for a collated sort or comparison when
the index was created with the same collation. This function builds
the column expression for such an index, for use with
Ecto.Migration.index/3:
create index("products", [collated(:name, "de")])
create index("products", [collated(:name, collation: "german_phonebook")])Note that if a PostgreSQL upgrade links a newer ICU library whose
collation data changed — uncommon, but it happens — PostgreSQL warns
of a collation version mismatch and indexes built with that
collation must be reindexed (REINDEX INDEX index_name, then
ALTER COLLATION collation_name REFRESH VERSION).
Arguments
columnis the column name as an atom or string.locale_or_optionsis a locale accepted byLocalize.Ecto.Collation.collation_for!/2, or a keyword list with a:collationoption naming a collation directly. The default is the current locale fromLocalize.get_locale/0.
Returns
- A column expression string such as
"name" COLLATE "de-x-icu".
Examples
iex> Localize.Ecto.Migration.collated(:name, "de")
~s["name" COLLATE "de-x-icu"]
iex> Localize.Ecto.Migration.collated(:name, collation: "german_phonebook")
~s["name" COLLATE "german_phonebook"]
@spec create_collation(Localize.locale() | String.t(), Keyword.t()) :: :ok
Creates an ICU collation in a migration.
Runs CREATE COLLATION with the ICU provider. The operation is
reversible: rolling back drops the collation.
Arguments
localeis aLocalize.LanguageTag.t/0, or any locale identifier accepted byLocalize.validate_locale/1.optionsis a keyword list of options.
Options
:nameis the collation name to create. The default is the nameLocalize.Ecto.Collation.collation_for!/1resolveslocaleto, such as"de-u-co-phonebk-x-icu", so queries built withLocalize.Ecto.collate/2find the collation without further configuration.:icu_localeis the ICU locale string for the collation definition. The default is the canonical locale identifier oflocale, which preserves all BCP 47 extensions.:strengthis the comparison strength::primary(base letters only — case- and accent-insensitive),:secondary(adds accents),:tertiary(adds case),:quaternary, or:identical. Encoded as the-u-ks-keyword.:numericastrueenables natural sort, comparing digit runs by numeric value so "file2" sorts before "file10". Encoded as the-u-kn-keyword.:case_levelastrueadds a case level between the accent and case strengths, making a:primarycollation accent-insensitive but case-sensitive. Encoded as the-u-kc-keyword.:case_firstis:upper,:lower, orfalse, ordering one case before the other. Encoded as the-u-kf-keyword.:alternateis:shifted(punctuation ignored at the primary strengths) or:non_ignorable. Encoded as the-u-ka-keyword.:backwardsastruecompares accents from the end of the string (Canadian French). Encoded as the-u-kb-keyword.:deterministicdetermines whether the collation is deterministic. The default istrueexcept when the effective strength is:primaryor:secondary, where it defaults tofalse— those strengths only compare case/accent variants as equal when the collation is nondeterministic. Nondeterministic collations work with=,DISTINCT,GROUP BYand unique indexes, but not withLIKEorpg_trgm(PostgreSQL 18 lifts theLIKErestriction).:if_not_existsaddsIF NOT EXISTSwhentrue. The default isfalse.:schemaschema-qualifies the collation name.
Returns
:ok.
Examples
create_collation("de-u-co-phonebk")
create_collation("de-u-co-phonebk", name: "german_phonebook")
# Natural sort: "file2" before "file10"
create_collation("en", numeric: true)
# Case- and accent-insensitive; nondeterministic by default.
# Use in a unique index for case-insensitive uniqueness:
# create index("users", [collated(:email, "und-u-ks-level1")], unique: true)
create_collation("und", strength: :primary)
create_collation("und", strength: :secondary, name: "case_insensitive")
@spec create_collation_sql(Localize.locale() | String.t(), Keyword.t()) :: String.t()
Returns the CREATE COLLATION statement for a locale.
Arguments
localeis aLocalize.LanguageTag.t/0, or any locale identifier accepted byLocalize.validate_locale/1.optionsis a keyword list of options. Seecreate_collation/2.
Returns
- The SQL statement as a string.
Examples
iex> Localize.Ecto.Migration.create_collation_sql("de-u-co-phonebk")
~s[CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de-u-co-phonebk')]
iex> Localize.Ecto.Migration.create_collation_sql("de-u-co-phonebk", name: "german_phonebook")
~s[CREATE COLLATION "german_phonebook" (provider = icu, locale = 'de-u-co-phonebk')]
@spec drop_collation(Localize.locale() | String.t(), Keyword.t()) :: :ok
Drops an ICU collation in a migration.
The operation is reversible: rolling back recreates the collation from the same arguments.
Arguments
localeis aLocalize.LanguageTag.t/0, or any locale identifier accepted byLocalize.validate_locale/1.optionsis a keyword list of options. Seecreate_collation/2;:if_not_existsis replaced by:if_exists.
Returns
:ok.
Examples
drop_collation("de-u-co-phonebk")
drop_collation("de-u-co-phonebk", name: "german_phonebook")
@spec drop_collation_sql(Localize.locale() | String.t(), Keyword.t()) :: String.t()
Returns the DROP COLLATION statement for a locale.
Arguments
localeis aLocalize.LanguageTag.t/0, or any locale identifier accepted byLocalize.validate_locale/1.optionsis a keyword list of options. Seedrop_collation/2.
Returns
- The SQL statement as a string.
Examples
iex> Localize.Ecto.Migration.drop_collation_sql("de-u-co-phonebk")
~s[DROP COLLATION "de-u-co-phonebk-x-icu"]
iex> Localize.Ecto.Migration.drop_collation_sql("de-u-co-phonebk", if_exists: true)
~s[DROP COLLATION IF EXISTS "de-u-co-phonebk-x-icu"]