So here is the code I came up with to generically generate an array param that will be stored on a jsonb property in ecto.
It only handles the root level, and is pretty limited, but I’m curious if the community has a better/more elegant/easier solution than I do. Let me re-phrase, I really believe you do, I just wanted to solicit advice.
Problem: jsonb array of objects stored on an Ecto model, these objects can be updated or added.
@doc "if the id is in the map already, update it, otherwise append it"
defp upsert_embed(%{id: id} = params, collection) when is_list(collection) do
case Enum.find(collection, &(&1.id == id)) do
nil -> collection ++ [params]
found -> updated = Map.merge(found, params)
collection = collection -- [found] ## This is the piece I'm really not confident in.
collection = collection ++ [updated]
end
end
@doc "provided map doesn't have an id"
defp upsert_embed(%{} = params, collection) when is_list(collection) do
collection ++ [params]
end
@doc """
adds or updates a child object of an array of maps.
"""
def upsert_embedded_field(%__struct__{} = struct, key, %{} = candidate_params) do
updated_embeds = Map.get(struct, key) || []
updated_embeds = case length(updated_embeds) do
0 ->
upsert_embed(candidate_params, updated_embeds)
_ ->
updated_embeds = Enum.reduce(updated_embeds, updated_embeds, fn(e, collection) ->
upsert_embed(candidate_params, collection)
end)
end
# Return the mutated list
updated_embeds
end






















