0

I would like to get the last key (the latest message) from my realtime database but not sure how this can be achieved. I see from this link i need to get Last child of my firebase databse that I can use orderByKey().limitToLast(1) to get this but it looks like I need to specify the complete ref in order to achieve this. Is that correct? Or is it possible if I can orderByKey().limitToLast(1) on the val()? Or is there another way I can achieve this? Here is my messages structure in the database:

enter image description here

enter image description here

I have a timestamp child under each key as shown above which I thought I could query in order to extract the latest key but I really don't know how to do this. Can someone please help? Below is my code so far:

database().ref(`messages/`).once(`value`, snapshot => {
 if(snapshot.exists()) {
   snapshot.forEach(function (childSnapshot) {
      if(childSnapshot.key.includes(auth().currentUser.uid)) {
      console.log("show me the key: "+childSnapshot.key)

      //not working
      console.log("show last message: "+ JSON.stringify(childSnapshot.val().orderbyKey().limitToLast(1)))
      }
    })
  }
})

console.log(JSON.stringify(messages)) => [{"-MfqYBzbusp1Cljgxpan":{"unreadMessage":true,"user":{"name":"Mike","avatar":"xxxxxx","_id":"tFhmw5oQoPhk8nF2sx5rE5BFqw93"},"timestamp":1627634061437,"senderId":"tFhmw5oQoPhk8nF2sx5rE5BFqw93","notification":{"body":"Hey","title":"Project","imageUrl":"./assets/xxxxx.png"},"text":"Hey"}}]

console.log(JSON.stringify(unreadMsgs)) => []

AJDee
  • 147
  • 1
  • 14

2 Answers2

1

The orderByKey and limitToLast methods exists on a DatabaseReference and not on the value you fetch from the snapshot fetched earlier. It seems the parent key for all messages is of format userId1userId2. If you know this combination then you run your query this way.

const uidsKey = "uid1" + "uid2"
const query = database().ref(`messages/${uidsKey}`).orderByChild("timestamp").limitToLast(1)

query.once("value").then((snapshot) => {
  console.log(snapshot.val())
})

But it seems you are trying to get UIDs of others users who have chats with user1 and trying to real all nodes first. I won't recommend doing that as that might have issues with security rules and so on. Instead if you keep list of those UIDs somewhere else, it'll be better. But if you want to keep what you have right now, try this:

const userUID = auth().currentUser.uid

database().ref("messages/").once("value").then(async (msgSnapshot) => {
  const keys = Object.keys(msgSnapshot.val() || {})
  const userChatKeys = keys.filter(k => k.includes(userUID))
  //const otherUserIDs = userChatKeys.map(k => k.replace(userUID, ""))

  //userChatKeys will be chat IDs where current user is included
  //now follow the same steps mentioned in first code snippet
  const queries = userChatKeys.map(chat => database().ref(`messages/${chat}`).orderByChild("timestamp").limitToLast(1).once("value"))
  const lastMessagesSnap = await Promise.all(queries)
  
  const messages = lastMessagesSnap.map(m => Object.values(m.val())[0])) 
  console.log(`messages: ${messages}`)
  const unreadMsgs = messages.filter((msg) => msg.unreadMessage === true)
  console.log(unreadMsgs.length)
})

This will logs last message from each of user's chat.

Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
  • Thanks for the response. This doesn't seem to work. The timestamp is under another key (messages -> uid1+uid2 key -> message key -> timestamp). Would orderByChild still work for this. I'm getting the error 'm.val is not a function. (In 'm.val()', 'm.val' is undefined)' – AJDee Aug 01 '21 at 16:20
  • @AJDee can you log `userChatKeys` and share the output? Are you getting the parent chats IDs? – Dharmaraj Aug 01 '21 at 16:22
  • Yes, I am. ["GjiGzmEX3yOgvyyyUHDHER1mSoE2tFhmw5oQoPhk8nF2sx5rE5BFqw93", "h3kIhXphkQUSPhTibEgU0YsxStI2tFhmw5oQoPhk8nF2sx5rE5BFqw93", "I9tpdxGQodf3TE8JpLtefn0XTGJ2tFhmw5oQoPhk8nF2sx5rE5BFqw93", "OGLjfTllh4b3eIHRFkLhBX3CrZn1tFhmw5oQoPhk8nF2sx5rE5BFqw93", "Ysrux9flYbRatW14pRFKFb471Tz2tFhmw5oQoPhk8nF2sx5rE5BFqw93", "q2dggRbm5aVl1ZXXB7wg6Db7I0i1tFhmw5oQoPhk8nF2sx5rE5BFqw93"] – AJDee Aug 01 '21 at 16:32
  • @AJDee oh I got it. I forgot to add `.once("value")` inside that map function. Please try it now. It should be `const queries = userChatKeys.map(chat => database().ref(\`messages/${chat}\`).orderByChild("timestamp").limitToLast(1).once("value"))` – Dharmaraj Aug 01 '21 at 16:33
  • Thank you so much I can now see the object but how can I check value of the unreadMessage? Whether if it's false or true? Essentially, I just want to get the count number of unreadMessages. m.val().unreadMessage doesn't do this. – AJDee Aug 01 '21 at 16:44
  • @AJDee you can `const messages = lastMessagesSnap.map(m => m.val())` and then use map again to get unread ones. `const unreadMsgs = messages.filter((msg) => msg.unreadMessage === true)` then to get the count `console.log(unreadMsgs.length)`. – Dharmaraj Aug 01 '21 at 16:46
  • Thanks but unreadMsgs doesn't return anything. The console.log for messages is [{"-MfqYBzbusp1Cljgxpan":{"unreadMessage":true,"user":{"name"..... – AJDee Aug 01 '21 at 17:43
  • @AJDee those are messages which are unread. What are you looking for? – Dharmaraj Aug 01 '21 at 17:43
  • Yes, I want to get the unread messages but console.log(unreadMsgs) is undefined. msg.unreadMessage fails – AJDee Aug 01 '21 at 17:50
  • @AJDee do you have Discord by chance? or can you please log messages, unreadMessages and share screenshot? – Dharmaraj Aug 01 '21 at 18:02
  • Yes I do have discord. What is your username? I have also added the logs for messages and unreadMessages in my question above. – AJDee Aug 01 '21 at 18:26
1

Firebase Realtime Database queries work on a flat list of nodes. So if you have a specific path /messages/nodeid already, you can find the latest message under that, but you can't find the latest message across all of /messages.

Reading all messages from all chatrooms, just to find the latest message for each chatroom this user is in is really wasteful though. As you add more users to the app, you're driving up the bandwidth cost for them, and for yourself too.

I recommend keeping a separate node where you track the chat rooms for each user, as explained in my answer on Best way to manage Chat channels in Firebase. With such a node you can then easily determine just the chat rooms for the current user, and then load the latest message for each of them with something like:

database().ref(`user_chatrooms/${auth().currentUser.uid}`).once(`value`, indexSnapshot => {
  indexSnapshot.forEach((indexSnapshotChild) => {
    let chatroomId = indexSnapshotChild.key;
    let query = database().ref(`messages/${chatroomId}`).orderByChild("timestamp").limitToLast(1)
    query.once(`value`, (msgSnapshot) => {
      console.log(`Last message in ${chatroomId} was ${msgSnapshot.val().text}`);
    })
  }
})
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807