Ecto and map with tuple keys

Hello everyone,

I am trying to persist structure into postgresql with ecto. My structure contains a map with tuple key.

defstruct coordinates: %{}

example: %MODULE{coordinates: %{{1, 2} => “whatever”}}

But Poison Encoder wants a key as string or atom for the coordinates map. I do understand postgres does not like tuple as key for jsonb.

So, is there an easy way to transform the tuple key to string before saving to database, then back to tuple after loading? Should I look into changeset or poison encoder? If possible, I would like to keep the more expressive key tuple form for data struct and the string key for db.

Thanks for taking time.

You can implement your own Ecto type: https://hexdocs.pm/ecto/Ecto.Type.html

You will declare the type as :jsonb and you can use load and dump to change the data as it goes and comes from the database.

2 Likes

Another possibility that @michalmuskala and @mitchellhenke explained to me when I was asking on the #ecto Slack channel yesterday how you could store arbitrary data in a column, would be to create the following type:

defmodule Anything do
  @behaviour Ecto.Type
  def type, do: :binary

  # Provide our own casting rules.
  def cast(term) do
    {:ok, term}
  end

  # When loading data from the database, we are guaranteed to
  # receive an integer (as databases are strict) and we will
  # just return it to be stored in the schema struct.
  def load(binary), do: {:ok, :erlang.binary_to_term(binary)}

  # When dumping data to the database, we *expect* an integer
  # but any value could be inserted into the struct, so we need
  # guard against them.
  def dump(term), do: {:ok, :erlang.term_to_binary(term)}
end

This will store any Elixir data type, regardless of what it is, in a binary column. The only disadvantage this has over using Postrex + jsonb + some logic to convert your things to JSON, is that a jsonb-type-column can be searched to some extent, while an encoded binary can not. However, for just storing some information, this should be enough.

3 Likes

Thank You for the answer.

I went to bed with a noob question and woke up with elixir’s god answer.

That is what I like with Elixir and this forum :slight_smile:

2 Likes