Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: Firebase Real-Time database orderByChild has no impact on query result

I have inserted data like this into Firebase Real-Time Database like this way:

enter image description here

And I query the database back like this way:

final databaseReference = FirebaseDatabase.instance.reference();
databaseReference
    .child('orders')
    .orderByChild('date_slug')
    .limitToFirst(pageSize)
    .once()
    .then((snapshot) {
  firstPageItems = snapshot.value;

  if (firstPageItems != null) {
    List<dynamic> curretList = [];
    firstPageItems.forEach((orderId, orderData) {

      print ("date_slug " + orderData['date_slug'] + "\r\n\r\n\r\n");
      curretList.add(orderData);
    });

    _list.addAll(curretList);
    _listController.sink.add(_list);
  }
});

However, the data didn't come back as sorted as I expected. See the output below.

I/flutter (17125): date_slug 2020-04-20 15:52:13
I/flutter (17125): 
I/flutter (17125): 
I/flutter (17125): date_slug 2020-04-20 15:52:11
I/flutter (17125): 
I/flutter (17125): 
I/flutter (17125): date_slug 2020-04-20 15:52:10
I/flutter (17125): 
I/flutter (17125): 
I/flutter (17125): date_slug 2020-04-20 15:52:12
like image 929
user1187968 Avatar asked Apr 20 '20 22:04

user1187968


1 Answers

As soon as you call firstPageItems = snapshot.value, you are converting the results into a map/dictionary. A dictionary can hold the keys and the values of the results, but it has no place for the relative order of the results.

To maintain the order of the results, you'll want to either observe onChildAdded:

var query = databaseReference
    .child('orders')
    .orderByChild('date_slug')
    .limitToFirst(pageSize);
query.onChildAdded
    .forEach((event) => {
      print(event.snapshot.value)
    });

If you need to know when all child nodes of your query have been handled, you can add an additional listener to the value event:

query.once().then((snapshot) {
  print("Done loading all data for query");
});

Adding this additional listener does not result in downloading extra data, as Firebase deduplicates then behind the scenes.


Alternatively, you can the FirebaseList class from the FlutterFire library, which uses that same onChildAdded and the other onChild... streams to maintain an indexed list.

An example of using this class:

list = FirebaseList(query: query, 
  onChildAdded: (pos, snapshot) {},
  onChildRemoved: (pos, snapshot) {},
  onChildChanged: (pos, snapshot) {},
  onChildMoved: (oldpos, newpos, snapshot) {},
  onValue: (snapshot) {
    for (var i=0; i < this.list.length; i++) {
      print('$i: ${list[i].value}');
    }
  }
);

As you can see this uses the onValue stream of the list to loop over the children in order. The onChild... methods are needed for the FirebaseList class, but we don't do anything meaningful with them here.


In recent versions of the FlutterFire library, the DataSnapshot class now has a children property, meaning you can finally do:

query.onValue((event) {
  event.snapshot.children.forEach((child) {
    print(child.value);
  });
})
like image 88
Frank van Puffelen Avatar answered Oct 01 '22 20:10

Frank van Puffelen