|
81 | 81 |
|
82 | 82 | This runs `CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de-u-co-phonebk')`. The name matches what `Localize.Ecto.Collation` resolves the locale to, so from then on `collate(p.name, "de-u-co-phonebk")` works with no further configuration. German phonebook order treats `ü` as `ue`: standard German sorts `Mueller, Muller, Müller` while phonebook order sorts `Mueller, Müller, Muller`. Other collation types include `zh-u-co-stroke` and `zh-u-co-zhuyin` for Chinese stroke and Bopomofo orderings, and `es-u-co-trad` for traditional Spanish, where `ch` sorts as a single letter. |
83 | 83 |
|
84 | | -Other ICU keywords open up collation behaviors beyond language tailoring: |
| 84 | +Other ICU keywords open up collation behaviors beyond language tailoring. `Localize.Ecto.Migration.create_collation/2` accepts them as tailoring options using the `Localize.Collation.Options` vocabulary, and query-time resolution carries the same keywords, so the locale-with-keywords form works end to end without naming anything: |
85 | 85 |
|
86 | | -* Numeric ordering (`-u-kn`) compares digit sequences by numeric value, giving natural sort: `file1, file2, file10` instead of `file1, file10, file2`. |
| 86 | +* Numeric ordering (`-u-kn`, option `numeric: true`) compares digit sequences by numeric value, giving natural sort: `file1, file2, file10` instead of `file1, file10, file2`. |
87 | 87 |
|
88 | 88 | ```elixir |
89 | | - create_collation("und-u-kn", name: "natural_sort") |
| 89 | + # In a migration |
| 90 | + create_collation("en", numeric: true) |
| 91 | + |
| 92 | + # In queries — resolves to the collation created above |
| 93 | + from f in Upload, order_by: collate(f.name, "en-u-kn-true") |
90 | 94 | ``` |
91 | 95 |
|
92 | | -* Strength reduction (`-u-ks-level2`) ignores case differences. Combined with `deterministic: false` this yields a collation under which `'HELLO' = 'hello'` is true — case-insensitive matching without `citext` or `lower()` wrappers, at the cost that `LIKE` and pattern matching cannot use the collation. |
| 96 | +* Strength reduction (`-u-ks-level2`, option `strength: :secondary`) ignores case differences; `strength: :primary` ignores accents too. These strengths default to a nondeterministic collation — the only mode in which `'HELLO' = 'hello'` is actually true — giving case-insensitive matching without `citext` or `lower()` wrappers, at the cost that `LIKE` and pattern matching cannot use the collation (PostgreSQL 18 lifts the `LIKE` restriction). A unique index over such a collation enforces case-insensitive uniqueness: |
93 | 97 |
|
94 | 98 | ```elixir |
95 | | - create_collation("und-u-ks-level2", name: "case_insensitive", deterministic: false) |
| 99 | + create_collation("und", strength: :secondary) |
| 100 | + create index("users", [collated(:email, "und-u-ks-level2")], unique: true) |
96 | 101 | ``` |
97 | 102 |
|
98 | | -Named collations like these are used in queries with the `:collation` option: |
| 103 | +Custom-named collations are used in queries with the `:collation` option: |
99 | 104 |
|
100 | 105 | ```elixir |
101 | | -from f in File, order_by: collate(f.name, collation: "natural_sort") |
| 106 | +create_collation("und", numeric: true, name: "natural_sort") |
| 107 | +from f in Upload, order_by: collate(f.name, collation: "natural_sort") |
102 | 108 | ``` |
103 | 109 |
|
104 | | -Two practical notes. PostgreSQL normalizes ICU locale identifiers when it stores them, so `und-u-kn-true` is recorded in standard form as `und-u-kn` — pass the standard form to avoid a notice. And because collations live in the database, remember that a collation created in a migration exists per database: test, dev, and production each get theirs when migrations run. |
| 110 | +Two practical notes. PostgreSQL normalizes ICU locale identifiers when it stores them (`und-u-kn-true` is recorded as `und-u-kn`), which can produce a server notice at creation time — harmless, and the collation's name is unaffected. And because collations live in the database, remember that a collation created in a migration exists per database: test, dev, and production each get theirs when migrations run. |
0 commit comments