Skip to content

Instantly share code, notes, and snippets.

@csokun
Created May 9, 2023 13:45
Show Gist options
  • Save csokun/61fe5c4975d9d8b040b62e8e3c711a93 to your computer and use it in GitHub Desktop.
Save csokun/61fe5c4975d9d8b040b62e8e3c711a93 to your computer and use it in GitHub Desktop.
Ecto conditional upsert
defmodule App.Posts do
# ref. https://amandasposito.com/elixir/ecto/macro/2021/01/22/macro-ecto-custom-on-conflict.html
# ref. https://elixirforum.com/t/upsert-conditional-update-e-g-update-only-when-existing-data-is-outdated/55503/2
defmacro custom_on_conflict_update_replace_all(queryable) do
values =
:fields
|> Post.__schema__()
|> Enum.map(fn f ->
{f, quote(do: fragment(unquote("EXCLUDED.#{f}")))}
end)
quote(do: Ecto.Query.update(unquote(queryable), [u], set: [unquote_splicing(values)]))
end
def on_conflict_query do
from(u in Post)
|> where([u], u.updated_at < fragment("EXCLUDED.updated_at"))
|> custom_on_conflict_update_replace_all()
end
def upsert(%Post{} = post) do
post
|> Repo.insert(
conflict_target: [:id],
on_conflict: on_conflict_query(),
stale_error_field: :updated_at,
return: true
)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment