Localize.Ecto (Localize SQL v1.0.0)

Copy Markdown View Source

Locale-aware query expressions for Ecto.

Text sorts differently in different languages, and neither PostgreSQL nor SQLite sorts linguistically unless a query asks it to. This library resolves a Localize language tag to an ICU collation and applies it to a query expression, so that ordering, comparison and case mapping follow the user's locale.

There is one module per database, because Ecto requires the SQL of a fragment to be a compile-time literal and the two databases do not spell everything the same way:

Both resolve collation names through the same Localize.Ecto.Collation, so a locale produces the same name — "de-x-icu", "sv-x-icu", "de-u-co-phonebk-x-icu" — on either database and a query written for one sorts identically on the other.

This module exposes the PostgreSQL macros directly, so existing code that imports it keeps working and PostgreSQL users need not think about the split:

import Ecto.Query
import Localize.Ecto

# Collate using the current locale (Localize.get_locale/0)
from p in Product, order_by: collate(p.name)

# Collate using an explicit locale
from p in Product, order_by: collate(p.name, "sv")

# Locale determined at runtime
def sorted_products(locale) do
  from p in Product, order_by: collate(p.name, ^locale)
end

# Collate a comparison: name < 'münchen' under German collation
from p in Product, where: collate(p.name < "münchen", "de"), select: p.name

# Use a collation created in a migration, by name
from p in Product, order_by: collate(p.name, collation: "german_phonebook")

For SQLite, import Localize.Ecto.SQLite3 instead. An application talking to both databases imports each where it needs it.

Collations for locales carrying a BCP 47 collation type, such as de-u-co-phonebk, are not preloaded by PostgreSQL. Create them once in a migration with Localize.Ecto.Migration.create_collation/2 and they resolve automatically thereafter. SQLite needs no such migration.

Summary

Functions

Applies PostgreSQL's AT TIME ZONE with a validated time zone.

Applies a COLLATE clause for the current locale.

Applies a COLLATE clause for the given locale or collation.

Locale-aware initcap() using the collation of the current locale.

Locale-aware initcap().

Locale-aware lower() using the collation of the current locale.

Locale-aware lower().

Locale-aware full-text search match using the current locale.

Locale-aware full-text search match.

Locale-aware upper() using the collation of the current locale.

Locale-aware upper().

Functions

at_time_zone(expression, zone)

(macro)

Applies PostgreSQL's AT TIME ZONE with a validated time zone.

See Localize.Ecto.Postgres.at_time_zone/2.

Examples

iex> import Ecto.Query
iex> query = from e in "events", select: at_time_zone(e.starts_at, "Australia/Sydney")
iex> match?(%Ecto.Query{}, query)
true

collate(expression)

(macro)

Applies a COLLATE clause for the current locale.

See Localize.Ecto.Postgres.collate/1.

Examples

iex> import Ecto.Query
iex> query = from p in "products", order_by: collate(p.name), select: p.name
iex> match?(%Ecto.Query{}, query)
true

collate(expression, locale_or_options)

(macro)

Applies a COLLATE clause for the given locale or collation.

See Localize.Ecto.Postgres.collate/2.

Examples

iex> import Ecto.Query
iex> query = from p in "products", order_by: collate(p.name, "sv"), select: p.name
iex> match?(%Ecto.Query{}, query)
true

iex> import Ecto.Query
iex> query = from p in "products", select: collate(p.name < p.description, "de")
iex> match?(%Ecto.Query{}, query)
true

initcap(expression)

(macro)

Locale-aware initcap() using the collation of the current locale.

See Localize.Ecto.Postgres.initcap/1.

initcap(expression, locale_or_options)

(macro)

Locale-aware initcap().

See Localize.Ecto.Postgres.initcap/2.

lower(expression)

(macro)

Locale-aware lower() using the collation of the current locale.

See Localize.Ecto.Postgres.lower/1.

lower(expression, locale_or_options)

(macro)

Locale-aware lower().

See Localize.Ecto.Postgres.lower/2.

Examples

iex> import Ecto.Query
iex> query = from p in "products", select: lower(p.name, "tr")
iex> match?(%Ecto.Query{}, query)
true

ts_match(expression, query)

(macro)

Locale-aware full-text search match using the current locale.

See Localize.Ecto.Postgres.ts_match/2.

ts_match(expression, query, locale_or_options)

(macro)

Locale-aware full-text search match.

See Localize.Ecto.Postgres.ts_match/3.

Examples

iex> import Ecto.Query
iex> query = from p in "products", where: ts_match(p.description, "wooden chair", "de"), select: p.id
iex> match?(%Ecto.Query{}, query)
true

upper(expression)

(macro)

Locale-aware upper() using the collation of the current locale.

See Localize.Ecto.Postgres.upper/1.

upper(expression, locale_or_options)

(macro)

Locale-aware upper().

See Localize.Ecto.Postgres.upper/2.