Jaye Renzo Montejo
Jaye Renzo Montejo

Reputation: 1862

Why does my Firebase query return the wrong order?

BACKGROUND

Hello guys, I am performing a query in Firebase that retrieves a list of messages:

ref.child("messages").on("child_added", function(snapshot) {
    console.log(snapshot.val());
});

The above query works fine. It displays the messages in order.

A message contains the following properties/fields:

<message-id>: { 
    user-id: <user-id>,
    message: "This is a sample message",
    time: 1446534920014
}   

PROBLEM

When displaying a message, I also need to display the user's name. So I need to add another query that retrieves the user's name using the user-id field:

ref.child("messages").on("child_added", function(snapshot) {
    var message = snapshot.val();
            
    // get the user's name
    ref.child("users").child(message["user-id"]).once("value", function(userSnapshot) {
        // logs the results in the wrong order
        console.log(message, userSnapshot.val().name);
    }
});

The problem was the inner query returns the list of messages in the wrong order. Why? And what should be the correct way in order to display the messages in the correct order?

EDIT

Ok here you go. Below is the exported JSON:

{
  "messages" : {
    "-K2FyInlsZqLdHou1QnA" : {
      "message" : "Message 1",
      "user-id" : "-K2Fxr1gdxynukjDzcIq"
    },
    "-K2FyOlw13MU9KHB5NQh" : {
      "message" : "Message 2",
      "user-id" : "-K2Fxr1gdxynukjDzcIq"
    },
    "-K2Fz2GxPgqGf8uDfK0d" : {
      "message" : "Message 3",
      "user-id" : "-K2Fy3uyw-RNcePo_Pn-"
    }
  },
  "users" : {
    "-K2Fxr1gdxynukjDzcIq" : {
      "name" : "John Joe"
    },
    "-K2Fy3uyw-RNcePo_Pn-" : {
      "name" : "Alfred"
    }
  }
}

My goal is just to display the messages (in order) along with the user's name. If I do this:

ref.child("messages").on("child_added", function(snapshot) {
    console.log(snapshot.val());
});

It displays the messages in order, i.e. Message 1, Message 2, Message 3. But I still don't have the user's name so I need to look into the users list to retrieve the user's name before displaying the message:

ref.child("messages").on("child_added", function(snapshot) {
    ref.child("users").child(snapshot.val()["user-id"]).once("value", function(userSnapshot) {
        console.log(snapshot.val(), userSnapshot.val().name);
    });
});

That returns a list of the messages including the user's name, but the problem is it's in the wrong order: Message 2, Message 1, Message 3

Upvotes: 2

Views: 1456

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 600130

The messages are retrieved in the order you'd expect. You can easily verify this by adding some additional logging:

ref.child("messages").on("child_added", function(snapshot) {
    var message = snapshot.val();
    console.log(message.message);

    ref.child("users").child(message["user-id"]).once("value", function(userSnapshot) {
        console.log(message.message+': '+userSnapshot.val().name);
    });
});

The output from this on my recent runs has been:

"Message 1"
"Message 2"
"Message 3"
"Message 2: John Joe"
"Message 1: John Joe"
"Message 3: Alfred"

What you see here in the first three lines is that the messages are in order. But you then start retrieving the user for each of the messages; and those users don't necessarily come back in the order you fired/expected them.

This a very common problem when using AJAX on the web: as soon as network traffic is involved, the order of your code is not necessarily the order in which things happen.

The solution is always the same: don't depend on your code executing in a specific order. For example: you most likely want to display the message in a list in the DOM somewhere.

var ul = document.getElementById('messages');
ref.child("messages").on("child_added", function(snapshot) {
    var message = snapshot.val();
    var li = document.createElement('li');
    li.id = 'msg_'+snapshot.key();
    li.innerText = message.message;
    ul.appendChild(li);

    ref.child("users").child(message["user-id"]).once("value", function(userSnapshot) {
        li.innerText = userSnapshot.val().name + ': '+ li.innerText;
    });
});    

Side note: you might want to consider adding a cache of user names. You'll now be calling out to the database for each message, for each user. This is an O(n^2) operation, so does not scale very well with the number of users.

Repro: http://jsbin.com/zofasi/edit?js,console

Upvotes: 3

Related Questions