Best way to model firestore data for a users items? - firebase

I'm trying figure out the best data model for a items collection in firestore that will allow users to add items they own. I would of course show an items list and then on another page item details so I need to be able to query.
Should I create each user item as its own document with user id inside like so...
Or Since firestore charges on document reads (and each user might have hundreds of items) would it be better to create one document with user id as the document name and then put all the user items in one document like so (each item is a map type (object)....
I am not sure the best way to model this so that is both cost efficent at scale and easy to CRUD and query. Thank you! (sorry for the links, don't have enough rep to inline the images)

Related

implement search operation using firestore sub collections

I am trying to make an ecommerce app with 4-5 different categories(mobile products, bedsheets, doormats, computers etc.) and want to show them under their own categories like these image1 image2 imag3 image4 and also want to implement "search products by name" functionality in my app .I know how to make sub-collections but Can anyone tell me how to structure firestore data base so that I can fetch data under their own individual categories and using the same data how to implement search product by their names. I am new to flutter please explain in details.
The best fit I can see for your solution would be to use Collection group queries. Just make sure the subcollection have the same name and you could query over them.

Single multi-tenanted firestore or many single tenanted firestores?

I'm building a SaaS system that allows users to define their own data models and enter data according to those models. It's a bit like airtable.
One user might model a bookshop, and would have a Book model, with title and ISBN fields. Another user might model medical records, and would have "date of last visit" as a field.
In the case of the bookshop, I want users to be able to search on title and ISBN. In the case of the medical records, I want users to be able to search on the date of the last visit.
I am using Firestore as my backend.
Firestore requires an index to enable a search. So that approach will not scale as # of customers increases.
My thought therefore was to have a Firestore instance for each customer, and those specific instances would have the necessary indexes.
I'm sure there are downsides to doing this though.
What would folks recommend to best solve this need?
What you are trying to achieve is some kind of weird, since you will not provide at least a few standard common properties for each user of your Bookshop.
When you want to perform a search in a Cloud Firestore database, you need the exact name of the property on which you want to search for. Having dynamic properties might not help you solve the search feature. However, you can create a document with a property of type array that can hold the name of all properties the users have chosen and perform a search on every property, but this solution will be much too expensive.
In my opinion, a possible solution might be to create at least a few common properties, so you can have the properties on which you can search. When someone creates, for example, a book shop you can display at the beginning all available properties a user can choose. Once you create a shop, you can have different users with different shop properties. This means that if a user does not choose a property, when you perform a search on that property, the results won't contain his/her products. This will work, only if you have predefined properties.

Create a collection linked to user ID in Firestore with flutter

Type of App to give context: An app that enables wedding bands to create a profile. Part of this profile will be to enter different dates with locations (Events)and return them in a list view. I have a Firestore collection called userData. The collection is made up of documents IDs(Firebase User ids) generated during sign in. These documents contain fields, mainly Strings and arraysfor things like a header image.I'm thinking of creating a class called Events with member variables date and venue. I'm wondering how I should structure Firestore to allow the Events be queried and returned in a listView. I've looked at sub collections where the UserData would be a parent but I'm not sure is this possible? Or maybe creating a collection at the root called Events but not sure how'd I'd connect the different bands to their Events. Here is a screen shot of Firestore. Each document is a band.
You can either:
Use a new top-level collection to store all events, with a field for the id of the band to help with querying for events for only that bad.
Use a subcollection under each band for their events.
It's totally up to you to choose. It will depend on the kinds of queries you want to make. Note that if you go with #2, you will not be able to query for events across bands (at least not until Firestore supports collection group queries which it does not today).

Firestore : How to design a Data model to make querying documents that are not exist in an array possible?

I'm trying to find a way to properly desing my Data Model with Firestore. I'm looking for something similar to what Tinder does, showing you people that you have'nt swiped yet, based on your location.
So I ended up with something like :
A User1 has an array of "met people"
A "Haven't yet met user"/ User2 his also a User with the same document model
They all belong in the same "Users" collection
I want to query all the users that this User1 haven't swiped yet
I know that you can't do something like "array_not_contains" or "!=" because all fields that you query need to be indexed.
So I wonder, is this possible to model data to make it work, or the only solution is to drop Firebase because this kind of query is not possible at all?
One alternative can be to store in a collection all the relationships (with theirs status) between all users. But that also means that whenever a user signup, I have to create as many documents as I have users that's really ugly and make a enormous numbers of documents.
EDIT:
Thanks again for your answer and sorry for my late answer.
There is no need to create a new database call since you already got all the users from that area in the first place.
Not If have a large response set, I will limit to a number. (5 in the example below).
And even If I don't limit the number, in the next db call, how I can know that new peoples has been added and how to retrieve only those.
I will not remove them from Users Collection has they can be show to others users.
P.S: I forget User4 in Users Collection pictures.
For User 1, get 5 first matchs, remove existing ones, show User5.
For User2, get 5 first matchs, remove existing ones, show User4, User5.
After users choices, Users are added to their list. Users Collection stay the same.
For User 1, get 5 first matchs, remove existing ones, nothing to show, even if I have a User 6, 7.
To fix that I launch a second query get the new ones but, more the user use the app more query I may need to do to try to display to him existing user in his area.
Maybe I've misunderstood what you named "initial list", for me it is the list object retrieve from my db containing all users (with limit).
EDIT 2:
You can check the answers of Alex Mamo to know how to query documents that are not exist in an array possible.
Let's me explain my use case and why I think, that won't work.
I want to be able to search all users next to me, for trying to do that in Firebase, I store Geopoint. Geopoint can't be really use for now out of the box with Firebase, so I user Geofirestore in a Cloud Function.
I store and update user Geopoints based on theirs locations, so this means user location change by time.
I limit the numbers of Users return by this function.
In my initial state I retrieve users next to me (User1), I get 3 an 4.
Let's say that I store last checked userId to use it later as a cursor for my query (User 4).
Now my geopoint change, and the users in this area changes too.
I request next bunch of users next to me, and I use my previous userId/document to "startAfter" (more on this
here), see the image below, that's won't work.
If I use the cursor (User4), I'll take 5, but not 2, because in the return list, if I order by Id, 2 will be before 4.
Worse, like below, if the return list may not even have user 4 in it, the cursor will be pointless.
My example is a bit simplified and does not take in account what is described in the first answer and my first edit (limited subset of users, data design).
A possible database structure for your app might be:
Firestore-root
|
--- users (collection)
|
--- uid (document)
|
--- acceptedUsers: ["uidOne", "uidTwo"]
|
--- declinedUsers: ["uidThree", "uidFour"]
|
--- //Other user properties
The mechanism is simple. When you first want to show a user profile to the current (authenticated) user, you have to create a query that will return all users (in user area). According to the user decision, you need to add the corresponding uid in either the acceptedUsers array or in declinedUsers array. Once you want to show another users, use the same query but this time, you need to make an extra operation. Once the query returns the users within user location, add all those users to a list. Compare the list that is coming from the database with your exting arrays and remove all the users from both arrays. In this way you'll have a list that contains only users that the actual user didn't see. This extra step is needed to make sure the id of the user does not exist in one of those arrays. In the end, simply choose a random user from the list and show the details to the user. That's it!
One alternative can be to store in a collection all the relationships (with theirs status) between all users. But that also means that whenever a user signup, I have to create as many documents as I have users.....that's really ugly and make a enormous numbers of documents.
This is not an option. This means that you need to write each time a user joins your app an enormous amount of data, which will be very costly. Since everything in Firestore is about the number of read and writes, I think you should think again about this approach. Please see Firestore usage and limits.
Edit:
Let's consider the initial list of users that has 10 records. With other words, all the users within that area are 10. You say that 7 users are already seen, that makes the list contain only the 3 remaining users.
So I display the 3, (or I do another request to get some more) and he check the 3.
Yes, you should display those 3 users and then remove them one by one from the initial list. There is no need to create a new database call since you already got all the users from that area in the first place. Once the list remains empty, you should display a message to the user that in that particular area are no more users to swipe.
When will create another database call?
Only when needed. Which means that you create another call once new users enter that area. Let's say 3 new users are new, you get a list now of 3 user and use the same algorithm.
More my user use the app more it’s difficult to show people that he haven’t seen, because his list become bigger.
If you think that the arrays will grow more than a document can hold, then you should consider storing the users in a collection and not in an array. So in this case, the problem is that the documents have limits. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:
Maximum size for a document: 1 MiB (1,048,576 bytes)
As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text (uids), you can store pretty much but as your array getts bigger, be careful about this limitation.
But if you'll stay within this limits, which I personally think you'll do, you have nothing to worry about.
Edit2:
Not If have a large response set, I will limit to a number. (5 in the example below). And even If I don't limit the number, in the next db call, how I can know that new peoples has been added and how to retrieve only those.
I will not remove them from Users Collection has they can be show to others users.
If you have large amount of data (many users in a single area), yes it's good idea to limit the results, but a much better idea would be to load the data in smaller chunks. In short, get 5 users, remove one by one till the list has zero users, load other 5 users and so on. This can be made using my answer from the following post:
Is there a way to paginate queries by combining query cursors using FirestoreRecyclerAdapter?
The initial list, is the list that you are getting when you first query the database. In this case, the initial list will contain 5 users.

How to copy a collection as subcollection in firebase?

I have a users collection and articles collection. Every user starts with all the articles in articles collection which I store as sub-collection of the user and I only keep the articles which the user has not read. When the user reads an document from article collection, I remove it from user's sub-collection. The problem is when a new user signup, I have to fetch all the documents in articles collection and copy over to user's sub-collection which is unnecessary bandwidth usage. Is there any way to minimise it? Is my database model is good enough?
I am more familiar with firestore than firebase but I can give you some ideas until someone better comes along!
You can avoid copying the entire articles by just keeping a list of article IDs for each user that can be used to look up the article in the main list. This list could either be a sub collection or just an array in the user document.
If you were really concerned about bandwidth you could store a list of all the articles in a top level document (that would have to be updated every time you added or removed an article). This stinks a bit of duplication and makes the model more fragile (you must keep two things in sync) but would allow you just to copy this small list instead.
A different approach, if your articles tend to be read in order, is that you could combine a list of unread articles with another field that indicates the latest read article - anything after this article can be considered unread even if not in the list. A new user would then just have this new field set to 0 to indicate no articles read. This also means you wouldn't need to add new articles to all users as each user would check for any articles newer than this new field when they access your service.
Hopefully this can give you some ideas to try and play around with!

Resources