10

How would you store an ordered list of N items in Google Firestore?

For example, a todo list. I had a couple of ideas but neither seem that smart.

You could put a 'position' key on the item but that would require updating all of the items' position value when one changes.

You could store them in a sorted array and do some splicing to resort before persisting.

I'd be keen to hear what is the recommend approach.

user2420484
  • 123
  • 1
  • 7

4 Answers4

5

The approach I use works without updating all the documents, but only the one you are ordering using a position property (This is the way Trello lists work I believe)

Suppose you have a list with 5 elements:

- A (position: 1)
- B (position: 2)
- C (position: 3)
- D (position: 4)
- E (position: 5)

Let's say you drag C between A and B, (this is the easiest calculation) you would get:

- A (position: 1)
- C (old position: 3, new?) <---- you need to calculate this new position
- B (position: 2)
- D (position: 4)
- E (position: 5)

At this point, you just need to know the previous element position and the next element position:

(A + B) / 2 = 1.5

1.5 is the new position for "C"

- A (position: 1)
- C (position: 1.5) <--- update only this doc in your db
- B (position: 2)
- D (position: 4)
- E (position: 5)

Let's move C again, to the latest position. You don't have a next element, in this case you do last + 0.5:

- A (position: 1)
- B (position: 2)
- D (position: 4)
- E (position: 5)
- C (new position: 5.5)

Last, if you don't have a previous element... first you check if you have a next element, in that case you do: next / 2 otherwise, set it to 0.5.

Let's move C to the top:

- C (new position: A / 2 = 0.5)
- A (position: 1)
- B (position: 2)
- D (position: 4)
- E (position: 5)

Eventually, you would divide float numbers, but it's not a problem because they are nearly infinite. (depending on the language you use).

singuerinc
  • 447
  • 4
  • 14
  • Thank you @singuerinc for sharing this solution, this is by far the most effective / simple solution, very easy to implement and maintain, very powerful! Thank you so much! – Chun Pin Chen Jul 26 '22 at 15:03
2

You could store two lists in Firestore. One with your actual data (unordered) and the other with a map of id to order.

Here's a Dart example:

main() {

  List myList = [
    {'id': 'red', 'test': 'some data'},
    {'id': 'green', 'other': 'some other data'},
    {'id': 'blue'},
  ];

  List myOrderList = ['green', 'red', 'blue'];

  List orderByOtherList(List theList, String commonField, List orderList) {
    List listOut = [];
    orderList.forEach((o) => listOut.add(theList.firstWhere((m) => m[commonField] == o)));
    return listOut;
  }

  print(myList);
  print(orderByOtherList(myList, 'id', myOrderList));
}

Here's the example on DartPad: https://dartpad.dartlang.org/dc77acd6e4cfce608796a51cda3ee9ad

Then combine the streams with combineLatest2 from rxdart to order the actual list in your app whenever either list changes.

This example uses Dart, but you should be able to create something similar in other languages.

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
Simpler
  • 1,317
  • 2
  • 16
  • 31
  • Great solution! however the Firestore has Document size limit of 1MB, this might also need to put it into consideration for large app. – Chun Pin Chen Jul 26 '22 at 15:07
1

Firestore doesn't "store sorted lists" as one of its features. What it will do is build an index of documents in a collection using the values of document fields that you define. You can then sort documents based on that index. What you do with those document fields is completely up to you. If you need to re-write the values to suit your intended ordering, then do that.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Thanks, I was over thinking it. Just store a sorted array as a field on the document and update it each time. – user2420484 Mar 19 '18 at 14:44
  • 2
    This seems like a bad approach given that you're charged for each document write. For OP's example of a to-do list, every reorder of a to-do item is on the order of N document writes to Firestore. That's not only going to take time, it's going to cost a lot. – Patrick Dec 05 '18 at 23:41
  • I wish I had one. Unfortunately I think a better approach is up to the Firestore team to implement. – Patrick Dec 06 '18 at 00:09
  • @Patrick You are also free to file a feature request. http://firebase.google.com/support/contact/bugs-features – Doug Stevenson Dec 06 '18 at 00:15
0

You can use Realtime DB with id:data branch and id:idPrevious branch.

You are basically making a linked list, which will allow you to make 2 writes on add, and 1 on delete. Reordering / moving will require to rewrite 3 things. Here is an example:

Original

id:data 1:A, 2:B, 3:C, 4:D, 5:E

id:idPrevious 1:0, 2:1, 3:2, 4:3, 5:4

Modified ABCDE->ADBCE

id:data 1:A, 2:B, 3:C, 4:D, 5:E - stays the same

id:idPrevious 1:0, 2:4, 3:2, 4:1, 5:3 - 3 changes max

Further detail:

5:3 happened when D was deleted from the order. 4:1 happened when D was assigned a place after an item. 2:4 happened when D was sharing an idPrevious with another data chunk, so feeling sorry for kicking it out, it offers its id as idPrevious for the kicked item.