This is a follow-up to: How to reorder items in a list in Ash framework - #2 by zachdaniel. I want to add a unique constraint to the position column but this would cause the position update to fail with:
{
:error,
%Ash.Error.Invalid{changeset: "#Changeset<>", errors: [%Ash.Error.Changes.InvalidAttribute{field: :position, message: "has already been taken", private_vars: [constraint: :unique, constraint_name: "activities_unique_position_index"], value: nil, splode: Ash.Error, bread_crumbs: [], vars: [], path: [], stacktrace: #Splode.Stacktrace<>, class: :invalid}]}
Is there an equivalent for :on_conflict
option for Ash.bulk_update?
Here is the current code for updating positions:
@impl true
def change(changeset, _opts, context) do
actor = context.actor
organisation = context.tenant
Ash.Changeset.before_action(changeset, fn changeset ->
requested_position = Ash.Changeset.get_attribute(changeset, :position)
old_position = changeset.data.position
max_position =
Activity
|> Ash.Query.filter(group_id == ^Ash.Changeset.get_attribute(changeset, :group_id))
|> Ash.count!(actor: actor, tenant: organisation)
new_position = min(requested_position, max_position)
with %Ash.BulkResult{status: :success} <-
shift_down(old_position, new_position, organisation, actor),
%Ash.BulkResult{status: :success} <-
shift_up(old_position, new_position, organisation, actor) do
changeset
else
%Ash.BulkResult{errors: errors} ->
Ash.Changeset.add_error(changeset, errors)
end
changeset
end)
end
defp shift_down(old_position, new_position, organisation, actor) do
Activity
|> Ash.Query.filter(position > ^old_position and position <= ^new_position)
|> Ash.bulk_update!(:decrement_position, %{},
strategy: :stream,
actor: actor,
tenant: organisation
)
end
defp shift_up(old_position, new_position, organisation, actor) do
Activity
|> Ash.Query.filter(position < ^old_position and position >= ^new_position)
|> Ash.bulk_update!(:increment_position, %{},
strategy: :stream,
actor: actor,
tenant: organisation
)
end
## Inside resource:
update :update_position do
accept [:position]
change Omedis.Accounts.Changes.UpdateActivityPositions
require_atomic? false
end
update :decrement_position do
accept []
change atomic_update(:position, expr(position - 1))
end
update :increment_position do
accept []
change atomic_update(:position, expr(position + 1))
end