Two-phase index rewrite for drag-to-reorder list views.
Given a list of UUIDs in their new display order and an Ecto schema,
rewrites a position field on the matching rows to 1..N matching
the order of the input list. The write runs in two passes inside a
transaction — first to negative indices, then to positive — so a
unique index on the position column (should one ever be added)
wouldn't trip mid-update.
Consumers that need pre-write validation (scope checks, permission
guards) or post-write side effects (activity logging, PubSub
broadcasts) should wrap this helper rather than fold the logic in
here. PhoenixKitProjects.reorder_projects/2 is the reference
consumer doing exactly that — this module owns only the index-rewrite
primitive.
Non-UUID entries in the payload are silently filtered (a stale or
malformed drop event can't poison the rewrite). Duplicates dedup
last-write-wins via Enum.uniq/1.
Example
defmodule MyApp.Endpoints do
alias PhoenixKit.Utils.Reorder
def reorder_endpoints(ordered_ids) do
Reorder.reorder(MyApp.Endpoint, ordered_ids, :sort_order, repo: repo())
end
end
Summary
Functions
Rewrites field on the rows whose uuid appears in ordered_ids,
setting it to each UUID's 1-based position in the list.
Types
@type result() :: {:ok, non_neg_integer()} | {:error, :too_many_uuids}
Functions
Rewrites field on the rows whose uuid appears in ordered_ids,
setting it to each UUID's 1-based position in the list.
Returns {:ok, count} where count is the number of rows actually
updated in the positive-write phase (matches Repo.update_all's
count semantics — UUIDs in the payload that don't resolve to real
rows aren't counted). Returns {:error, :too_many_uuids} when the
dedup'd payload exceeds the configured cap. An empty / fully-filtered
payload returns {:ok, 0}.
Options
:repo— the Ecto repo to use. Defaults toPhoenixKit.RepoHelper.repo/0so it picks up the host app's repo.:max_uuids— payload cap, checked after dedup. Default500. Guards against runaway drop events from a misbehaving client.