Reputation: 329
I'm having a hard time understanding why this simple query is not giving me the expected results.
In my Firebase realtime database my structure looks like this:
Pretty simple, right? Next I have a cloud function that runs whenever I manually change one of the orbs
values under any of the users. This is my function:
exports.testTopPlayers2 = functions.database.ref('/TestUsers/{user}/Statistics').onUpdate(_ => {
const allPlayersRef = admin.database().ref('/TestUsers');
const query = allPlayersRef.orderByChild('orbs').limitToLast(2)
return query.on('value', (snap, context) => {
return console.log(snap.val());
})
Also very straightforward I would think. In regular english, what I'm expecting that function to do is: "When any of the orbs
values are changed, run this function to sort all of the users by their orbs value (lowest to highest) and take a new DataSnapshot of only the final 2 users' data (The users with the highest number of orbs)".
When I print to the console, I'm expecting to see the data from User3
& User6
because they have the highest number of orbs... but instead it seems to be sorting by the username (User5
& User6
) or simply not sorting at all. Here is the log output:
Which clearly does not sort by what I'm defining in my query.
Maybe I'm missing something obvious but I've stared at it long enough... hoping someone can spot a mistake in my function regarding how it's sorting.
I appreciate the help!
Upvotes: 0
Views: 936
Reputation: 329
Thanks to Frank's thorough answer, I was able to understand what needs to happen and got it working perfectly with minor tweaks.
With the exact modifications suggested by Frank, my output in the Firebase console looked like this:
Which is a lot better than what I was able to produce until now, but it still wasn't showing the exact data I wanted to use.
I wasn't interested in the key of the snap
(which is the top-level TestUsers
node), but rather I wanted the key of the player
which provides the username responsible for the high score.
Next, Instead of getting an [object] for the orb values, I needed the actual value of the orbs, so I had to dive into the children of each player
object to get that value.
This is my tweaked cloud function in all it's glory lol:
exports.testTopPlayers2 = functions.database.ref('/TestUsers/{user}/Statistics').onUpdate(_ => {
const allPlayersRef = admin.database().ref('/TestUsers');
const query = allPlayersRef.orderByChild('/Statistics/orbs').limitToLast(2)
return query.once('value').then((snap) => {
let results = [];
snap.forEach((player) => {
let username = player.key
let orbsValue = player.child("Statistics").child("orbs").val()
results.push( {Player: username, Score: orbsValue } )
});
console.log(results)
return results;
})
})
And the result of the Firebase log is this:
This is exactly what I was after and can now proceed :D
Upvotes: 0
Reputation: 598648
You're looking for:
allPlayersRef.orderByChild('Statistics/orbs').limitToLast(2)
Note that you'll also want to use once()
instead of on()
:
return query.once('value').then((snap) => {
return snap.val();
})
You'll also want to remove that console.log
around snap.val()
, since console.log()
doesn't return anything.
Finally: when you call .val()
on a snapshot, you're converting it to key-value pairs and lose the ordering information. If you want to maintain the ordering, you'll want to use forEach
:
return query.once('value').then((snap) => {
let results = [];
snap.forEach((player) => {
results.push( {key: snap.key, ...snap.val() }
});
return results;
})
Upvotes: 2