0
votes

I want to store data using jsonb that could be either an array or a map. The source data arrives as either an array or a map, and we want to unify this on the server so the front-end can work with a single type. Is there a way/workaround that I can use, store and retrieve both json/map and array types which are valid for the jsonb postgres type?

I have tried to use source: to copy to array type sourced field before calling cast pipeline if I get an array in params:

condition: Map.has_key?(params, "value") && is_list(value)

I am successfully able to save data, but when retrieving from the DB I am getting a cast error

field(:value, :map)
field(:value_array, {:array, :integer}, source: :value)

Note: Ecto version > 3 and postgres 10

1

1 Answers

1
votes

Solution was to define custom ecto types which would accept and load both arrays and maps

defmodule HaiData.Jsonb do
  @behaviour Ecto.Type
  def type, do: :map

  # Provide custom casting rules.
  def cast(data) when is_list(data) or is_map(data) do
    {:ok, data}
  end


  # Everything else is a failure though
  def cast(_), do: :error

  # When loading data from the database, we are guaranteed to
  # receive a map or list
  def load(data) when is_list(data) or is_map(data) do
    {:ok, data}
  end

  # When dumping data to the database, we *expect* a map or list
  # so we need to guard against them.
  def dump(data)  when is_list(data) or is_map(data), do: {:ok, data}
  def dump(_), do: :error
end

Credit goes to idiot - an elixirforum user