How Ecto field_source_mapper works

Any good example in how field_source_mapper works in Ecto ?

This is my take on it, though I’ve never personally used it myself.

@field_source_mapper allows for your schema field names to be different than your column names. For example, lets say you have:

schema "users" do
  field :name, :string
  field :location, :string
  field :foo, :string
end

Ecto expects there to be a name, location, and foo column on the source. You might be in a situation where you’re interfacing with an existing database that has already defined the columns names. The database might be using columns username instead of name, and users_current_location instead of location. If you wanted to use name and location in your schema, but still map them to the existing underlying columns then you could use a @field_source_mapper for that. Something like:

@field_source_mapper fn
  :name -> :username
  :location -> :users_current_location
  x -> x
end

It’s important to retain the default x -> x mapping so that fields like foo still map to the foo column.

4 Likes

Nice !

I was thought that the mapper can be used to make transformations in the columns values too, is it possible right ? Because the mapper accepts functions so should be ok to apply a filter to a result.

The mapper is just for changing the columns names. It only receives the column name as an argument and is expected to return just a column name. From the docs:

In other words, it is a mechanism to automatically generate the :source
option for the field macro. It defaults to fn x -> x end, where no
field transformation is done;

And then the :source option docs from the field macro:

:source - Defines the name that is to be used in database for this field.

I guess I’m somewhat foolishly realizing that the way I used it in my previous example (mapping two of the fields directly) is better left for the :source option like so:

schema "users" do
  field :name, :string, [source: :username]
  field :location, :string, [source: users_current_location]
  field :foo, :string
end

The @field_source_mapper seems more appropriate for when you want to map all columns names following a similar pattern. In fact, it looks like @field_source_mapper will be ignored whenever :source is set on the fields, seen here:

  source = opts[:source] || Module.get_attribute(mod, :field_source_mapper).(name)
2 Likes

If you need to transform the data coming from the db take a look at Ecto.Type

3 Likes

@LostKobrakai yeah that makes sense. Thanks @kylethebaker for the clarification

Nice! This example really should be in the documentation!