When to duplicate data in noSQL? - firebase

I'm working on a project where Firebase Firestore will be used. I've not had experience with noSQL so I work on understanding this technology.
The application lets an user to select a movie category and download an associated movie to let him movie edition.
The category and movie won't be change by the users and will be fixed or change by the owner. Think this like a Netflix catalog where the user can only watches the movies
There are several categories but for the moment only one movie is inside a category (may have more later).
Each movie has related metadata.
In the future:
- an user object will be used to rank each user regarding their score (information related to the application).
- some movie will be available with localisation restriction, i.e a movie would be available for US user only.
In my first thought, the data structure will look like this:
// Collection
Category: {
name: "drama" // Could be action, or other
}
// Collection
Movie: {
name: "Matrix"
description: "Best movie ever"
duration: 1321312
url: "https://www....."
allowedCountry: "us" // is it the right place for this field?
category: "drama" // is it ok to duplicate data here?
}
//Collection
user: {
ranking: 3
withMovie: "Matrix" // is it ok to duplicate data here?
}
I'm not sure if it's the right data structure for this problem.
The flow of the app would present in the first time all the possible categories (so I create a separated collection in order to avoid to iterate on all the song to get the categories)
Then when the user select a category the possible movies are display and the app download the selected one.
Is is okay to iterate on all possible movies to show the movies related to the category?
Or should I put the movies as a sub collection of the category collection?

I'd typically indeed keep the category directly in the movie document in this scenario, since it makes the queries much easier to read.
firebase.firestore().collection("movies").where("category", "==", "drama")
In fact, consider if your movies can really only have a single category (as you've modeled it now), or whether they may have multiple categories in the future (as Netflix does as far as I know). You'd model the latter as an array of categories:
categories: ["drama", "sci-fi"]
And then query it with:
firebase.firestore().collection("movies").where("categories", "array-contains", "drama")

Related

How to structure collections in Firestore and access them in flutter?

I am trying to create an social app using flutter, in that app I have users and their posts, so in firestore I am creating 2 collections,
Users
Posts
So, in users it will have user data like email, display picture, bio etc. For identification I'm creating a key in posts which will have a reference to the user to whom the post belongs, like so,
Now, I while I ask a particular post I also want some of the user details like UserName and display picture so that I can show it in the Post in the UI like,
So, I want to use StreamBuilder as it will reflect any changes made, but I can't get user details if I'm using StreamBuilder.
How can I achieve this?
According to your data model, you need to make two queries, the first one to get the user ID and others to get the posts corresponding to the user.
final StreamController<List<Post>> _postsController =
StreamController<List<Post>>.broadcast();
Stream listenToPostsRealTime() {
// Register the handler for when the posts data changes
_postsCollectionReference.snapshots().listen((postsSnapshot) {
if (postsSnapshot.documents.isNotEmpty) {
var posts = postsSnapshot.documents
.map((snapshot) => Post.fromMap(snapshot.data, snapshot.documentID))
.where((mappedItem) => mappedItem.userId == FirebaseAuth.instance.currentUser().uid)
.toList();
// Add the posts onto the controller
_postsController.add(posts);
}
});
return _postsController.stream;
}
I would like to recommend you to visit this video: "How to structure your data" and this post, remember that you will be charged for every read to the database, so if you're going to gather information from the Users document every time you retrieve a Post, it would be a better idea to have duplicated information from the Users document inside your Posts document, this way you only have to query your Posts document. Remember that denormalizing data (like replicating it) on the NoSQL world is a common behaviour.
Your structure could be something similar to:
Posts:
createdAt: "June 25, 2020"
description: "New food"
email: "test#email.com"
image: "https://example.com/assets/image.jpg"
likes:
[updatedAt: "June 25,2020", user: likerUser]
user:
[ name: posterUser,
profile_picture: "https://example.com/assets/posterPicture.jpg",
profile_url: "https://example.com/profiles/userPoster"]
In order to keep your data synced between both documents I recommend you to check this answer

how to maintain duplicate data between documents sync on firebase?

I have a question about firestore and cloud function.
let say i have 2 collection:
products and reuse_shopping_carts.
and my reuse_shopping_cart document contain many products with all it’s properties (id,name,price,href).
now i want that if i change the price of a specific product in products collection , a cloud function will run on all reuse_shopping_cart documents and in every document that contains the specific product it will change is price.
someone can explain me how?
or what is the right approach to do it?
i know i can store in the reuse_shopping_cart only the product id and fetch the updated product data at client, but for a list of 100 product in one reuse_shopping_cart it’s will require 100 separate query for every time i want to read the shopping list, and changing price of a product is mach more rare, so i think it’s a better approach.
if you think differently i will happy to hear...
thank you!
You can structure your reuse_shopping_carts document like this:
products: (map)
(product id)
name:
value:
For example:
Then to find which documents to update when the price changes you can query like this:
firebase.firestore().collection('reuse_shopping_carts').where(`products.${productId}.price`, '>=', 0)

Firestore data structure for a hotel reservation system

I want to build a hotel reservation system that also will push availability to the channel manager. I want to use Firestore to store my data. I read a few articles but still pretty new to this.
I am planning to set up my database using a structure like this:
collection: "reservations"
fields: reservation_id, status, name, email, check-in date, check-out date, room_id, etc
collection: "rooms"
fields: room_id, room_type, room_number,etc
collection: "availability"
fields: room_id, date, isAvailable
examples: {room_id: 1, date: "2018-10-05", isAvailable: true}, {room_id: 1, date: "2018-10-06", isAvailable: true}
These are some situations I will need:
Situation: A new booking is created
I will create a new "reservation" doc and I will also update the "availability" docs for these dates.
Situation: A multi-calendar view of all rooms
I will get all docs for the dates and rooms and display in a grid
My questions are:
(1) Is this the best structure to set up for Firestore?
(2) Do I need to set "availability" as a subcollection of "rooms"?
The only thing is I need to get all rooms' availability quite often so that will cause lots of reading operations.
(3) Do I store Date as a timestamp object or a string? There's no hour/minute time involved so it looks a bit too much for me and I like "2018-10-05" it looks neat.
(4) How do I write the query to update availability when a new booking is created? How to get the date range I want?
Thank you for reading my questions :)
I recommend you to add dateCreated to reservations.
and for rooms you should also add floor(1st/2nd) and where is hotel level(I mean 5/4 stars)
and you also need a hotel Collections for it's id,Name ,location,stars, isRestaurant Available, Parking Available etc..

Organizing user/post relation in a database

Using the Firebase database I need to decide how to organize the user / post(e.g a tweet) relation. Two common reading tasks include:-
Showing an overview of a user's posts
Filtering and selecting specific posts based on their content
I currently use this:
posts
randproject1234jdfs
user_uid: randomUserName1234,
content: "example"
users
randomUserName1234
posts
project_id: randproject1234jdfs
nickname: "Mr Example"
This stores the same information twice:
In posts/randstring1234jdfs/user_uid , the value points to the user.
In users/1234/posts the project_id point to the post.
Would removing one of these lead to significantly slower data-reading (having to loop through the entire folder to see if the user_uid/project_id matches the given post/user)?
Or would it be better to organize the data in a different way altogether (e.g removing the user/posts split)?
You want to do the following:
Showing an overview of a user's posts
Filtering and selecting specific posts based on their content
You can do this then:
posts
randomid
user_uid: randomUserName1234,
content: "example"
posttitle: "English"
randomid2
user_uid: randomUserName1235,
content: "examples"
posttitle: "Math"
users
randomUserName1234 <-------------------- userid
email: email#gmail.com
nickname: "Mr Example"
randomUserName1235<--------------------anotheruserid
email: email#gmail.com
nickname: "Mr Awesome"
Since you want to show the user's post, then you can just query the node posts something like this: orderByChild(user_uid).equalTo(randomUserName1234) using that you can retrieve the content which are the posts I guess.
To retrieve posts of any user depending on content using the above u can do this:
orderByChild("posttitle").equalTo("English") and then retrieve the node content which I'am assuming they are the posts.
Remember denormalization is normal and it is important or you will have a hard time doing the queries. https://firebase.googleblog.com/2013/04/denormalizing-your-data-is-normal.html
The user node let it have the list of users in this app for example.
The posts node will be like above and it will be connected to the user by having this user_uid: randomUserName1235 as seen above.

Firebase database structure - denormalized data?

I read a lot about nosql databases lately. I get that rule of thumb is to structure the data based on our view (of course, depends on the use case).
Now, let's say that we have a social app and the user has a profile but he also creates posts and we have to store them in the database.
So, I see some developers choose to do that like so:
Posts
-----UserID
-----------PostID
-----------------username: John
-----------------profileImage: https://...
-----------------posted_photo: https://...
This totally fits the structure base on the view. We'd go into posts and our userID and we could get all the data that our view needs. Now my question is, what happens when the user has made 100K posts and he decides to change his profile photo for example. All of his posts so far contain his old photo, so now, we have to write a method that would cycle through 100K of posts (or all of his posts in general) and update his photo. In 2 hours, he decides that "Nah, I don't like this photo, I'd change it back" and we have to make another 100K queries.
How is that (denormalized data) ok? Sure, its easier, its flat but then we have to make ridiculous amounts of queries to change a single profile photo. What's the way to handle this really?
I've done this storing user's data in a place and setting just the userID as post attribute.
posts:
userID:
postID:
userID: 'user1',
attachedImageURL: 'http:..',
message: 'hey',
reblogID: 'post4',
type: 'audio|poll|quote'
users:
user1:
name: 'john',
profileImage: 'http..'
It requires one more query to Firebase to retrieve user's profile data but it's a good way to solve this. It really depends on how you want to use those data.

Resources