---
title: Embedding Elixir Structs in Ecto Models
teaser: |
  Store semi-structured data inside a Postgres table
  without sacrificing expressiveness.
tags: elixir,ecto,web
author: Josh Steiner
published_on: 2015-10-15
---

Postgres can store unstructured data such as [arrays], [json], and [jsonb][json]
as of version 9.4. Ecto, Elixir's database wrapper, provides first class support
for serializing and deserializing Ecto structs and arrays into these native data
types without sacrificing the expressiveness of Ecto models. Embedded records
have all the things regular models have, such as structured fields, lifecycle
callbacks, and changesets. Let's look at how easy it is to embed structs into
our Ecto models.

[arrays]: http://www.postgresql.org/docs/9.4/static/arrays.html
[json]: http://www.postgresql.org/docs/9.4/static/datatype-json.html

## Embedding a single struct with `embeds_one`

You can use [`embeds_one`] to embed a single struct in an Ecto model. The record
you are embedding into must have a database field using the `:map` type for
unstructured data. In Postgres this is the `jsonb` type under the hood.

[`embeds_one`]: http://hexdocs.pm/ecto/Ecto.Schema.html#embeds_one/3

```elixir
defmodule MyApp.Repo.Migrations.CreateAccount do
  use Ecto.Migration

  def change do
    alter table(:accounts) do
      add :name, :string
      add :settings, :map
    end
  end
end
```

Now you can define the model you are embedding into:

```elixir
defmodule Account do
  use Ecto.Model
  schema "accounts" do
    field :name
    embeds_one :settings, Settings
  end
end
```

And now, the define the record you have embedded in `Account`:

```elixir
defmodule Settings do
  use Ecto.Model

  # embedded_schema is short for:
  #
  #   @primary_key {:id, :binary_id, autogenerate: true}
  #   schema "embedded Item" do
  #
  embedded_schema do
    field :email_signature
    field :send_emails, :boolean
  end
end
```

Embedded records behave like typical associations, except setting and
deleting embeds can _only_ be done via changesets.

```elixir
account = Repo.get!(Account, 20)
settings = %Settings{email_signature: "Josh Steiner", send_emails: true}

# You may want to move this to the model layer.
# This is done here for ease of demonstration.
changeset =
  account
  |> Ecto.Changeset.change
  |> Ecto.Changeset.put_change(:settings, settings)

Repo.update!(changeset)
```

This will automatically call the function `changeset/2` in the embedded model
(in this case, `Settings`) when saving the parent record.  This means embedded
records automatically go through validations! You can modify the function that
is called by passing the [`:on_cast`] option to `embeds_one`.

[`:on_cast`]: http://hexdocs.pm/ecto/Ecto.Schema.html#embeds_one/3

Embedded records are conveniently loaded with the parent record, so you don't
have to worry about joins or preloading:

```elixir
account = Repo.get!(Account, 20)
account.settings #=> %Settings{...}
```

## Embedding multiple structs with `embeds_many`

[`embeds_many`] behaves similarly to `embeds_one`, but allows you to store an
array of Ecto structs. Under the hood, Postgres uses a combination of `array`
columns with `jsonb` elements.

[`embeds_many`]: http://hexdocs.pm/ecto/Ecto.Schema.html#embeds_many/3

```elixir
defmodule MyApp.Repo.Migrations.CreatePeople do
  use Ecto.Migration

  def change do
    alter table(:people) do
      add :name, :string

      # It is recommended to set the default value to an empty array.
      add :addresses, {:array, :map}, default: []
    end
  end
end
```

Now you can define your models:

```elixir
defmodule Person do
  use Ecto.Model

  schema "people" do
    field :name
    embeds_many :addresses, Address
  end
end

defmodule Address do
  use Ecto.Model

  embedded_schema do
    field :street_name
    field :city
    field :state
    field :zip_code
  end
end
```

Setting many to many fields is done with an `array`:

```elixir
person = Repo.get!(Person, 7)
addresses = [
  %Address{street_name: "20 Foobar Street", city: "Boston", state: "MA", zip_code: "02111"},
  %Address{street_name: "1 Finite Loop", city: "Cupertino", state: "CA", %zip_code: "95014"},
]

changeset =
  person
  |> Ecto.Changeset.change
  |> Ecto.Changeset.put_change(:addresses, addresses)

Repo.update!(changeset)
```

You can now access these like a `has_many`:

```elixir
person = Repo.get!(Person, 5)
person.addresses #=> [%Address{...}, %Address{...}]
```

## Trade-Offs

As you've seen, embedding records is simple and comes with many of the powerful
features of Ecto. It's even easy to add fields to an embedded record without
running migrations. These are some nice benefits, however they come with serious
trade-offs worth considering.

When you use unstructured data, you lose some of the powerful relational
features that a SQL database provides. For example, since a record can only be
embedded in a single parent, you can't model a many-to-many relationship with
embedded records. You also can't use database constraints on structure and
uniqueness when storing in a JSON field. While you can add these constraints to
your application code, it's usually best to validate at the database level to
ensure data integrity.
