Firebase realtime database structure in chat app - android

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!

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.

Thanks to #Linxy for a brilliant answer
I have created a firebase database regarding #Linxy answer
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"
}
}
}

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.

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.

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.

{
"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"
}
}
}

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.

Related

Move from firebase to firestore

I'm working on an app that contains USERs and EVENTs.
Each event has several users and each users has several events.
In Firebase, both events and users as "details" key(see json below), and those details are duplicate so all the event users has the details of the event, so in my main activity I have a recyclerview that shows those events easly (and the same way for users list inside event activity.
To sync all those duplicates I use firebase functions.
Now I want to move ti firestore but I'm not sure how should it be.
From what I see I can have list of users IDs in event document and then do a query for all events with user.id = current_user_id.
The issue is that I have more fields per user (expenses, and I want to add more) So either I have also subcollection for each user, or have more complicate list.
Can I query the events according to their subcollection id?
I will appreciate any help with that.
The current Json:
{
"events" : {
"-L7v0K***" : {
"average" : 110,
"details" : {
"date" : {
"date" : 1520354933426
},
"location" : "Tel Aviv",
"name" : "test"
},
"items" : {},
"require_transactions" : [ {}],
"totalexpenses" : 220,
"users" : {
"ARKuwgrDHcNnXHoPlCgIBXOObjD3" : {
"details" : {
"uid" : "ARKuw***",
"userEmail" : "r***g#walla.com",
"username" : "R** G***"
},
"expenses" : 20
},
"pDFtk***" : {}
},
"-L84Gg***" : {}
}
},
"users" : {
"ARKuw***" : {
"details" : {
"uid" : "ARKuwg***",
"userEmail" : "r***g#walla.com",
"username" : "R** G***"
},
"events" : {
"-L7v0Kx***" : {
"date" : {
"date" : 1520354933426
},
"location" : "Tel Aviv",
"name" : "test"
},
"-L97_3***" : { }
},
"TAJK6***" : {}
}
}
}
Firebase is pretty close to firestore, i also started everything with firebase realtime and moved to firestore, to have more complex queries.
So, as i understood, you will have two primary collections, you can create them by:
firebase.firestore().collection('users')
firebase.firestore().collection('events')
inside users you will have a subcollection called events to trigger all events a specific user have, so it will be like this:
firebase.firestore().collection('users').doc(userId).collection('events')
To make queries, for example, to get all events that a specific user is attending you can type:
firebase.firestore().collection('users').doc(userId).collection('events').get()
(here you can do filters like limiting, 'where', byId... by 'field equals to'... all of this is the documentation)
this will return all eventsId that are in the collection he attended.
Remember to work thinking on scale to get lower cost.
If you can explain more the task you need i can work in a solution.
Documentation of firestore is very easy.

In Firebase database, how to keep data consistency when both push and update operations are needed?

Say I have the following database in Firebase, when a user send a new message to another user, it need to push a new message to "ChatThreads/hash(User1,User2)" and update both "UserChats/User1/User2" and "UserChats/User2/User1".
"ChatThreads" : {
"hash(User1,User2)" : {
"1" : {
"message" : "Hello",
"sender" : "User2",
"time" : 9835592
},
"2" : {
"message" : "hi",
"sender" : "User1",
"time" : 10000000
},
"3" : {
"message" : "I am boss",
"sender" : "User2",
"time" : 14835592
}
}
}
},
"UserChats" : {
"User1" : {
"User2" : {
"lastTime" : 14835592,
"latestMessage" : "I am boss"
}
},
"User2" : {
"User1" : {
"lastTime" : 14835592,
"latestMessage" : "I am boss"
}
}
}
How do I keep data consistency in the case?
In the document of Firebase, the method of creating a reference to multiple update's common parent and put all updates in a map was introduced. However, in this case, not only update but also push is needed.
When you call push() without any arguments, you get a unique key that's always generated on the client. (Push ids are always generated locally.) With that key, you can then construct the path to the location where you want to add the data you along with all the other data for that update.

Firebase, query specific keys from a large set of data

Any help is much appreciated. Thank you so much for your time.
Let us say, there is a large set of articles under Articles node.
"Articles" : {
"article1Key" : {
"articleAuthor" : "Author",
"articleFavByNo" : 21,
"articleKey" : "Key",
"articleName" : "Name",
"articlePostedOn" : "21/07/11",
"articleTopic" : "Topic"
},
"article2Key" : {
"articleAuthor" : "Author",
"articleFavByNo" : 21,
"articleKey" : "Key",
"articleName" : "Name",
"articlePostedOn" : "21/07/11",
"articleTopic" : "Topic"
},
...
"article10Key" : {
"articleAuthor" : "Author",
"articleFavByNo" : 21,
"articleKey" : "Key",
"articleName" : "Name",
"articlePostedOn" : "21/07/11",
"articleTopic" : "Topic"
}
The articles are posted by some authors. Registered user can browse through the articles, and like them. The liked articles keys are stored in Users profile under favArticles node. Since, the article contains huge amount of data under it, only the key and name of the article is stored under User profile.
"Users" : {
"ZtlIQ2d1qJT1XpmHuGxwFSwaiEy2" : {
"emailId" : "vs#gmail.com",
"favArticles" : {
"article1Key" : {
"name" : "article1"
},
"article7Key" : {
"name" : "article7"
},
"article4Key" : {
"name" : "article4"
}
},
"firstName" : "Vimala",
"image" : "default",
"lastName" : "Sridhar"
}
}
Let us say that the user has liked some 50 articles out of 1000. Now if I want to display the User favorite articles in a RecyclerView, how should I write my query to pick the specific articles from the article list?
Since you already keep a list of the favorites for each user, you'd just:
load that list of favorites
iterate over it
load the references article for each
If you're worried about the performance of this loop-and-load: Firebase loads all the articles in step 3 over the same connection, so the requests are pipelined. For reasonable numbers of articles, this is actually quite fast. See my answer here for more details: Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly

Firebase database is being updated at one node but is not affecting the other node

I have two siblings namely "intents" and "messages". when I update the messages fields it gets done properly but one the same procedure is done with intents fields, no change occurs. Below is the android-java code for the same.
IntentMessage testIntentMessage = new IntentMessage("Hello test intent", "Hello test intent");
FriendlyMessage tempMessage = new FriendlyMessage("namastey", "india", "http://imgsdown.1mobile.com/group1/M00/C1/21/S36rZla-KYqAV08jAABUIgbzI-E596.png","543587653");
mFirebaseDatabaseReference.child("intents").child("-sjhfdvshv").setValue(testIntentMessage);
mFirebaseDatabaseReference.child("messages").child("-KUVR-MkLOVwNkx6nm-_").setValue(tempMessage);
Below is my firebase database JSON file after above commands are executed:
{
"intents" : {
"-kjuvhsjkv" : {
"intentFields" : "testIntentFields 1",
"intentName" : "testIntentName 2"
},
"-sjhfdvshv" : {
"intentFields" : "testIntentFields",
"intentName" : "testIntentName"
}
},
"messages" : {
"-KUVR-MkLOVwNkx6nm-_" : {
"name" : "india",
"photoUrl" : "http://imgsdown.1mobile.com/group1/M00/C1/21/S36rZla-KYqAV08jAABUIgbzI-E596.png",
"text" : "namastey",
"timeStamp" : "543587653"
}
}
}
}
The real problem is :
when everything is alright with "messages" node, why is the "intents" node not being updated?
Given that I have made the IntentMessage class in the same way as FriendlyMessage class
I got my problem as commented by Wilik, I didn't mention the rules for "Intents" section in the "rules" section of the database. Now I'm getting proper data.

How to properly denormalize data in Firebase

I am very new to NoSQL and denormalization. However, I wish to allow the actions at SignUp within my app defined as:
If a username is already taken, then a user is not allowed to use it
If a phone number is already taken, then a user is not allowed to use it
Allow a new user to "sync" their phone number contacts with the server to determine who are presently users, and retrieve their respective uid's
I have the schema outlined as below given the quick need to check if a username/phone number is already present at user sign up, as well as the needed search and compare given if the new users contacts phone numbers are link to users already present within the app:
{
"presentUsersByPhoneNumber" : {
"1614#######" : {
"uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
},
"1614#######" : {
"uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"1614#######" : {
"uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
},
"1614#######" : {
"uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
}
},
"presentUsersByUsername" : {
"ak" : {
"uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
},
"ak2" : {
"uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"ak3" : {
"uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
},
"kja" : {
"uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
}
},
"users" : {
"1783917f-00e4-47a0-b2cd-987bdf185129" : {
"phoneNumber" : "614#######",
"username" : "ak3"
},
"99e4989b-a046-4c5f-9478-5ebd8bdc3ded" : {
"phoneNumber" : "1614#######",
"username" : "ak2"
},
"a96da7b1-7c4e-44bc-b82e-fc75bed52bcd" : {
"phoneNumber" : "1614#######",
"username" : "ak1"
},
"fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de" : {
"phoneNumber" : "1614#######",
"username" : "kja"
}
}
}
Is this approach going too fair in the act of denormalizaiton?
In NoSQL you should model the data for how your application needs to access it. Read this article on NoSQL data modeling for more information.
So if you need an efficient way to check whether a phone number or username is already taken, it makes sense to store mappings for those. The only thing I would probably do different there is to store them as simple types:
"phoneNumberToUid" : {
"1614#######" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
"1614#######" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"usernameToUid" : {
"ak" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
"ak2" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
}
One thing I noticed in your sample data is that you have a key ak in presentUsersByUsername, but there is no corresponding child in users with that name. This typically happens because your code either aborts half-way through or because you made a mistake at some point during development.
You can prevent many of these problems by:
using multi-location updates, so that all writes are sent to Firebase as a single command
ref.update({
'/users/a96da7b1-7c4e-44bc-b82e-fc75bed52bcd/username': 'ak1',
'/usernameToUid/ak': null,
'/usernameToUid/ak1': 'a96da7b1-7c4e-44bc-b82e-fc75bed52bcd'
});
This update is safest way to change the name from the user from ak to ak1, wiping the old mapping and adding a new one.
using validation rules ensure that a user for each name exists
"usernameToUid": {
"$username": {
".validate": "newData.parent().parent().child(newData.va()).child('username').val() == $username"
}
}

Categories

Resources