Firestore Data Modelling - firebase

My app has two Firestore Collection:
Events (collection)
Document
eventId
hostId (userId of organizer)
title
etc.
Users (Collection)
Document
userId
friends (array of userId's)
age
etc.
I would like for a user to query all events created by his friends.
I considered
do a seperate query for each friend and pull each friend events. This could get ugly with sorting on dates.
adding a hostFriends field in the event and use an array-contains user's Id query. But this will be problematic if someone add a friend after having created an event. I would have to sync this across continiously..
doing client side filtering, but this will lead to many unneccesary reads..
Would be happy to hear any additional idea's or if one of the above would make sense? many many thanks!
This is my first post, apologies if I made any formatting mistakes.

Related

Firebase Structure

I am writing my first flutter and firebase app and I am a little confused on how best to structure the database. Below I have put a brief description of what I want the app to do... its a learning app based on Flash Cards.
I want to have FlashCards which will have the Question and Answer fields. The FlashCards will all be assigned to a Book and then when they are loaded the user will always have to select a Book to study and then this will list all cards in the book. Then I want to make it so that multiple users can have access to that same book.
So in a real world example, a child could have a science test so he creates a Book called "Aug 2020 Science", then he adds many cards to the book with the Questions and Answers... then once he completes the book, he could share it with his friends and they all have the same 'book' to study.
In SQL I would create tbl_Cards, tbl_Books, tbl_Users, tbl_UsersBooks (tbl_UsersBooks would just have a table that shows the ID's for the User and the Book). So a user can have many books, a book can have many users and many cards.
In SQL I could just show relational data from each table and just filter the UserBooks to show current user. However, I am understanding if I had this structure in firebase I would pretty much be pulling every document and collection each time a user logs in.
Any help would be great!
Thanks
You could try something like this:
books collection:
${bookId}:
bookName: (string)
createdBy: (userId)
cards collection:
${cardId}:
answer: (string)
question: (string)
In firebase cloud firestore for flutter (and any other platform/framework that can use firestore), you can do some simple queries to only load specific types of documents. In flutter, you can make a query like this:
final firestore = FirebaseFirestore.instance;
final query = db.collection('books').where('createdBy', isEqualTo: userId);
You could get all of those documents by calling
query.get();
Or you could listen to realtime updates like this:
query.snapshots.listen((querySnapshot) {
...
});
I hope this answers your question.

Firebase how to filter collection with a sub collection

I have a collection called "posts".
Posts has sub collection called "feedback".
when a user give feedback to a post his id and comment get added to feedback sub collection.
Now I want to find posts that user has not given a feedback.
Something like following sql query
select * from posts where userId not in (select userId from feedback)
Can someone provide advice on how to do this?
Firestore doesn't support joins between collections or subqueries. You won't be able to perform any queries that use data from more than one collection.
Also, querying for non-existence isn't supported by Firestore. So, you won't be able to query for the absence of data in a field. Firestore requires that all queries be able to use a highly performant index, which only tracks data present in documents.

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.

Efficient way to check for existing related documents in NoSQL (Firestore)?

Following Current Datamodell
User
User ID
Video
VideoID
LikedBy (Subcol)
User ID
User ID
User ID
Now if a User visits a video I wanna show if he Liked the Video already or not (similar to youtubes button color if you liked already).
My current approach is querieing for a Document with the Key of the signed In UserID and if I find one it means the user liked the video. The problem is I have this for Artists that you can subscribe too similar to channels on youtube.
This alone created about 3x the initial Reads I have on Page Load.
I would like to hear if there is any more efficient way to query for such a thing or structure the data.
Be aware that if you suggest me to store all liked Shows in the User or Show Document that this is not scalable due to the 1MB Limit.
1) You can have a subcollection on the Users, storing the ids of the posts the likes.
2) You can create a users_likes, collections where the Ids is the user id and inside have an array with the ids of the posts the user likes.
3) Last, just make props called likes on the user collection an store the ids of the posts.
All options have a trade-off, I would make like a user and posts_likes query on load and keep that in memory (no external user is going to affect this).
Be aware that if you suggest me to store all liked Shows in the User or Show Document that this is not scalable due to the 1MB Limit.
If you are expecting a user to like more than 1 millions of posts... otherwise, storing 1Mb of only ids is a good idea... I use this same pattern for a user events tracking, I have events defined (equivalent to your posts) and the user make actions that correlate to those events (your likes), I have cases with more than 80K and it works like charm. I gave your 3 options, I would say, start with 3 until it doesnt work, then go to 2 and same process up to 1. Since you will work with array of ids, support yourself with this
My current approach is querieing for a Document with the Key of the signed In UserID and if I find one it means the user liked the video.
Yes, that's a correct approach.
This alone created about 3x the initial Reads I have on Page Load.
I don't know where this is coming from but there is certainly something wrong. Unfortunately, nothing in your question can help me see the problem.
I would like to hear if there is any more efficient way to query for such a thing or structure the data.
I don't understand much from your schema, but I would structure the database this way:
Firestore-root
|
--- users (collection)
| |
| --- uid (document)
| |
| --- //user properties
|
--- video (collection)
|
--- videoId (document)
|
--- likedBy: ["uid", "uid", "uid"]
As you can see, likedBy property is of type array. So once you get a video document, you can simply check the uid of the logged in user against the likedBy array. If it exists, it means that user has already liked that video, otherwise has not.

Cloud Firestore and data modeling: From RDBMS to No-SQL

I am building an iOS app that is using Cloud Firestore (not Firebase realtime database) as a backend/database.
Google is trying to push new projects towards Cloud Firestore, and to be honest, developers with new projects should opt-in for Firestore (better querying, easier to scale, etc..).
My issue is the same that any relational database developer has when switching to a no-SQL database: data modeling
I have a very simple scenario, that I will first explain how I would configure it using MySQL:
I want to show a list of posts in a table view, and when the user clicks on one post to expand and show more details for that post (let say the user who wrote it). Sounds easy.
In a relational database world, I would create 2 tables: one named "posts" and one named "users". Inside the "posts" table I would have a foreign key indicating the user. Problem solved.
Poor Barry, never had the time to write a post :(
Using this approach, I can easily achieve what I described, and also, if a user updates his/her details, you will only have to change it in one place and you are done.
Lets now switch to Firestore. I like to think of RDBMS's table names as Firestore's collections and the content/structure of the table as the documents.
In my mind i have 2 possible solutions:
Solution 1:
Follow the same logic as the RDBMS: inside the posts collection, each document should have a key named "userId" and the value should be the documentId of that user. Then by fetching the posts you will know the user. Querying the database a second time will fetch all user related details.
Solution 2:
Data duplication: Each post should have a map (nested object) with a key named "user" and containing any user values you want. By doing this the user data will be attached to every post it writes.
Coming from the normalization realm of RDBMS this sounds scary, but a lot of no-SQL documents encourage duplication(?).
Is this a valid approach?
What happens when a user needs to update his/her email address? How easily you make sure that the email is updated in all places?
The only benefit I see in the second solution is that you can fetch both post and user data in one call.
Is there any other solution for this simple yet very common scenario?
ps: go easy on me, first time no-sql dev.
Thanks in advance.
Use solution 1. Guidance on nesting vs not nesting will depend on the N-to-M relationship of those entities (for example, is it 1 to many, many to many?).
If you believe you will never access an entity without accessing its 'parent', nesting may be appropriate. In firestore (or document-based noSQL databases), you should make the decision whether to nest that entity directly in the document vs in a subcollection based on the expect size of that nested entity. For example, messages in a chat should be a subcollection, as they may in total exceed the maximum document size.
Mongo, a leading noSQL db, provides some guides here
Firestore also provided docs
Hope this helps
#christostsang I would suggest a combination of option 1 and option 2. I like to duplicate data for the view layer and reference the user_id as you suggested.
For example, you will usually show a post and the created_by or author_name with the post. Rather than having to pay additional money and cycles for the user query, you could store both the user_id and the user_name in the document.
A model you could use would be an object/map in firestore here is an example model for you to consider
posts = {
id: xxx,
title: xxx,
body: xxx,
likes: 4,
user: {refId: xxx123, name: "John Doe"}
}
users = {
id: xxx,
name: xxx,
email: xxx,
}
Now when you retrieve the posts document(s) you also have the user/author name included. This would make it easy on a postList page where you might show posts from many different users/authors without needed to query each user to retrieve their name. Now when a user clicks on a post, and you want to show additional user/author information like their email you can perform the query for that one user on the postView page. FYI - you will need to consider changes that user(s) make to their name and if you will update all posts to reflect the name change.

Resources