Ecto and data mapping: some structures are hard to work with

Some of my Ecto structures need to be translated. For example, an “organization” has a name in multiple languages:

schema "organizations" do
  has_many(:translations, OrganizationTranslation)
end

schema "organization_translations" do
  field(:language, :string)
  field(:name, :string)
  belongs_to(:organization, Organization)
end

This is how I retrieve the organizations and translations from the database:

query = from o in Organization,
  join: t in assoc(o, :translations),
  preload: [translations: t]

Repo.all(query)

Now to get the name of an organisation in English, I have to write the following code:

Enum.find(organization.translations, &(&1.language == "en")).name

I was thinking about writing a function in the context def get_organisation(id, language) and just merge the fields (of the language specified in the arguments) from OrganizationTranslation into the Organization structure and remove the translations property, so that I can just write organization.name; however I guess it will lead to bugs if I try to use the Ecto’s API with that transformed structure, because obviously by transforming the structure in that way it will be different than the structure defined in the Schema.

How would you handle this case? I.e. your Ecto Schema structure correlates/reflects the db in a not so convenient way, as the example described above.

If you know what language you want you can include that in your query:

language = "en"

query = from o in Organization,
  join: t in assoc(o, :translations),
  where: t.language == ^language,
  select: {t.name, o}

Repo.all(query)

This would return {"Some Name", %Organization{}}.

2 Likes

Try this:

fields = [:name]
locale = "en"

query = from o in Organization,
  join: t in assoc(o, :translations),
  where: t.language == ^locale,
  select_merge: map(t, ^fields)

https://hexdocs.pm/ecto/Ecto.Query.html#select_merge/3

2 Likes