I had considered most of the solutions listed in the article except for the true fraction method. I believe that solution was explored by the author as they thought floating points were somehow solving a problem that couldn't be solved by integers. I settled on an approach that is essentially Solution 2 but using integers instead, each time bisecting the other two integers. The important aspect is that when inserting at the head or tail of the list, you bisect between MININT (-2^63) or MAXINT (2^63-1) respectively.
It might be possible to have a table that keys to the first that has a level and a value. For 1.1.26.1 above, it would have the rows (1, 1), (2, 1), (3, 26), (4, 1) (with keys to the table rows). Not sure exactly how to write the sql to join it all together and sort it, but it seems possible, at least if you have some known maximum number of levels.
If your problem is there are too many items in the list, that's going to be a usability problem so you'd probably need to categorize the items.
If performance is such an issue that ordering <50 items with approach 1 is unacceptable you undoubtedly have performance issues with the rest of your app too.
I guess you can always swap pairs of rows with subtraction, like swapping row 5 and 7 would be:
update t set user_order = 5 + (7 - user_order) where id in (123, 456)
Maybe you could use case statements to make an enumerated function in the more general case of updating multiple rows, but that's a bit ugly.http://sqlfiddle.com/#!17/b3d19/1
You can do something similar in MySQL with temp tables.
Bulk update through upsert (slightly different than a bulk update, since it can create rows too)
MySQL: http://sqlfiddle.com/#!9/a6f050/1
Postgres: http://sqlfiddle.com/#!17/a8367/1
UPDATE example
SET position = CAST(temp.position AS INTEGER)
FROM unnest(:ids, :positions) AS temp (id, position)
WHERE example.id = CAST(temp.id AS INTEGER); update items
set pos = case
when pos = P then Q
when P < Q and pos between P and Q then pos - 1
when Q < P and pos between Q and P then pos + 1
else pos
end
Plus a where clause to limit the shuffling to the actual list being reordered, since you would keep all users' lists in the same table. update t
set user_order =
case
when id = 123
then 500
else user_order + 1
end
where id = 123 or user_order >= 500;Implement a specific call that takes a list of all IDs in the new order, and batch run the UPDATEs on the server side.
I did enjoy the article, but did not realize it was PostgreSQL specific until towards the end.
Very clever, Argyle.
(1) If updates fail, you don't get a resulting broken state
(2) The size of the lists is not that large (<50), so the extra writes are not that expensive.
Essentially, with any variation on fractional updates (where only 1-2 rows are updated per list update), the client is sending to the server "move item a to position x". Then, shortly after, "move item b to position y", etc. And if any intermediate step fails, the server and client are out of sync.
If, instead, the client message is "here is the new state of the list: (a,b,c)" and the server updates, failed requests only leave the state temporarily out of sync. The client will continue to send the complete correct state at each update.