Multi-Users Chat Room Data Structure in Firebase Database

Multi-users chat room data structure in Firebase Database

If I understand the question, the app has multiple users and those users could have chats 1-1 or 1-many and you want to avoid users getting events for chats they are not part of.

Let's start with a users node

users
uid_0
name: "Bill"
chats:
chat_id_0: true
chat_id_9: true
uid_1
name: "Jesse"
chats:
chat_id_0: true
chat_id_6: true
.
.
.

and then a simplified chats node

chats
chat_id_0:
uid_0: "Hey there, Jesse"
uid_1: "Sup Bill?"
chat_id_6:
uid_0: "This is Bill, anyone around?"
chat_id_9:
uid_1: "Look ma, no hands"
uid_7: "Impressive"

In the above, chat_id_x is created with .childByAutoId when the user starts a chat.

When Bill logs in (uid_0) read in his chats node and then add observers to the chats listed within that node. In this case, Bill is part of chat_id_0 and chat_id_9. Do the same process for Jesse who is part of 0 and 6.

Both Bill and Jesse are having a chat in chat_id_0, Bill is awaiting a reply from any users also watching chat_id_6 and Jesse is conversing with uid_7 in chat_id_9.

From there any time there's an event (.childAdded when a new message is added to a chat for example) only those users observing that chat ('subscribed') will receive an event.

Obviously more detail would be included in each chat node (timestamp etc)

how do I check if there's already a chat with the same recepient (or
recepients) based on the chatId

There's several ways to tackle this;

One is that when an observer is added to each of the chats you are part of (.childAdded) each child node within that chat node will be returned to your app. This is good for two things - one is to populate the UI with the existing chat messages and secondly... you will then know who was involved with that chat. Keep those UID's in an array with the chat id, and before sending a chat to that person see if they exist in the array as an existing chat.

A second option is to query for all the chat's the users uid appears in (query using .observeSingleEvent). That will return all of the chat nodes they belong to and then have the uid's of who else they've chatting with and the chat id's as well.

Another option is when the chat is created, /chat_id_0, add a child node called /who_dat that keeps a list of who was invited to the chat

chats
chat_id_0
who_dat
uid_0: true
uid_1: true
uid_2: true
messages:
uid_0: "Hey there, Jesse"
uid_1: "Sup Bill?"

in this case uid_2 was also added to chat_id_0 but haven't heard from them yet.

Firebase database structure for chat application

It sounds like you want to:

  1. Have chat "rooms" between users
  2. Show a list of each user's chat rooms, with the latest message for that room

If those are your requirements, I'd model precisely those in your database.

So for each chat room (a chat between a certain set of users), model the messages for that room:

chats: {
$roomId: {
$messageId: {
senderId: "..."
message: "..."
}
}
}

Now for each user, model a separate list of their chats and the latest message:

userRooms: {
$uid: {
$roomId: {
"message: "..."
}
}
}

Now whenever a user posts a message to a room, you will need to push that message to /chats/$roomId and for each user in that chat room write the message to /userRooms/$uid/$roomId (overwriting the exiting message there).

This type of data duplication is known as fanning out data, because you're spreading a single snippet of information over multiple places in the database. It is quite common in NoSQL databases, and is part of the reason they scale so well: they trade write complexity for read performance.

Firebase data structure for one to chat app or private

When a user publishes a product (the potential seller), you'll need to associate their user ID with that product. Based on that, the interested user (potential buyer) can make the connection.

Given your other requirements, I'd nest the chats:

chats: {
uid1_uid2_productid: {
pushid: { message: ..., timestamp: ..., sender: ... },
pushid: { message: ..., timestamp: ..., sender: ... }
}
}

And then associate these chats with the correct users in user-specific lists:

user_chats: {
uid1: {
uid1_uid2_productid: true
},
uid2: {
uid1_uid2_productid: true
}
}

Instead of true you could also store a value (or more properties) that help you display the list of chats for that specific user.


If you want each user to have a completely separate copy of the chat/room, your easiest approach is to duplicate the rooms. So:

chats: {
uid1_uid2_productid: {
pushid: { message: ..., timestamp: ..., sender: ... },
pushid: { message: ..., timestamp: ..., sender: ... }
}
uid2_uid1_productid: {
pushid: { message: ..., timestamp: ..., sender: ... },
pushid: { message: ..., timestamp: ..., sender: ... }
}
}

And then:

user_chats: {
uid1: {
uid1_uid2_productid: true
},
uid2: {
uid2_uid1_productid: true
}
}

Best way to manage Chat channels in Firebase

A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.

You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:

var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2

var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);

console.log(user1+', '+user2+' => '+ roomName);

user1 = "Eusthace";
user2 = "Frank";

var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);

console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>

Firebase data structure for chat app

The only issue you may run in to is how the data is called. Note that when you call the 'Chat Log', because it is a child of 'Users' and 'chatPartners', you will be calling the data of everything in that branch, essentially loading every piece of data in the database under 'Users', which is time and performance sensitive.



Related Topics



Leave a reply



Submit