14

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!

AL.
  • 36,815
  • 10
  • 142
  • 281
Agustin
  • 143
  • 1
  • 1
  • 5
  • Do you have chat rooms or there is only a single chat room for those 1000 users? – Alex Mamo Jun 14 '17 at 07:38
  • 1
    It's not a Chat Room. It's an app like whatsapp. The problem is the childlistener in "Messages" node. This node has all messages of all users, then if a user sends a message to another user, it reaches all users. – Agustin Jun 14 '17 at 11:24

7 Answers7

58

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.

Linxy
  • 2,525
  • 3
  • 22
  • 37
  • 2
    I see. Anyway I would need to implement functions and triggers from firebase, right? – Agustin Jun 15 '17 at 11:33
  • 5
    @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 ? – kernelman Apr 27 '19 at 12:22
  • 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](https://stackoverflow.com/questions/57908240/firestore-query-where-field-is-not-equal-to-value-using-indexes-with-onsnapshot) – Tarvo Mäesepp Sep 13 '19 at 17:57
  • 1
    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. – Nischal Kumar Jan 04 '21 at 17:42
7

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" : "XXXXX.XXXX@gmail.com",
      "userPhotoUrl" : "https://lh3.googleusercontent.com/a-/AAuE7XXXXXXXXX"
    },
    "-LrDEBoLokW-5mhaT3yz" : {
      "id" : "-LrDEBoLokW-5mhaT3yz",
      "userDisplayName" : "Ishaq Bhojani",
      "userEmail" : "XXXXXXX.XXXXXX@gmail.com",
      "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"
    }
  }
}
Shiv Kumar Baghel
  • 2,464
  • 6
  • 18
  • 34
Qadir Hussain
  • 8,721
  • 13
  • 89
  • 124
5

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.

Denton
  • 51
  • 1
  • 2
  • 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? – TakeMeHomeCountryRoads Jul 03 '20 at 09:27
  • 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 – Denton Jul 04 '20 at 19:23
  • 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 – TakeMeHomeCountryRoads Jul 09 '20 at 03:14
  • 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. – Denton Jul 10 '20 at 11:43
1

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.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
0

this structure doesn't support what you want to do, it better to change it by using something like channels, where a channel contains the messages between two persons, so when any one of them send a message the other one will be notified.

Oussema Aroua
  • 5,225
  • 1
  • 24
  • 44
  • 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? – Agustin Jun 14 '17 at 11:29
  • 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 – Oussema Aroua Jun 14 '17 at 12:36
  • In that case, a user should be connected to as many childlisteners as they have conversations. This is the right way? – Agustin Jun 14 '17 at 12:54
  • unfortunately yes, that why you need to use firebase functions so you don't need local notification and get push – Oussema Aroua Jun 14 '17 at 12:55
  • 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? – Agustin Jun 14 '17 at 14:01
  • 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] (https://firebase.google.com/docs/functions/database-events) – Oussema Aroua Jun 14 '17 at 14:04
0
{
    "users": {
        "userId": {
            "conversations": {
                "conversationId": {
                    "unseenCount": 0
                },
                "conversationId2": {
                    "unseenCount": 3
                }
        }
    },
    "conversations": {
        "conversationId": {
            "displayedMessage": "Message",
            "members": {
                "userId1": true,
                "userId2": true
            },
            "messages": {
                "messageId": {
                    "type": "text",
                    "text": "Hello",
                    "createdAt": "",
                    "senderId": "userId",
                    "status": "sent",
                    "payload": ""
                }
            },
            "lastMessage": "my last message"
        }
}

}

Daniel Lessa
  • 367
  • 3
  • 6
  • 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 :) – Eduard Jan 04 '19 at 20:49
-5

I think this will be the best structure for it:

{
  messages: {
    A8Fcn28ak9ask46: {
      chat_id: "combination of sender and receivers number",
      sender_id: "person sending the message", 
      receiver_id: "person send it to",
      text: "message that the user types",
      timestamp: "123981849404"
    },
    ...
  }
 }

then when u get the results, you can filter through the chat_id's in forward and in reverse, which will get the conversation between two people.

Hope it helps.

Ali
  • 33
  • 5
  • 3
    This is the most inefficient solution. You are making all users to get every data in the system. Your app will never scale. – Adi Oct 11 '18 at 13:09
  • @Adi agree that it is the most inefficient one, in the meantime can you advise why it can't scale? – Matthew So Jan 06 '23 at 08:38