How to structure a like query in Firestore? - firebase

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.

Related

Firestore Rules - Array validation

I am using firestore for creating a food blog where users can upload posts and like it.
Call it facebook for food.
Below I will give you summary of 2 firestore database collections which are going to be used in my question.
First is Users - where the document name is based on UID and it stores information like, username/emailID
Second is Posts - where the document name is based on UID, however in the structure of the collection
It has a column named as "Likes" which is an array object. This stores 2 values : 1. Name of the person who liked the post and 2. UID of the person who liked the post.
Please find the structure of LIKES column in posts document below :
I am trying to add more validations to the likes part of the document.
However, from postman it is possible to change the name of the person who is liking the post.
The logic of the restrictions has to be on 2 things.
LIKES - UID should be present in my users collection. Also, UID in LIKES column shouldn't be changeable.
LIKES - Name should not be allowed to be changed.
I tried a of way of doing it but it doesnt work :
exists(/databases/$(database)/documents/users/$(request.resource.data.likes[request.resource.data.likes.size() -1].userId))
What it does is checks the last UID of person liking that document. However, in this scenario if there are more than 1 Likes, the name/uid could be altered for the ones which were present earlier.
Not a very efficient way.
Also, Loops dont work in rules so I cant loop it up to get it.
Any help would be really appreciated.
Please let me know if there are any more snippets which would be required.

Firestore Rules to check if user liking post exists in users Cloud Firestore

I'd like to check if the user liking the post is already present in our users cloud Firestore.
There are two different collections here, need to check if one's reference is present in another.
Users collection has user details - UID/Name/Email
Posts collection has details : Post Text / Post Likes / Post Comments
Where Likes is an Map which has userId of person who has liked the image and userName.
From backend its possible to exploit the posts collection and enter malicious value in post documents.
I am trying to achieve this for ON UPDATE condition : To check the user liking post exists in users collection.
------ Tried Below So far but it doesnt work --------------------
match /posts/items/{document}
allow update: if exists(/database/$(database)/documents/users/$(request.resource.data.likes.userId));
What I want is to check if the users liking are already existing or not.
This doesnt work because the likes column is an array and can have multiple objects.
Any help would be great.

Best Practice to keep user data in firebase firestore?

I am using firebase as a backend for my Android App. And this app is a social media app where users can post, comment & like. so I am storing user data in user_collection and this collection is secured by security rules where if uid == auth.uid (any user can only access data of himself).
Now in this app when a user post something every user can see this post. And in post_collection I am saving userId in post_doc.
So the problem is I need to show name of user to other users and I have only userId but the problem is a user can't get name of other user by uid beacuse of security rules. now I have to solutions for this please tell me which one is better or you can also suggest any other solutions also?
I can use cloud functions getUserNameById() (Problem : I need to call this function very frequently in feed when user scroll)
I can store name also in post_doc (problem : when user changes his name then It will show old name in old post)
Thanks for you kind help
In a scenario like the one you describe, I would typically store the user name in each post doc. I would also ignore updates to the name, as I think of the user name in the post doc as a historical value: this is the name the user had when they posted this. Now you may want different behavior of course, in which case I recommend reading: How to write denormalized data in Firebase
Your approach with Cloud Functions is fine too, and quite common in some situations. But I tend to only use Cloud Functions for reading data, it the read operation itself is particularly complex, which isn't the case here. In a case like this, I'd recommend coming up with a data model that allows the use-case and security you want.
For example: if you create a collection usernames where each document has the UID as its document ID, and then contains a single field with the username for that UID, you could implement the lookup of the user name through Firestore.
So you could have:
Store the full user profile in /users/$uid.
Store the user name in /usernames/$uid.
Have a Cloud Function that triggers when /users/$uid is written and that updates /usernames/$uid.
The client then has read access to each /usernames/$uid document, or even to the entire /usernames collection in one go if needed.
This way the names can be cached on the client, and continue to work when the app is offline, unlike in your approach with a Cloud Function that looks up the user name.
Consider the solution: whatever public data you need (author name, author userpic link etc) just save it with the post at the time it had created.
So your Message Pojo will looks like:
id
authorName
text
etc..
and just display this name (authorName).
It will be the bad way to go any time to User_collection folder to take the name even if there are would be not strict security (becouse it takes time and document reads)

Saving users scores and favorites in Firestore Database

I am working in a small project that uses Firestore database as a backend. I explain about the database so it is understood what I need:
Basically I have a collection that contains a list of documents where each one of them represent a game. For each game I have the name, cover image, info, category, etc.
I also have a collection of the users, where I have the specific UID for each user (retrieved from the auth section), email, etc.
What I want now is to save the score that some user may have in some of these games, as well as the favorite games that the user could save. What I don't get to understand is how to create the connection between the users and the games. For example, I thought that I should save the users score creating a collection within each document(game) in the first collection that mentioned. But when I create this collection with ID "scores" it asks me for the first document where I have to facilitate an ID (if not automatic) and then I don't know how to proceed.
I have read also that I would have to create additional collections in the root folder like "favorites" or "scores" specifying the UID of the user but, how do I connect the user UID, the score, and game which the user got that score from?
I hope I explained myself properly. Thanks.
Firstly, I agree with Doug's comment above. The Firestore tutorial videos are a great resource!
In terms of connecting data to your user, you have some options. You can either:
Create sub-collections under each user. Such as /users/{user_id}/favorites. Favorites could be a sub-collection or an array of game_ids depending on your use case.
Store a userID field in the documents in a top level "scores" or "favorites" collection. Then you can query for scores in the /scores collection by adding a where userID == {user_id} clause to your query of the /scores collection.

How do I efficiently find if one set of nodes has elements contained in another in Firebase?

I am building a social media database schema, in which I have users, followers, tags and posts. To conform to the firebase model I have flattened the structure as suggested in the firebase documentation as seen below. The issue that I am struggling with is when a user selects a tag and sees a bunch of posts from the tagPosts table all related by tag returned, I would then like to show the posts created by the current users followers first.
In SQL this would be done with an inline query checking the users followers, against the posts returned by a specific tag.
However in firebase I am not sure how do this without downloading all the posts contained under the tagID node in tagPosts and checking through each post's creator against the node of Followers for the current user userID. This operation could easily grow out of hand for 100s of posts amongst 100s of users. Ive tried modeling off of this answer, How do I check if a firebase database value exists? and this article From SQL to Firebase — How to structure the DB for a social network app. Am I poorly structuring the data how do I fix this thank you so much.
`
Users-
-userID1
-misc. userData
-userID2
-misc. userData
Followers-
-userID1
-userIDOfFollower1
-userIDOfFollower2
Following-
-userID1
-userIDOfFollower1
-userIDOfFollower2
Posts-
-postID1
-userIDFromCreator
-misc. PostData
Tags-
-tagID1
-misc. TagData
TagsUsers
-tagID1
-userID1
-userID2
TagsPosts
-tagID1
-postID1
-postID2
Edits-Thank you Frank
In our storyboard flow we plan to have a user see a wall of tags determined by constantly updating popular score based on properties of the tag and where we predict the user may have interest. The user will then select a tag and see posts related to that tag, from those posts I would like to show the posts from a users followers before those of everyone else who’s post falls in the category of a specified tag.
I have considered two possibilities either I optimize on reads in which I would have to keep track of every time a users follower posts to a tag and record the tagID along with the postID in a node for every follower a user has who posted in a special node of FollowersTags which would have a structure of listing for each userID a list of users and the all the followers of a user posted to which would become 100s of writes for each post created directly proportional to the number of followers a friend has.
*creates a list of posts to a specific tag made by followers
FollowersTags
-userID1_tagID1(composite key)
-postID1
-postID2
-postID3
-postID4
-userID1_tagID2
-postID1
-postID2
-postID3
-postID4
Or I could optimize on writes as tried above, which presents us with our current predicament of having to perform a query 100s of times directly proportional to the number of posts in a tag.
Is there any way around these two options which of the two is the better approach.
Unfortunately I would not be able to predict the posts displayed to the user before they select a tag.
In the Firebase Realtime Database, I typically model the data in the database to what I show on the screen. So if you have a "wall" of recent, relevant posts for each user, consider modeling precisely that in your database: a list of recent, relevant posts (or post IDs) for each user.
UserWalls
userID1
"timestamp_or_push_id": "postId1"
"timestamp_or_push_id": "postId2"
userID2
"timestamp_or_push_id": "postId1"
"timestamp_or_push_id": "postId3"
While the problem of determining what to show remains the same, with this database model it's now a write-time problem, instead of a read-time problem.

Resources