# `Localize.Address.Ecto.Map.Type`
[🔗](https://github.com/elixir-localize/localize_address/blob/v1.0.0/lib/localize/address/ecto/map_type.ex#L2)

An `Ecto.Type` storing a `t:Localize.Address.Address.t/0` as `jsonb`.

An address is stored as a single JSON object rather than as a column
per component. A postal address has many components and any given
address uses few of them — a Tokyo address and a Nebraska one
disagree about which fields even apply — so a wide table of mostly
empty columns is the wrong shape. Fields that are `nil` are omitted
from the stored object entirely, and a component added to the struct
later needs no migration.

### Schema

`jsonb` is a built-in type, so nothing but the column is needed:

    # in a migration
    add :address, :map

    # in a schema
    field :address, Localize.Address.Ecto.Map.Type

On PostgreSQL an Ecto `:map` column is `jsonb`, so the stored
address is queryable and indexable in SQL:

    from c in Customer, where: fragment("? ->> 'city' = ?", c.address, "Tokyo")

A GIN index over the column makes such lookups fast, and an
expression index over a single component serves a frequently
filtered one:

    create index(:customers, ["(address ->> 'city')"])

### Ordering

Sort an address component the same way as any other text. Because
`->>` yields text, `Localize.Ecto.Postgres.collate/2` from
`localize_sql` applies locale-aware ordering to it, which matters
for any locale whose collation differs from byte order.

### Casting

`cast/1` accepts an address struct, or a map keyed by either strings
or atoms — which is what an HTML form supplies. Unknown keys are
ignored, so a form carrying extra parameters does not fail. Parsing
an unstructured address string is `Localize.Address.parse/2`; it is
not attempted here, because a cast that silently invoked a parser
would make a form submission dependent on the libpostal NIF.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
