Justin
Justin

Reputation: 329

How to Sort/Filter Firebase Realtime Database with Query in Javascript

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:

enter image description here

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:

enter image description here

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

Answers (2)

Justin
Justin

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:

enter image description here

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:

enter image description here

This is exactly what I was after and can now proceed :D

Upvotes: 0

Frank van Puffelen
Frank van Puffelen

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

Related Questions