Mutating the shape of data stored in the db vs. what's presented in Ecto struct (without embedded schema)

This is more of a educational question… usually, my Ecto schemas match the column names in my database pretty closely. Every now and then I specify the :source option in my field definitions if I want my Ecto schema to use a different field name than what’s in the database.

But how flexible is Ecto when you want your Ecto struct to be very different from what’s in the database. For example, let’s say we want a struct like this:

@type t :: %__MODULE__{
  a: String.t()
  b: integer()
  c: boolean()
}

but our database is actually defined like this:

defmodule Foo do
    use Ecto.Schema
    
    @primary_key false
    schema "bar" do
      field(:a, :string, null: false)
      field(:meta, :map)
    end  
  end

The idea being that the b and c values are persisted inside the meta map. Is this possible in Ecto? (And I realize that deviating too much between the 2 representations here can be confusing/unhelpful). But I’m curious – can it be done and how? Thanks in advance!

Ecto is pretty modular and so in some sense anything is possible if you call back to Repo.query! and Repo.load and do transformations yourself.

In particular however if you plan on inserting or updating data from Elixir into that table I would definitely recommend that your schema map pretty closely to the table. In your case there is no way for ecto to do an update based that changes what’s inside meta with a schema that treats those keys as virtual fields.

If you’re just trying to have more ergonomic ways to read or query data then you could explore using a database view and an Ecto Schema that maps tightly to that view, or even just a good old ecto query where you then transform the result into some other struct.

1 Like

There is no transparent transition, no, you’ll have to handle the conversions yourself.