Add conditions to upsert
Is there any way to add conditionals when doing an upsert in a create action?
For example, I have this resource:
9 Replies
If I create a new instance with the same id, it will do an upsert. with a query more or less like this:
The thing is, I want to add a condition to the upsert part, basically I want to only apply the upsert if the
transaction_time
that I'm updating is bigger than the one in the DB.
Basically I want to change the query to this one:
Looking at the Ash documentation, I couldn't find any option to add conditionals to my create action, is there a way for me to achieve this?Just for completeness, in this forum post it shows how it is done with Ecto https://elixirforum.com/t/upsert-conditional-update-e-g-update-only-when-existing-data-is-outdated/55503/2
Elixir Programming Language Forum
Upsert conditional update e.g update only when existing data is out...
Found the solution (ref. Handling Upsert stale error) turn on_conflict into Ecto.Query use stale_error_field to prevent upsert stale error For example: def upsert(%Post{} = post) do on_conflict = from Post, where: [public: false], update: [set: [title: "second"]] post |> Repo.insert( conflict_target...
We don't have that option currently
you'll need to build a manual action
can you open a request to ash?
Yep, I will create it tomorrow when I'm back to work
Do you mind giving me just some tips on how to proceed with the manual action?
I never understood how exactly I'm supposed to actually insert the changeset in the DB with a manual create action. My guess is that there is some way to manually apply all checks from the resource attributes to the changeset, if there is no error, retrieve the end attribute map from it, insert it using Ecto Repo.insert directly, and then somehow converting the result (especially in case of an error) back to an error that is equivalent to one that Ash.Changeset would give.
I'm just not sure what is the best approach to do this and I can't recall an example of something like that in the documentation.
@Zach Daniel done: https://github.com/ash-project/ash/issues/667
GitHub
Add support for creating upsert conditionals · Issue #667 · ash-pro...
Is your feature request related to a problem? Please describe. Yes, some times you want to also add conditionals when running upsert in create actions. For example, let's say that, during an up...
This is my current workaround:
It is super ugly, but seems to work fine.
The only thing that I didn't figure out yet is how to transform the ecto changeset back to a ash changeset when there is errors.
For example, if the upsert condition fails, it will return this changeset:
Do we have some helper function to convert an Ecto.Changeset error into an Ash.Changeset error? In other words, I want to get the errors from Ecto.Changeset (
[transaction_time: {"is stale", [stale: true]}]
) and convert into a meaningful error inside Ash.Changeset.
I tried just passing the Ecto.Changeset errors field to Ash.Changeset using the add_error
function, that kinda works but it gives me a more "generic" error:
I was wondering if there is some "parser" that would fill all the error fields correctlyThere isn't one exposed, but what
AshPostgres
does looks something like this:
Ah, thanks! Based on that, my final workaround is the following: