ruthback's avatar

Database design - order feature

Hi, I'm trying to figure out how to fit a feature into my database schema. It's a simple to-do app, but the items in the to-do list can be arranged by the user via drag and drop and I'd like to keep that custom order the next time the applications loads. I thought of creating a new column on the to-do called "order" of type integer, but I have a couple of concerns about this:

  1. If I make the new to-do be at the beginning of the list by default, taking order "0", I would have to displace the entire list of to-do's which seems inefficient.
  2. If I make the new to-do be at the end of the list by default, I'd have to count the number of entries first and use that number + 1. This is fine, but after deleting some items and then trying to add a new one there's a chance that it won't be at the end of the list.

So, as simple as this can be I'm still not quite sure how to go about doing this. Any ideas? Thanks!

0 likes
9 replies
LaryAI's avatar
Level 58

To handle the ordering of to-do items in your application, you can indeed add an "order" column to your to-do items table. However, to address your concerns, you can use a different approach that doesn't require updating all rows when inserting a new item at the beginning or dealing with gaps when items are deleted.

Here's a strategy that can help:

  1. Use a floating-point number for the "order" column instead of an integer. This allows you to place items between others without needing to update the entire list.

  2. When adding a new item to the beginning of the list, you can find the smallest order value and subtract a fixed amount from it to get the new item's order value.

  3. When adding a new item to the end, you can find the largest order value and add a fixed amount to it.

  4. When reordering items, you can calculate the order value of the dragged item based on the items before and after it.

Here's an example of how you might implement this in your database migration and application logic:

-- Example SQL migration to add 'order' column
ALTER TABLE todos ADD COLUMN `order` FLOAT NOT NULL DEFAULT 0;
// Example PHP code to insert a new item at the beginning
$smallestOrder = Todo::min('order');
$newItemOrder = $smallestOrder - 1;
$todo = new Todo;
$todo->order = $newItemOrder;
$todo->save();

// Example PHP code to insert a new item at the end
$largestOrder = Todo::max('order');
$newItemOrder = $largestOrder + 1;
$todo = new Todo;
$todo->order = $newItemOrder;
$todo->save();

// Example PHP code to reorder an item
$previousItemOrder = Todo::where('id', $previousItemId)->value('order');
$nextItemOrder = Todo::where('id', $nextItemId)->value('order');
$newOrder = ($previousItemOrder + $nextItemOrder) / 2;
$todo = Todo::find($itemIdToMove);
$todo->order = $newOrder;
$todo->save();

This approach allows for a large number of reorderings before you run into any precision issues with the floating-point numbers. If you ever do, you can write a script to "rebalance" the order values to be whole numbers again, but this should be a rare necessity.

Remember to always handle edge cases, such as when the item is moved to the very beginning or end of the list, or when there are no items before or after the one being moved.

When querying the to-do items, make sure to order them by the "order" column to display them in the correct order:

$todos = Todo::orderBy('order', 'asc')->get();

This solution should provide a balance between efficiency and functionality for your to-do app's ordering feature.

ruthback's avatar

I suppose I could simply use an integer value and keep decreasing or increasing as needed? I doubt that I'll get into the billions of notes anyway, but somehow using floating numbers doesn't seem right.

Also, how could I retrieve the lowest possible value in the order column? I thought of this but I'm not sure if this is proper, or whether it should be included in the controller or perhaps is better abstracted to a method in the model?

DB::table('notes')->min('order')
ruthback's avatar

@Thunderson My concern is not about how or where to sort things but how to store that information efficiently in the database.

Tray2's avatar
Tray2
Best Answer
Level 73

The simplest would probably be to use a index/priority counter that increment by by ten,

10 Task One

20 Task Two

30 Task Three

Then when you move a task three in to the second position then you give it a index of 15, and send that to the database using an ajax request, and on save you can either re-index them again, or register a job that re-indexes the tasks

10 Task One

20 Task Three

30 Task Four

ruthback's avatar

@Tray2 I like that idea, it gives enough flexibility and is simple enough. I guess I'm overthinking it? Well, better give it a shot Thanks!

1 like
Tray2's avatar

@ruthback It is easy to get too complex, sometimes its good to take a step back and look at it through K.I.S.S.

1 like
ruthback's avatar

@Tray2 Agreed. And btw, I've run into some of your blog posts through other discussions here about database design and they're incredibly useful. Thank for all thehelp!

1 like

Please or to participate in this conversation.