Handling deleted id in Firebase - firebase

I'm building an app with a social network component using Firebase, currently if a user likes a post I create a node in the user document called likes and I add the post id, example:
users: {
k9EdVpyRJ2R2: {
likes: {
E36F50C: true
}
}
}
I'm wondering if the post gets deleted should I just handle the deleted post-id on client side when I get the likes ids? or is there a better way to trim the data (or even restructuring it since the app is not live yet)

You'd typically remove the likes at the same time as you remove the post. To efficiently determine what likes to remove, you should keep a link from each post to its likes (in addition to the user-to-likes mapping you already have).
With that you can use a single multi-location update to remove the post and all likes. See this blog post for examples: https://firebase.googleblog.com/2015/10/client-side-fan-out-for-data-consistency_73.html

Related

Firestore and Rules | Allowing/disallowing read access to post under a public/private user?

Situation
I have the following Firestore setup
/posts/{id}
/posts/{id}/comments/{id}
/users/{id}/followers/{userId}
A user profile can either be public or private. All users can see posts by public users, but only users who follow private users can see said post, ie. they are in the owner's followers collection.
Current Solution
The post doc looks like this:
owner_account_visibility: public || private
ownerId: uid
The comment doc looks the same:
owner_account_visibility: public || private
ownerId: uid
My rules look like this
match /events/{eventId} {
allow read: isValid();
match /eventComments/{commentId} {
allow read: isValid();
}
}
function isValid(){
return (resource.data.owner_account_visibility == "public" || exists(/users/$(resource.data.ownerId)/followers/request.auth.uid)))
}
Problem
I see problems/questions with this solution:
Problem: A user may create many posts, which in turn may have lots of comments. This means that if a user updates their account visibility, a cloud function has to update possibly thousands of post and comment documents
Problem: A user may load many private posts and comments, and for each one of those is a database read, which can get very expensive as the user scrolls their feed
Question: In the isValid() function, there are two conditions seperated by an OR sign (||). Does this mean that if the first condition returns true (resource.data.owner_account_visibility == "public") then the function will not check the second condition (exists(/users/$(resource.data.ownerId)/followers/request.auth.uid)), saving me a database read? If this isn't the case, then I will waste a loooot of reads when a user loads tons of comments from a post even though it is public...
Does anyone have a proposed solution to this problem? Any help would be appreciated :)
I solved this myself. In short, instead of letting a user set their accounts' visibility, I let them set each post's visibility. This is simply because that is the functionality I want in my app. Now, I can simply use resource.data.post_visibility == "public", avoiding the issue of having to update every post if a user changes their account's visibility. If the first condition is false, I do as I did in my current solution in the question (exists(/users/$(resource.data.ownerId)/followers/request.auth.uid)). Also, comments and replies to a post are opened to all authenticated users even though the post is set to private, since comments aren't necessarily the post owner's own content/sensible information

Firebase storage url, new file keep same access token

Duplicate of: Firebase storage URL keeps changing with new token
When a user uploads a profile pic I store this in firebase storage with the file name as the uid.
Lets say the user then goes and makes say 100 posts + 500 comments and then updates their profile image.
Currently I have a trigger which goes and updates the profile image url in all of the post and comment documents. The reason I have to do this is that when the image is changed in storage the access token is changed and this is part of the url so the old url no longer works.
What I want to do is not have the access token change. If I can do this I can avoid the mass updates that will massively increase my firestore writes.
Is there any way to do this? or an alternative?
Edit:
Another solution if you don't mind making the file public.
Add this storage rule and you won't have to use a token to access the file.
This will allow read access to "mydir" globally in any subfolder.
match /{path=**}/mydir/{doc} {
allow read: if true;
}
There are only two options here:
You store the profile image URL only once, probably in the user's profile document, and look it up every time it is needed. In return you only have to write it once.
You store the profile image URL for every post, in which case you only have to load the post documents and not the profile URL for each. In return you'll have to write the profile URL in each post document, and update it though.
For smaller networks the former is more common, since you're more likely to see multiple posts from the same user, so you amortizing the cost of the extra lookup over multiple posts.
The bigger the network of users, the more interesting the second approach becomes, as you'll care about read performance and simplicity more than the writes you're focusing on right now.
In the end, there's no singular right answer here though. You'll have to decide for yourself what performance and cost profile you want your app to have.
Answer provided by #Prodigy here: https://stackoverflow.com/a/64129850/10222449
I tried this and it works well.
This will save millions of writes.
var storage = firebase.storage();
var pathReference = storage.ref('users/' + userId + '/avatar.jpg');
pathReference.getDownloadURL().then(function (url) {
$("#large-avatar").attr('src', url);
}).catch(function (error) {
// Handle any errors
});

Request.auth.metadata in security rules?

I have a Firebase project where I'd like for users to be able to see when other users created their profiles. My initial hope was that I could use "user.metadata.creationTime" on the frontend to pass the date into the user's extra info document and verify that it is correct by having "request.resource.data.datecreated == request.auth.metadata.creationTime" as a Database Rule, but it looks like it is not possible according to the documentation.
Is there any way I can verify that the creation date is correct on the backend?
More info edit: Below is the code that is being triggered when a user creates a new account on my profile. The three values are displayed publicly. I'm creating a niche gear for sale page so being able to see when a user first created their account could be helpful when deciding if a seller is sketchy. I don't want someone to be able to make it seem like they have been around for longer than they have been.
db.collection('users').doc(user.uid).set({
username: "Username-156135",
bio: "Add a bio",
created: user.metadata.creationTime
});
Firestore rules:
match /users/{id} {
allow get;
allow create, update: if request.resource.data.username is string &&
request.resource.data.bio is string &&
request.resource.data.created == request.auth.metadata.creationTime;
}
user.metadata.creationTime, according to the API documentation is a string with no documented format. I suggest not using it. In fact, what you're trying to do seems impossible since that value isn't available in the API documentation for request.auth.
What I suggest you do instead is use a Firebase Auth onCreate trigger with Cloud Functions to automatically create that document with the current time as a proper timestamp. Then, in security rules, I wouldn't even give the user the ability to change that field, so you can be sure it was only ever set accurately by the trigger. You might be interested in this solution overall.

Android Firebase Generating Likes

I am working on an project somewhat it is same like an instagram but ,i am not getting how to mange likes when someone like the post then we can use transaction to count++ the likes but when if a user again see the post how to know that the user has already like the post and make the like button in active state?
You could store the liked posts for the user.
Either by storing them at the user:
users/{userId}/likes/{postId} = true
Or by storing them at a separate location:
likes/{userId}/{postId} = true
You could also store them at the post itself:
posts/{postId}/likedBy/{userId} = true
Thinking about it, I think the last option would probably be best as you could set the value in the same transaction as the count++ one.

Meteor: Single-Document Subscription

Whenever I encounter code snippets on the web, I see something like
Meteor.subscribe('posts', 'bob-smith');
The client can then display all posts of "bob-smith".
The subscription returns several documents.
What I need, in contrast, is a single-document subscription in order to show an article's body field. I would like to filter by (article) id:
Meteor.subscribe('articles', articleId);
But I got suspicious when I searched the web for similar examples: I cannot find even one single-document subscription example.
What is the reason for that? Why does nobody use single-document subscriptions?
Oh but people do!
This is not against any best practice that I know of.
For example, here is a code sample from the github repository of Telescope where you can see a publication for retrieving a single user based on his or her id.
Here is another one for retrieving a single post, and here is the subscription for it.
It is actually sane to subscribe only to the data that you need at a given moment in your app. If you are writing a single post page, you should make a single post publication/subscription for it, such as:
Meteor.publish('singleArticle', function (articleId) {
return Articles.find({_id: articleId});
});
// Then, from an iron-router route for example:
Meteor.subscribe('singleArticle', this.params.articleId);
A common pattern that uses a single document subscription is a parameterized route, ex: /posts/:_id - you'll see these in many iron:router answers here.

Resources