Storing data from MongoDB to state - best option - reactjs

I'm a newcomer to the MERN stack and just putting a little blog site project together for some practice...
In my case, I'm retrieving all the blog posts to list briefly on the home screen each with a link to the full blog article. In this case the data is already retrieved and stored in React state.
Question is, when I go to the full blog article is it better to take the data from the already retrieved full list of blogs saved in state or should I get the individual blog entry from the database again with using ID.
I'm confident implementing either - I'm just considering the best practice, or benefits/dangers of doing either? Could data get out of sync?
I'm a newbie - be gentle!
Thanks for all advice.

I think when fetching all blogs for home page, you should only fetch the data which you will show for each blog rather than fetching the whole blog.
Lets assume each of your blog has following fields:
{
"title" : "Title of blog",
"body" : "This is my blog, ...."
"comments": [comment1, comment2, ...]
"upvotes" : [],
"downvotes" : [],
"author" : ObjectId("ref to the author schema"),
"time" : 10:30 PM
}
on home page, i'm assuming you only need to show the author, title, time and some lines of the body so you need to only fetch those instead of getting the whole blog.
For that you can use .select() function of mongoose:
const blogs = await Blogs.find({}).select({ title: 1, time: 1, body: 1, author: 1 });
This way, you will only be getting the data you require. By implementing this, when a user clicks on a blog to view it, you can re-fetch the whole blog and get the latest result (in case the blog was updated or new comments/votes were added, the changes will be reflected).
Hope this answers your question.

Related

How to keep track of which posts have been liked by a user?

I am working on an application that allows users to like content, which will be visualized by changing an outlined heart icon to a fully filled heart icon if the user has liked that post.
I have a list-page that renders every post in the database as a Content Card component. That component contains the icon mentioned above. In order to decide whether the component should be rendered with the outlined or the filled heart, I need a way of establishing whether the user has liked the corresponding post or not.
What I currently do
Every like is it's own model instance of Like which has a Foreign key pointing to the post that is associated with it. I have an API endpoint at content/likes/?post_id=${postID} which gives me all of the users that have liked the given post.
For every post on the page, I call this endpoint and then loop over the response to see if the logged in user is in that list of users who liked the content. If that's the case, I set a piece of state to true and the heart is filled for that post.
My approach works fine but I am making a separate request for every post on the page. Let's say I have 100 posts displaying, I assume that it wouldn't be a best practice to make 100 requests just to fill that icon.
I have been thinking about ways to improve this but I am not sure how people usually handle this. Any help is appreciated!
How about a batch likes endpoint?
content/batch_likes/?post_ids=1,2,3,4,5...
and a return value of either liked IDs
[1,3,5]
or a map of liked-ness:
{"1": true, "2": false, "3": true, "4": false, "5": true}
These can both be implemented very efficiently:
List:
liked_ids = Like.objects.filter(user=self.request.user, post_id__in=post_ids).values_list('post_id', flat=True)
Dict (using the above):
like_map = dict.fromkeys(post_id, False)
like_map.update({liked_id: True for liked_id in liked_ids})
EDIT:
You can also expose this in a DRF serializer:
class PostSerializer(serializers.ModelSerializer):
user_liked = serializers.SerializerMethodField()
# ... other fields and meta ...
def get_user_liked(self, obj) -> bool:
# See if we'd cached this...
if hasattr(obj, '_user_liked'):
return obj._user_liked
# Or if we didn't, do a query (boo-hoo, slow)
return self.obj.like_set.filter(user=self.context['request'].user).exists()
You can then add a Prefetch in your DRF viewset to get that _user_liked "cache" property for your objects in a single query.
Another mechanism for doing the user like cache might be to stow a map in that serializer context.

How to create the following data structure in a NoSQL environment

Intro
I have a FireStore database similar to a social media db, with 3 collections Users, Events, and EventUpdates. My goal is to create a feed with eventUpdates created by me and my friends. So i have to expand my database with friendship connections. But i struggle with 3 problems, and hopefully somebody here can push me in the right direction to solve these.
Problem/Question 1:
I added username and user image to the EventUpdate model so it's easier to query. I've heard denormalise is the way to go in a NoSQL database. But if a user updates his user image, i've to update all eventUpdates created by that user. Sounds like something you don't wanne do. But is there a better way to do this?
Problem/Question 2:
How can i create a data structure that is optimised for performing the following query: get eventUpdates from me and my friends ordered by date.
Problem/Question 3:
How to store likes? I can keep a counter in a eventUpdate. But this becomes a problem when i denormalise eventUpdates (see current solution underneath EDIT)..
Data structure example .
{
"users": {
"1": { "name": "Jack", "imageUrl": "http://lorempixel.nl" }
},
"events": {
"A": {
"name": "BeerFestival",
"date": "2018/09/05",
"creatorId": "1"
}
},
"eventUpdates": {
"1": {
"timestamp": "13243543",
"creatorId: "1",
"creatorName": "Jack",
"creatorImageUrl": "http://lorempixel.nl",
"eventId": "A",
"message": "Lorem ipsum"
}
}
}
EDIT
OK, after some trial and error i ended up with the following structure. This structure seems work, but my problem with this solution is that i need to make a lot of write calls to update a single eventUpdate because of all the copies in each feed (1000 followers means 1000 copies). And it looks like i need to do that a lot.
I would like for example to add a like button to each event update. This trigger an update on all EventUpdate copies. For me it looks like firebase is not suited for my project and i'm thinking of replacing it with a SQL DB, or can anyone here change my mind with a better solution?
{
"users": {
"user1": { "name": "Jack",
"imageUrl": "http://lorempixel.nl",
"followers": ["user1"]
}
},
"feeds": {
"user1": {
"eventUpdates": {
"1": {
"timestamp": "13243543",
"creatorId: "1",
"eventId": "A",
"message": "Lorem ipsum"
}
},
"following": {
"user1": {
"name": "Jack",
"imageUrl": "http://lorempixel.nl",
"followers": ["user1"]
}
}
},
"events": {
"A": {
"name": "BeerFestival",
"date": "2018/09/05",
"creatorId": "1"
}
}
}
I added username and user image to the EventUpdate model so it's easier to query. I've heard denormalise is the way to go in a NoSQL database.
That's right, denormalization and is a common practice when it comes to Firebase. If you are new to NoQSL 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 same rules apply to Cloud Firestore.
But if a user updates his user image, i've to update all eventUpdates created by that user. Sounds like something you don't wanne do. But is there a better way to do this?
Yes, that's also correct. You need to update all the places where that image exists. Because you have chosen google-cloud-firestore as a tag, I recommend you see my answer from this post because in case of many write operations, Firestore might be a little costly. Please also see Firestore pricing plans.
Regarding Firestore, instead of holding an entire object you can only hold a reference to a picture. In this case, there is nothing that you need to update. It's always a trade between these two techniques and unfortunately there is no way between. You either hold objects or only references to objects. For that, please see my answer from this post.
How can i create a data structure that is optimised for performing the following query: get eventUpdates from me and my friends ordered by date.
As I see, your schema is more a Firebase realtime database schema more than a Cloud Firestore. And to answer to your question, yes you can create. So talking about Firestore, you can create a collection named eventUpdates that can hold eventUpdate objects and to query it according to a timestamp, a query like this is needed:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference eventUpdatesRef = rootRef.collection("eventUpdates");
Query query = eventUpdatesRef.orderBy("timestamp", Query.Direction.ASCENDING);
But please note that the timestamp field should be of type Date and not long. Please also take a look at my answer from this post, to see how you can add a date property in a Cloud Firestore database.
How to store likes? I can keep a counter in a eventUpdate. But this becomes a problem when i denormalise eventUpdates (see current solution underneath EDIT)
You can simply add likes but I recommend you see the last part of my answer from this post. So you might consider adding that count in a Firebase realtime database rather than in Cloud Firestore. Both databases work very well together.
This structure seems work, but my problem with this solution is that i need to make a lot of write calls to update a single eventUpdate because of all the copies in each feed (1000 followers means 1000 copies). And it looks like i need to do that a lot.
You might also take a look at my answer from this post.
For me it looks like firebase is not suited for my project and i'm thinking of replacing it with a SQL DB, or can anyone here change my mind with a better solution?
I don't think this way. There are many apps out there that have the exact mechanism as yours and are working very well.
If you want your feed items to be in sync with the real users data (new profile image when the user changes it for example) you can simply store the user ID in the eventUpdate document. This way you don't have to keep them in sync manually, and every time you have to display the item in the feed you could simply fetch user data, and easily query many eventUpdates on userId and created_at fields ( assuming you have them ).
To implement likes in your feed the solution depends on a bunch of things like traffic volume.
The simplest way is to update a likes field with a transaction, but Firestore has a maximum updates frequency on a single document of 1 second. Plus, a transaction can easily fail if more than 5 transactions are trying to update the same document.
To implement a more solid likes system take a look at this page from the official Firebase docs.
Firestore has a different approach to the NoSQL world. Once you know the data you will use (as You already do) there are some very important points about what architecture the data will have. And It depends a lot about how the data grows, what kind of queries you will need and how often you will use them. Some cases You can create a root collection that aggregates data and queries might be easier.
There is a great video from Firebase Channel that might help. Check it out!
How to Structure Your Data | Get to Know Cloud Firestore #5
https://www.youtube.com/watch?v=haMOUb3KVSo
[UPDATED] December 26th
Others videos that might help to model and query your data is these videos:
How to Connect Firebase Users to their Data - 3 Methods
https://www.youtube.com/watch?v=jm66TSlVtcc
How to NOT get a 30K Firebase Bill
https://www.youtube.com/watch?v=Lb-Pnytoi-8
Model Relational Data in Firestore NoSQL
https://www.youtube.com/watch?v=jm66TSlVtcc

How to retrieve all children from Firebase Database

I'm trying to learn how to use Firebase by creating a simple social media application.
https://i.stack.imgur.com/wfeyw.png (Here is how my database is laid out)
I was able to add data to the database but I'm having trouble understanding how to retrieve data. I want to be able to retrieve all the posts from a certain user and then display each one as an item in a list.
let Posts = firebase.database().ref('Posts').child(userId);
So far thats my query and here is the code I'm using to add the posts to the database. Could someone also tell me what is the term for the Id that is being generated for each post?
firebase.database().ref('/Posts').child(userId).push({
Text: post,
Score: 0
});

Single piece of content, multiple URLs?

I have a use case that I could use some advice on.
We publish multiple products, each of which has it's own subtree on the site. Generally, a piece of content gets published to just a single product, e.g. a news article gets published to product A and can be accessed at one URL.
However, sometimes we have content that we want to publish to multiple products, e.g. a single news article gets published to products A, B, and C and will be available at 3 different URLs.
With our current CMS we end up doing this by copying and pasting the content, which is a hassle for editors, especially if the content needs to be updated.
An ideal scenario would be where and editor edits the content in one place, specifies the products to publish to, and the content is served by more than one URL and with a template that is product-specific.
It seems that RoutablePageMixin could be useful here, but I'm not sure how to handle letting the editor specify the destination products and making the routing aware of that choice.
Has anyone solved a similar problem using Wagtail?
I have solved a similar problem in Wagtail, the RoutablePageMixin is the key to solving this problem.
If you have /blog/A/slug-product/, /blog/B/slug-product/, /blog/C/slug-product/ , then you can get the slug value slug-product here, then use this value to search the distinct content in your db.
class BlogPage(RoutablePageMixin, Page):
def get_posts(self):
return PostPage.objects.descendant_of(self).live()
#route(r'^(\d{4})/(\d{2})/(\d{2})/(.+)/$')
def post_by_date_slug(self, request, year, month, day, slug, *args, **kwargs):
post_page = self.get_posts().filter(slug=slug).first()
return Page.serve(post_page, request, *args, **kwargs)
As you can see, I did not use the date info of the url but the slug value to get the blog post object, you can follow the pattern here to use regex to match the url you want.
If the slug values in urls are also different, this solution might not work very well, but in most cases, this solution can work fine.
I have written a blog post talking about how to use RoutablePageMixin to make the page routable, you can check this link if you wang to get more about RoutablePageMixin.
Routable Page
Rather than thinking of your news articles as being child objects of one or more products, it might help to think of them as one big pool of news articles which are categorised by product. Your product page will then effectively be a filtered index page of news articles.
Here's how I'd model it:
If you want your news articles to exist at a canonical URL that's independent of any particular category, or you want to make use of page moderation and/or previewing, then define NewsArticle as a page model; otherwise, define it as a snippet or a ModelAdmin-managed model.
On the NewsArticle model, have an InlinePanel where editors can associate as many related products as required:
class NewsArticle(Page):
body = RichTextField()
date = models.DateField()
content_panels = Page.content_panels + [
FieldPanel('body'),
FieldPanel('date'),
InlinePanel('related_products', label="Related products"),
]
class NewsArticleRelatedProduct(Orderable):
news_article = ParentalKey(NewsArticle, related_name='related_products')
product = models.ForeignKey(ProductPage, on_delete=models.CASCADE, related_name='news_articles')
panels = [
PageChooserPanel('product'),
]
On your ProductPage model, add a method that returns a queryset of news items, filtered and sorted appropriately:
class ProductPage(Page):
# ...
def get_news_articles(self):
return self.news_articles.live().order_by('-date')
You can then loop over the news articles in your product page template, using a tag like {% for news_article in page.get_news_articles %}.

Firebase master detail while having unique keys ionic

Just started using ionic, angular and firebase. And I'm having an issue creating a master-detail pattern.
What I want to achieve:
Have a list on my page that's being retrieved from firebase database. When clicking on of the listitems it should show the details of the selected listitem.
What I currently have:
I can save data in firebase database through a form. This is my write operation that I want to get and put it in my list. I'm adding the data in the database with push() method of firebase
My problem:
I've flattened the data structured as described in the documentation
MetaData{
2017:{
1x48sdf3617SDf542394:{
name: "someName",
date: "24/01/2017"
}
}
},
fullData{
2017:{
8i89gslsdk617SDf542gsLd:{
name: "someName",
date: "24/01/2017",
comment: "someComment",
required: "true",
...
}
}
}
So in the list, which is the masterpage, I only getting the metaData back. When clicking it should go to the detail page. The detailpage should get the data back from fullData. The problem is how is the mapping between the metadata and the fulldata. because they both have a unique key generated by firebase.
I looked at some other questions, but they don't have this specific issue as they are defining their own unique key. I don't want to do that, would like to use the auto generated key from firebase.
How would I tackle this?
You can achieve your requirement in two possible ways :
Using the same key for metadata and details. You can store the key in a variable and use the same to write the details. your data structure be like
/metadata/someKey/ {your javascript object}
/detail/someKey/ {your detail of metadata}
By referencing it from metadata. In this case your data structure will be
/metadata/someKey/{....., details : someKey1}
/details/someKey1
I found the answer in the documentation. I need to use the update() method. First creating a key, then using that key in multiple places to save the data using the data() method
Check the documentation

Resources