Say I have NoSQL structure as follows:
messages
chat_id (known)
message_id (generated automatically)
{author, timestamp, content}
I also have users/
branch where I can see last logins of both users involved.
I want to get the number of unread messages for a given user by 1 query.
How would you implement such a task?
There are two parts to this problem:
Counting the number of messages
Assuming you are using the Firebase Realtime Database or Cloud Firestore, there are two typical approaches to counting:
Counting the messages client-side is easiest to implement, so typically what inexperienced developers start with. When the number of messages is low, that works fine. But as the number of messages grows, you'll be download more and more data.
At that point you'll switch to keeping a counter in the database and updating that on every write/delete operation. This duplicates data, so is typically something that developers with a background in relational databases struggle with. But it is the most common approach.
You can update the counter client-side, or use a server-side approach such as this example of maintaining counters in Cloud Functions.
Tracking what the user has read
To know how many unread messages a user has, you need to know what the user has already read. Here again, there are two common approaches:
Tracking the individual messages sounds most correct, so I see this being done a lot. I also see developers struggling with it, since it involves a lot of book keeping. It's definitely possible, but I'd recommend starting with a simpler approach...
Chat apps deal with (chronological) sequences of messages. If you know the ID of the latest message that the user has seen, you could assume that the user has also seen all messages that are older than that.
Say that I track the timestamp of the last message you read, and it's
1535725374298
. I can now query the chat to only retrieve messages newer than that:Provided you are using Firebase Authentication's user id's, uid's, which provides a consistent uid for a user across devices, setting message id's to the
/users/$uid
document is the way to go.Now, all you have to do is listen to the
/users/$user
document with the .onSnapshot()
. Once the client takes action to read a message in a snapshot, simply delete that message id from theusers/$uid
node.How you store the message id's in
users/$uid
(the userDocument) is kind of up to you - could be an array or could be a nested object, it might even be possible to store message id's individually as typereference
, provided there is a way to identifytype == reference
client-side (so you can iterate the doc fields looking for unread messages in your realtime snapshot).The advantage to this is that you simplify your code/system. Let Firebase send you updates (new unread messages) as they happen.