Localize.Ecto.Migration (Localize Ecto v0.1.0)

Copy Markdown View Source

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
end

The 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.

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

collated(column, locale_or_options \\ Localize.get_locale())

@spec collated(atom() | String.t(), Localize.locale() | String.t() | Keyword.t()) ::
  String.t()

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

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"]

create_collation(locale, options \\ [])

@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

Options

  • :name is the collation name to create. The default is the name Localize.Ecto.Collation.collation_for!/1 resolves locale to, such as "de-u-co-phonebk-x-icu", so queries built with Localize.Ecto.collate/2 find the collation without further configuration.

  • :icu_locale is the ICU locale string for the collation definition. The default is the canonical locale identifier of locale, which preserves all BCP 47 extensions.

  • :deterministic determines whether the collation is deterministic. The default is true, matching PostgreSQL's default. Non-deterministic collations compare canonically equivalent strings as equal but cannot be used with LIKE or pattern matching.

  • :if_not_exists adds IF NOT EXISTS when true. The default is false.

  • :schema schema-qualifies the collation name.

Returns

  • :ok.

Examples

create_collation("de-u-co-phonebk")

create_collation("de-u-co-phonebk", name: "german_phonebook")

create_collation("und-u-ks-level2", name: "case_insensitive", deterministic: false)

create_collation_sql(locale, options \\ [])

@spec create_collation_sql(Localize.locale() | String.t(), Keyword.t()) :: String.t()

Returns the CREATE COLLATION statement for a locale.

Arguments

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')]

drop_collation(locale, options \\ [])

@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

Returns

  • :ok.

Examples

drop_collation("de-u-co-phonebk")

drop_collation("de-u-co-phonebk", name: "german_phonebook")

drop_collation_sql(locale, options \\ [])

@spec drop_collation_sql(Localize.locale() | String.t(), Keyword.t()) :: String.t()

Returns the DROP COLLATION statement for a locale.

Arguments

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"]