orderByChild not working in Firebase
I am trying to query my database such that it retrieves an ordered list based on a child key. I do it as follows (see below), but nothing happens, meaning that it returns an object ordered exactly in the same way as it is stored in the Firebase database. What is going on?
self.getAllProfiles = function () {
var qProfile = $q.defer();
var ref = new Firebase(FBURL);
ref.child("users").orderByChild('last_update').on("value", function (snapshot) {
console.log(snapshot.val()) // HERE IS WHERE IT SHOULD BE ORDERED
qProfile.resolve(snapshot.val());
}, function (errorObject) {
qProfile.reject(errorObject);
});
return qProfile.promise;
};
To add, my users node looks as follows:
users
/$username
/last_update
/id
/data
/profile_image
/display_name
Here is a snapshot:
Tester: Object
github: Object
last_update: 1447732462170
userId: "github:12345"
Solution 1:
When you call snapshot.val()
, you are getting back a JSON object. The order of keys in a JSON object is determined by your browser and not by Firebase.
To get the children in order use the built-in forEach
method of the snapshot:
self.getAllProfiles = function () {
var qProfile = $q.defer();
var ref = new Firebase(FBURL);
ref.child("users").orderByChild('last_update').on("value", function (snapshot) {
snapshot.forEach(function(child) {
console.log(child.val()) // NOW THE CHILDREN PRINT IN ORDER
});
qProfile.resolve(snapshot.val());
}, function (errorObject) {
qProfile.reject(errorObject);
});
return qProfile.promise;
};
You can leave the q.resolve()
call where it is: snapshot.forEach()
is not an asynchronous call.
Solution 2:
I know this question has been answered and is more than 1 year old, but since there are still some confusion in the comment section, I would like to add some information.
The problem
The original problem is that the OP want to retrieve an ordered list based on a child key from Firebase realtime database, but the .orderByChild('arg')
does not work as expected.
But what didn't work as expected is not .orderByChild('arg')
, but .on("value", callback)
. Because .on("value", callback)
works a bit of different from other eventTypes like .on("child_added", callback)
.
Example
Say we have a firebase realtime database as below:
{
myData: {
-KYNMmYHrzLcL-OVGiTU: {
NO: 1,
image: ...
},
-KYNMwNIz4ObdKJ7AGAL: {
NO: 2,
image: ...
},
-KYNNEDkLpuwqQHSEGhw: {
NO: 3,
image: ...
},
}
}
--
If we use .on("value", callback)
, the callback() will be called 1 time, and return an Object Array of 3 objects.
ref.on("value", function(snapshot) {
console.log(snapshot.val());
// Please see Frank van Puffelen's answer
}
--
If we use .on("child_added", callback)
, the callback() will be called 3 times, each time returns an Object, and they are returned in order.
ref.once("child_added", function(snapshot) {
console.log(snapshot.val());
// The objects are returned in order, do whatever you like
}
Conclusion
If you only need to fetch ordered data from firebase (e.g. to initialize UI.) Then ref.orderByChild('arg').once("child_added", callback)
suits you well, it is simple and easy to use.
However, if for some reason you need to use ref.orderByChild('arg').on("value", callback)
, then please see Frank van Puffelen's answer.
Reference
Please read Firebase Document for more information about on(eventType, callback, cancelCallbackOrContext, context)
, their arguments and their return values.
Another useful document: Work with Lists of Data on the Web
Solution 3:
For ordering using the value
event listener:
firebase.database().ref('/invoices').orderByChild('name').on('value', snapshot => {
snapshot.forEach(child => {
console.log(child.key, child.val());
});
}
If you want to reverse the order, try:
function reverseSnapshotOrder (snapshot) {
let reversed = [];
snapshot.forEach(child => {
reversed.unshift(child);
});
return reversed;
}
reverseSnapshotOrder(snapshot).forEach(child => {
console.log(child.key, child.val());
});
Please see: https://firebase.google.com/docs/database/web/lists-of-data#listen_for_value_events