Efficient way to check for existing related documents in NoSQL (Firestore)? - firebase

Following Current Datamodell
User
User ID
Video
VideoID
LikedBy (Subcol)
User ID
User ID
User ID
Now if a User visits a video I wanna show if he Liked the Video already or not (similar to youtubes button color if you liked already).
My current approach is querieing for a Document with the Key of the signed In UserID and if I find one it means the user liked the video. The problem is I have this for Artists that you can subscribe too similar to channels on youtube.
This alone created about 3x the initial Reads I have on Page Load.
I would like to hear if there is any more efficient way to query for such a thing or structure the data.
Be aware that if you suggest me to store all liked Shows in the User or Show Document that this is not scalable due to the 1MB Limit.

1) You can have a subcollection on the Users, storing the ids of the posts the likes.
2) You can create a users_likes, collections where the Ids is the user id and inside have an array with the ids of the posts the user likes.
3) Last, just make props called likes on the user collection an store the ids of the posts.
All options have a trade-off, I would make like a user and posts_likes query on load and keep that in memory (no external user is going to affect this).
Be aware that if you suggest me to store all liked Shows in the User or Show Document that this is not scalable due to the 1MB Limit.
If you are expecting a user to like more than 1 millions of posts... otherwise, storing 1Mb of only ids is a good idea... I use this same pattern for a user events tracking, I have events defined (equivalent to your posts) and the user make actions that correlate to those events (your likes), I have cases with more than 80K and it works like charm. I gave your 3 options, I would say, start with 3 until it doesnt work, then go to 2 and same process up to 1. Since you will work with array of ids, support yourself with this

My current approach is querieing for a Document with the Key of the signed In UserID and if I find one it means the user liked the video.
Yes, that's a correct approach.
This alone created about 3x the initial Reads I have on Page Load.
I don't know where this is coming from but there is certainly something wrong. Unfortunately, nothing in your question can help me see the problem.
I would like to hear if there is any more efficient way to query for such a thing or structure the data.
I don't understand much from your schema, but I would structure the database this way:
Firestore-root
|
--- users (collection)
| |
| --- uid (document)
| |
| --- //user properties
|
--- video (collection)
|
--- videoId (document)
|
--- likedBy: ["uid", "uid", "uid"]
As you can see, likedBy property is of type array. So once you get a video document, you can simply check the uid of the logged in user against the likedBy array. If it exists, it means that user has already liked that video, otherwise has not.

Related

How to structure a like query in Firestore?

I'm building a simple application where posts appear on a user's home page and he can like or unlike them.
first when there were no users in the applications, I made a simple boolean field "liked", as shown in the figure, to determine if a post is liked or un-liked. However, when I started working with users, I find it hard to find the perfect structure for the likes field.
In Firestore, I added a field named "likedBy" for each post, which contains a map with a key of each user's id and a boolean to determine if the user liked the post or not.
I don't know if this structure is suitable or not, and if not, is there a better way to reach my goal?
The likedBy field is enough to cover most use cases.
Just store in likedBy an array of all users who liked the post by user ID. When a user likes - add the user ID to the array and remove it upon unlike.
That means you can also remove the likes and liked field as you can get it from reading the likedBy array.
I would recommend going one step further and save more than the User ID. You could also save the User displayName and photoUrl. That way, you don't need to read ($$$$money$$$$) the documents of every Users who liked Posts in order to display their name and/or avatar.

Firebase efficiently model nXn relationship

Let's say I have the following scenario:
I have multiple events, that multiple users can attend. Also, users can attend multiple events. What's the best way of storing the required information with maintaining data consistency?
Here's what I came up with and why I don't really fancy them:
collection "events" -> event document -> subcollection "users" -> user document
Problem:
Each user exists on each event, resulting in multiple documents of each user. I can't just update user information as I would need to write to every relevant event document and fetch the relevant user documents.
Really a disaster if trying to make the least reads/writes possible
E.g.:
this.afs.collection('events').get().then(res => {
res.forEach(document => {
document.ref.collection('users', ref => ref.where('name', '==', 'Will Smith')).get()
//Change name accordingly
})
})
collection "users" -> user document -> subcollection "events" -> event document
Problem:
Each event exists on each user, resulting in multiple documents of each event. (Same problem as in the first scenario, just the other way around)
collection "users" and collection "events" with each having users and events as documents subordinate to them.
There's an array attending_events which has the relevant event id's in it.
Problem:
Kind of the SQL way of sorting things. There's the need of getting each document with a seperate query using a forEach() function.
E.g.
this.afs.collection('events').doc(eventId).get().then(res => {
res.users.forEach(elem => {
this.afs.collection('users').doc(elem.name).get()
//Change name accordingly
})
})
What am I missing, is there better approaches to model the desired architecture?
When using collection "events" -> event document -> subcollection "users" -> user document
It's not so bad as you might think. This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoSQL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase realtime database but the same rules apply in the case of Cloud Firestore.
I want to be able to change user information or event information without the need of fetching hundreds of documents.
If you think that user details will be changed very often, then you should consider storing under each user object an array of event IDs and not use a subcollection. In the same way, you should also add under each event object an array of UIDs. Your new schema should look like this:
Firestore-root
|
--- users (collection)
| |
| --- uid (document)
| |
| --- events: ["evenIdOne", "evenIdTwo", "evenIdThere"]
| |
| --- //Other user properties
|
--- events (collection)
|
--- eventId (document)
|
--- users: ["uidOne", "euidTwo", "uidThere"]
|
--- //Other event properties
Since you are holding only references, when the name of a user is changed, there is no need to update it in all user objects that exist in events subcollection. But remember that in this approach, to get for example all events a user is apart off, you should create two queries, one to get the event IDs from user document and second to get event documents based on those event IDs.
Basically it's a trade-off between using denormalization and storing data in arrays.
What's the best way of storing the required information with maintaining data consistency?
Usually, we create the database schema according to the queries we intend to perform. For more infos, I also recommend to see my answer from the following post:
What is the correct way to structure this kind of data in Firestore?

Firestore chat app - build messages home page

I have a Flutter chat app with Firestore RTDB backend:
messages (collection)
message_1 (document)
chat (collection)
chat_1 (document)
chat_2 (document)
users (array, document field)
user_id_1 (String)
user_id_2 (String)
user_info (map to store user info, like name, avatar etc)
message_2 (document)
chat (collection)
chat_1 (document)
chat_2 (document)
users (array, document field)
user_id_1 (String)
user_id_2 (String)
user_info (map to store user info, like name, avatar etc)
I want to create a home page where it shows all the chats a user is involved in, sorted by most recent, just like any normal chat app:
I know how to show the chats the user is involved in. Problem is, I don't know how to handle the sorting. There is one simple way to do this: each time a new message is sent, use a cloud function and update a field in the message document called lastSent, then do orderBy('lastSent', descending: true) in your query. The problem is, each time you send a message, you have to do two writes instead of one just to update this field. Is there a better way to handle this?
Note: My app is not solely a chat app, this is only part of the main app. Imagine a chat functionality similar to Airbnb, so the volume or frequency of chat messages may not be as large as Facebook messenger for example
The common solution is to do what you propose: store a latest_updated timestamp in the document of each "chat room".
That indeed means that you'll need to do two writes instead of one. But on the other hand, you can now determine the correct ordering with by just reading the "chat room" documents, instead of having read individual messages under it.
Note that, while it is certainly possible to do this with Cloud Functions, this can also be done directly from the client. You can even catch the requirement in security rules that a client can only write a new message, if they also set the latest_updated timestamp of the corresponding "chat room" document to the same value as the timestamp of the message, although this will incur the cost of one additional document read for each message you add.

How to know when client stop listening for changes in firestore for specific reference?

I am struggling to find a solution for an interesting thing I need to do in Firestore.
Would be awesome if you help me. I will try to explain:
I have something like poker tables, where users can create tables and join them. When a user has a table opened other users can see him in the table. When he leaves the table he should disappear ( here is where I have the problem )
Leaving the table can happen by navigating to other page, by closing tab, etc. He can be in the same table in different tabs, browsers or devices ( i can change this if there is no other option ).
So what I need is to display which players are connected to a table.
Can you think of an optimal solution? How would you implement this?
.
If it helps, some things I tried but don't work:
listen from server side when user closes the websocket that listen for changes in a table and, if there is no other socket opened for that user and table, remove that user from table.
=> it is not possible
create a setInterval clientSide to update a timestamp in db for the user in table every 10 seconds. Then, when displaying the table, filter players by this tiemestamp being newer than Date.now() - 15 seconds
=> it doesn´t work good, it has glitches and a lot of updates to DB
I also tried the buildPresence hack with realtime database but it doesnt help. It is just usefull for knowing that user is online, not when he leaves specific table.
So what I need is to display which players are connected to a table. Can you think of an optimal solution? How would you implement this?
The most simple solution I can think of is to create a collection of tables and store in each document an array of user ids:
Firestore-root
|
--- tables (collection)
|
--- tableId (document)
|
--- users: ["usersIdOne", "usersIdTwo", "usersIdThree"]
Now to check wich players are connected to a table, you can simply attach a realtime listener on a document/query so you can get data in realtime. This means that once a player joins or leaves a table, the listener will fire and you'll be notified instantly.
To add a user to a table, simply add its uid in the users array and to remove a listener, remove its uid from the users array. For more informations:
https://firebase.google.com/docs/firestore/manage-data/add-data
Please see arrayUnion and arrayRemove.

Firestore Data Modelling

My app has two Firestore Collection:
Events (collection)
Document
eventId
hostId (userId of organizer)
title
etc.
Users (Collection)
Document
userId
friends (array of userId's)
age
etc.
I would like for a user to query all events created by his friends.
I considered
do a seperate query for each friend and pull each friend events. This could get ugly with sorting on dates.
adding a hostFriends field in the event and use an array-contains user's Id query. But this will be problematic if someone add a friend after having created an event. I would have to sync this across continiously..
doing client side filtering, but this will lead to many unneccesary reads..
Would be happy to hear any additional idea's or if one of the above would make sense? many many thanks!
This is my first post, apologies if I made any formatting mistakes.

Resources