From 9beea1b744fb5e34da83e89e6b0183945d1ae7a8 Mon Sep 17 00:00:00 2001 From: Nathan Long Date: Tue, 30 Jul 2024 10:28:30 -0400 Subject: [PATCH] Document conditional upserts (#4463) --- lib/ecto/repo.ex | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/ecto/repo.ex b/lib/ecto/repo.ex index 06402d2aa8..8864f6b1f9 100644 --- a/lib/ecto/repo.ex +++ b/lib/ecto/repo.ex @@ -1767,6 +1767,32 @@ defmodule Ecto.Repo do inserting a struct with associations and using the `:on_conflict` option at the same time is not recommended, as Ecto will be unable to actually track the proper status of the association. + + ## Advanced Upserts + + Using an `Ecto.Query` for `:on_conflict` can allow us to use more advanced + database features. For example, PostgreSQL supports conditional upserts like + `DO UPDATE SET title = EXCLUDED.title, version = EXCLUDED.version + WHERE EXCLUDED.version > post.version`. + This means that the title and version will be updated only if the proposed + row has a greater version value than the existing row. + + Ecto can support this as follows: + + conflict_query = + from(p in Post, + update: [set: [ + title: fragment("EXCLUDED.title"), + version: fragment("EXCLUDED.version") + ]], + where: fragment("EXCLUDED.version > ?", p.version) + ) + + MyRepo.insert( + %Post{id: 1, title: "Ecto Upserts (Dance Remix)", version: 2}, + conflict_target: [:id], + on_conflict: conflict_query + ) """ @doc group: "Schema API" @callback insert(