I need a little help to better understand how to do something or the best approach in Meteor.JS
In my app I currently have a user which has topics and topics have a list of posts, each topic has a comments and a posts / comments count.
I would like to add collaborators to each topic IE a user can invite someone to add posts and comments to a specific topic basically sharing or collaborating under that topic which is then private to the two or X amount of users.
What's the best approach for this when it comes to Meteor Pub/Sub and Mongo collections.
Thanks,
Almog
There are many ways to solve this. For example, instead of tying topic to a single user, add a param that would hold _ids of all users allowed to the topic:
sampleTopic = {
_id: 'fpoierj9',
title: 'Sample',
userIds: [
'opijo42',
'ik03agg',
'po32a0v',
],
};
Now, in your publish channel, show topics that contain your user Id in said array:
Meteor.publish('topics', function() {
return Topics.find({userIds: this.userId});
});
Related
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
I learned about firebase and cloud functions recently and have been able to develop simple applications with them.
I now want to expand my knowledge and really struggling with Trigger Email Extension.
On a specific event on my firebase, I want to fire an email to the user in a custom format, but I am unable to even activate the extension for now.
Can someone please explain with example please about these fields marked in the picture?
I had this question too, but got it resolved. Here's your answer:
"Email documents collection" is the collection that will be read to trigger the emails. I recommend leaving named "mail" unless you already have a collection named mail.
"Users collection (Optional)" refers to a collection (if any) that you want to use in tandem with a user auth system. I haven't had this specific use case yet, but I imagine once you understand how Trigger Email operates, it should be somewhat self-explanatory.
"Templates collection (Optional)" is helpful for templates in which you can use handlebar.js is automatically input specific information per user. (eg. <p>Hello, {{first_name}}</p> etc.) Similar to the previously mentioned collections, you can name it whatever you want.
How to create a template (I have yet to actually implement this, so take this with a grain of salt):
In your templates collection, you want to name each document with a memorable ID. Firebase gives the example:
{
subject: "#{{username}} is now following you!",
html: "Just writing to let you know that <code>#{{username}}</code> ({{name}}) is now following you.",
attachments: [
{
filename: "{{username}}.jpg",
path: "{{imagePath}}"
}
]
}
...specifying a good ID would be following. As you can see, the documents should be structured just like any other email you would send out.
Here is an example of using the above template in javascript:
firestore()
.collection("mail")
.add({
toUids: ["abc123"], // This relates to the Users Collection
template: {
name: "following", // Specify the template
// Specify the information for the Handlebars
// which can also be pulled from your users (toUids)
// if you have the data stored in a user collection.
// Of course that gets more into the world of a user auth system.
data: {
username: "ada",
name: "Ada Lovelace",
imagePath: "https://path-to-file/image-name.jpg"
},
},
})
I hope this helps. Let me know if you have an issues getting this set up.
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
How would I structure my data in firebase to retrieve all posts that the current user has not commented on. I am very new to nosql and I can't seem to get my head out of a SQL way of structuring it.
This is my attempt at it:
Posts: {
someUniqueId: {
user: userid,
content: "blah"
}
}
Comments: {
someCommentUniqueId: {
comment: "ola",
post: someUniqueId,
user: userid
}
}
Now if the above is correct, I have absolutely no idea how I would query this. Is it even possible in NOSQL?
Firebase does not have a mechanism to query for the absence of a value. See is it possible query data that are not equal to the specified condition?
In NoSQL you often end up modeling data for the queries that you need. So if you want to know which posts each user still can comment on, model that information in your JSON tree:
CommentablePosts_per_User
$uid
$postid: true
This type of structure is often called an index, since it allows you to efficiently look up the relevant $postid values for a given user. The process of extracting such indexes from the data is often called denormalization. For a (somewhat older) overview of this technique, see this Firebase blog post on denormalization.
I recommend this article as a good introduction to NoSQL data modeling.
If I may suggest a couple of options:
Posts:
someUniquePostId:
user_id_0: false
user_id_1: true
comment: "dude, awesome post"
user_id_2: false
user_id_3: true
comment: "wicked!"
drive space is cheap, so storing all the user id's within the post would allow you to easily select which posts user_id_0 has not commented on by query'ing for user_id_0: false.
Alternatively you could flip the logic
Posts:
post_id_0:
user_id_1: "dude, awesome post"
user_id_3: "wicked"
post_id_1:
user_id_0: "meh"
user_id_2: "sup?"
Users:
user_id_0:
no_posts:
post_id_0: true
user_id_1:
no_posts:
post_id_1: true
This would enable you to query which posts each user has not posted to: in this case, user_id_0 has not posted to post_id_0 and user_id_1 has not posted to post_id_1
Of course, depending on the situation, you can also lean on client logic to get the data you need. For example, if you only care about which posts a user didn't comment on yesterday, you could read query them by .value of yesterday and do a comparison in code to see if their user_id is a child of the post. Obviously avoiding this if the dataset is large.
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.