I have a resource that has a uuid primary key and a lookup value that is hashed on create with the following Hash change. The change takes a specified argument, hashes it and sets it to the specified attribute like so:
create :create do
argument :lookup, :binary
accept [additional fields...]
primary? true
change {Changes.Hash, argument: :lookup, attribute: :hashed_lookup}
end
This works fine. I also have a read action that hashes the lookup and uses that in a filter so I can read by an unhashed lookup like so:
read :read_by_lookup do
argument :lookup, :binary
prepare {Preparations.Hash, argument: lookup, attribuite: hashed_lookup}
end
However, I’m not sure how to best update a single record when I only have the lookup value, I don’t have the UUID.
A bulk update works and I can re-use the query from the lookup read action:
Ash.Query.for_read(:read_by_lookup, %{lookup: "lookup value"})
|> Ash.bulk_update(:update, %{data: %{...}})
But just doing a regular update doesn’t work without first doing a read to get the resource so it has the UUID. If I had the UUID I could go:
%Resource{id: "some uuid"}
|> Ash.Changeset.for_update(:update, %{data: %{...}})
|> Ash.update()
But this doesn’t seem to work:
update :by_lookup do
argument :lookup, :binary
accept [additional fields...]
change fn changeset, _context ->
lookup = Ash.Changeset.get_argument(changeset, :lookup)
hash = hash(lookup)
changeset
|> Ash.Changeset.filter(expr(hashed_lookup == ^hash))
end
end
Calling the update action:
%Resource{}
|> Ash.Changeset.for_update(:by_lookup, %{lookup: "lookup_value", data: %{...}})
|> Ash.update!()
Throws:
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in: Resource.by_lookup
Unknown Error
* ** (ArgumentError) nil given for :id. Comparison with nil is forbidden as it is unsafe. Instead write a query with is_nil/1, for example: is_nil(s.id)
I tried a few other things but it looks like for_update is expecting the resource to have an :id set for filtering. Is there a way that I can create a regular update action that can update based on a field other than id without having to make a trip to the db for a read first? And is there an more “idiomatic” way to have the lookup automatically hashed?