Firebase realtime database structure in chat app

23,804

Solution 1

If you do a structure like similar to this:

-chats
   - chatUID
       - members
           - userUID
       - lastMessageSent:messageUID
       - ... more properties  

-chatMessages
   - chatUID
     - messageUID
         - sentBy: userUID
         - messageDate:""
         - messageTime:""
         - message:""

-userChats
    - userUID
       - chatUID

you can attach a listener to /userChats/userUID, which will display active chats, and a listener to /chatMessages/chatUID, which will get all chat messages for a specific chat conversation.

This way is a lot easier to setup firebase security rules, and users will only receive chat messages which they are apart of.

Solution 2

Thanks to @Linxy for a brilliant answer

I have created a firebase database regarding @Linxy answer

enter image description here

Here is the complete JSON export

{
  "Chats" : {
    "-Lsfsd234xda" : {
      "lastMessageSent" : "-LrDEBo1-Message",
      "members" : [ "-LrDEBoLokW-5mhaT3ys", "-LrDEBoLokW-5mhaT3yz" ],
      "more_properties" : "goes here"
    }
  },
  "Users" : {
    "-LrDEBoLokW-5mhaT3ys" : {
      "id" : "-LrDEBoLokW-5mhaT3ys",
      "userDisplayName" : "Qadir Hussain",
      "userEmail" : "[email protected]",
      "userPhotoUrl" : "https://lh3.googleusercontent.com/a-/AAuE7XXXXXXXXX"
    },
    "-LrDEBoLokW-5mhaT3yz" : {
      "id" : "-LrDEBoLokW-5mhaT3yz",
      "userDisplayName" : "Ishaq Bhojani",
      "userEmail" : "[email protected]",
      "userPhotoUrl" : "https://lh3.googleusercontent.com/a-/AAuE7mB3KTbXXXXXXXX"
    }
  },
  "chatMessages" : {
    "-Lsfsd234xda" : {
      "-LrDEBo-MessageUID" : {
        "message" : "Hi there!",
        "messageDate" : "10/10/2019",
        "messageTime" : "10:16pm",
        "sentBy" : "-LrDEBoLokW-5mhaT3ys"
      },
      "-LrDEBo1-MessageUID" : {
        "message" : "Hello",
        "messageDate" : "10/10/2019",
        "messageTime" : "10:17pm",
        "sentBy" : "-LrDEBoLokW-5mhaT3yz"
      }
    }
  },
  "userChats" : {
    "-LrDEBoLokW-5mhaT3ys" : {
      "0" : "-Lsfsd234xda",
      "1" : "-Lsfsd234xda1",
      "chatUID" : "-Lsfsd234xda"
    }
  }
}

Solution 3

I know it's late to answer but for future readers although Linxy's answer is neater, I would like to point out a more efficient one having been tried both structures:

ChatMessages
   smallerUID_biggerUID
      messageUID
         sentBy : userUID
         messageDate : ""
         message : ""
      .
      .
   .
   .
UserChats
   userUID
      pairUID
        lastMessage : ""       
      .
      .
   .
   .

In this way, instead of first finding out the chatId then finding out which user is associated with that chatId, we can directly search which users should appear in our active chat tab and get thouse users' information (username, profilePicture). The reason for that is we can always calculate the chatId if we know the user's id we would like to message with. So for the message tab, we calculate the chatId (smallerUID_biggerUID) in client side and search for the messages in referencing it.

Solution 4

In order to structure your database, please read this post: Structuring your Firebase Data correctly for a Complex App. You'll find here for sure the answer to your question.

As a conclusion, try to flatten(denormalize) your database as much as possible.

Hope it helps.

Share:
23,804
Agustin
Author by

Agustin

Updated on February 04, 2022

Comments

  • Agustin
    Agustin over 2 years

    sorry for my bad English level, I'm from Argentina.

    I have the following messages data structure in Firebase:

    "messages"
       "-KezmqXSdKCNFFA432Uc___-KfCEwklG_y3naRDIUiY"
             "messageDate": "20170620"
             "messageTime": "18:44" 
             "message": "Hi"
       "-KezFDSAADFASFFS3221___-KASDF32324SDFASD1FS"
             "messageDate": "20170620"
             "messageTime": "22:23" 
             "message": "How are you?"
    

    Where -KezmqXSdKCNFFA432Uc, -KfCEwklG_y3naRDIUiY, -KezFDSAADFASFFS3221 and -KASDF32324SDFASD1FS are users.

    My problem is that I created a childEventListener in "messages" node to receive new users messages but I am receiving all the new messages of all the users (I'm logged in one user per app) because my childListener is in "messages" node.

    Is it correct that if I have 1000 users when adding a message, a new message reaches the 1000 users? (Assuming that within the app, you can check to which user that message belongs).

    Thanks!

  • Agustin
    Agustin about 7 years
    I understand but then I should create channels for each conversation between two users and let these users listen to that channel by a childlistener? In this case, should you create dynamic channels and dynamic childevents?
  • Oussema Aroua
    Oussema Aroua about 7 years
    the channel will be created once one of them send a message to the other and add it to both users channel list, you can use firebase functions to add trigers on the database to send the push notification or add a listener on the channels node and test if the user own that channel, or add event listener on the specified nodes
  • Agustin
    Agustin about 7 years
    In that case, a user should be connected to as many childlisteners as they have conversations. This is the right way?
  • Oussema Aroua
    Oussema Aroua about 7 years
    unfortunately yes, that why you need to use firebase functions so you don't need local notification and get push
  • Agustin
    Agustin about 7 years
    So I read about firebase functions, I should create a new channel in firebase from user "A" and then create a function to indicate to user "B" that he has received a message and that he must connect a listener to the channel. But that function, should be created from android?
  • Oussema Aroua
    Oussema Aroua about 7 years
    you will write a trigger on the database, when a channel changes send a notification to the other user at the channel check this [link] (firebase.google.com/docs/functions/database-events)
  • Agustin
    Agustin about 7 years
    I see. Anyway I would need to implement functions and triggers from firebase, right?
  • Adi
    Adi over 5 years
    This is the most inefficient solution. You are making all users to get every data in the system. Your app will never scale.
  • Eduard
    Eduard over 5 years
    While usable, that's very dumb. If I have 5000 conversations, every time you log in or the user gets updated, you will load all 5000 conversation IDs :)
  • kernelman
    kernelman about 5 years
    @Linxy userA and userB are in a chat room, and both have added a listener to /chatMessages/chatUID. Now if userA pushes a new message in chatUid, both users A&B will receive this new message through their listeners ? Isnt this a waste of Bandwidth for userA, since he himself pushed it. Is it possible to have a Db structure so that userA will only listen to messages from userB ?
  • Tarvo Mäesepp
    Tarvo Mäesepp almost 5 years
    How would you count unred messages in this case? This question is old and uses realtime database instead of firestore which I use but I have the same thing pretty much with collections. I also asked question
  • TakeMeHomeCountryRoads
    TakeMeHomeCountryRoads about 4 years
    Can you explain your reasoning a little more, I am confused about how you would calculate the uid clientside. Wouldn't you have to go through all of the users and mix and match?
  • Denton
    Denton about 4 years
    Not really sure if I got the unclear point so let me explain with an example. Say we have an activity displaying active chat users (users the client has at least 1 message with). To display this, we get the data under UserChats/userUID(the client's id). Then when the client clicks one of the users, we need to show the messages between the client and the clicked user. Every messaging pair is uniquely identified as "smallerUID_biggerUID". We already got the uid we would like to see the messages the client has with and we know our own id. So we can calculate this pair's chatId and get their mssgs
  • TakeMeHomeCountryRoads
    TakeMeHomeCountryRoads almost 4 years
    The smaller and larger UID makes sense to me, but using this system, wouldn't you still have to query all of the other users UIDs in order to calculate all of the other chat node ids
  • Denton
    Denton almost 4 years
    You can calculate the chatIds of the people we chat with before by attaching a listener to UserChats/userUID. But i think i got what you are asking now. To get all the userUIDs, You can simply get all the userUIDs from Users with any type of query you like and display them. Only when you click one of the users, you will need to calculate the chatid and attach a listener to chatMessages. So what im trying to say is you dont need to get or calculate all the chatIds. The recyclerview elements should contain only the pairInfo (pairUsername, pairUID etc), not the chatId.
  • PeakGen
    PeakGen over 3 years
    Can you please share the code you used to setup this structure?
  • Nischal Kumar
    Nischal Kumar over 3 years
    Also, this will require a listener for each charUid. Maintaining all these listeners could be memory intensive where a user generally has 1k chat rooms.
  • Morris S
    Morris S over 2 years
    how do you query for the receiver of the message without knowing the chatUID only the userUID of the sender?