Getting Friend list data with less read in Firestore - ionic-framework

My project is in Ionic3, Angular2 and Firestore.
As Firestore costing depends on the number of reads I have the following concern.
I have the following structure for users.
User(collection)
->User ID(Doc) -> Name:
-> Photo URL:
-> Friend (collection)
->Friend ID (Doc) -> statue: Active
->Friend ID (Doc) -> statue: Inactive
->User ID(Doc) -> ...
Following this structure, if I want to show my friend list...
I will query the Friend collection for all active friends. (This is one read)
I have to loop through (read) all the IDs to get their Name and Photo URL.
So If a user has 200 friends. It will cost me 200 reads just to display the data.
Is there any better approach?

A solution is to use database denormalization: you duplicate the information you need (e.g. name and photoURL) in your Friend documents. This way, upon reads, you have all data for displaying what you want. For sure, there is a tradeoff. Upon friend updates, you have to either update all related documents or live with outdated information. However, it seems your use case has more reads than writes. As a consequence, it should not be a big issue to apply denormalization.

Related

How to organize FireStore Collections and Documents based on app similar to BlaBlaCar rides

It's my first time working with FireStore. I'm working on a ridesharing app with Flutter that uses Firebase Auth where users can create trips and offer rides similarly to BlaBlaCar, where other users can send requests to join a ride. I’m having difficulty not only deciding the potential collections and paths to use, but also how to even structure it.
For simplicity at this stage, I want any user to be able to see all trips created, but when they go to their “My Rides” page, they will only see the rides that they’ve participated in. I would be grateful for any kind of feedback.
Here are the options I’ve considered:
Two collections, “Users” and “Trips”. The path would look something like this:
users/uid and trips/tripsId with a created_by field
One collection of “Users” and a sub-collection of “Trips". The path seems to make more sense to me, which would be users/uid/trips/tripId but then I don't know how other users could access all the rides on their home feed.
I'm inclined to go with the first option of two collections. Also very open to any other suggestions or help. Thanks.
I want any user to be able to see all trips created, but when they go
to their “My Rides” page, they will only see the rides that they’ve
participated in
I make the assumption that participating in a ride is either being the author or being a passenger of the ride.
I would go for 2 collections: one for users and one for trips. In a trip document you add two fields:
createdBy with the uid of the creator
participants: an Array where you store the author's uid and all the other participants uids (passengers)
This way you can easily query for:
All the rides
All the rides created by a user
All the rides for which a user is a participant, using arrayContains.
(Regarding the limit of 1 MiB for the maximum size for a document I guess this is not a problem because the number of passengers of a ride shouldn't be so huge that the Array fields size makes the document larger than 1 Mib!)
Note that the second approach with subcollections could also be used since you can query with collections group queries but, based on the elements in your question, I don't see any technical advantage.

How to get subcollection after groupCollection()?

I'm building a chat app on iOS using firestore. I can't figure out how to get a subcollection after doing groupCollection().
The database structure is
users (collection)
some_user_info
conversations (collection)
some_conversation_info
messages (collection)
If A and B have a conversation and A sends a message to B, what I did is I create a conversation with the same id for both A and B, but only store that message to A. (Who sends it, who owns it)
So when fetching all messages between A and B, I have to do
db.collectionGroup("conversations").whereField("id", isEqualTo: conversationId)
But it seems there is no way to fetch messages collection after group query on conversations. Is there any walkaround?
Thank you!
Since a collection group, by definition, includes an unlimited number of individual collections of the same name, it doesn't make sense to simply address another single subcollection under that. If you want to query a specific messages subcollection under a specific document that comes back from a collection group query, you can certainly build that in your code, but you will need its DocumentReference to build that.
Or, maybe you want to do a collection group query on messages, you can do that as long as you're able to filter that with the fields of the documents you're looking for.

Flutter firestore structure for query condition

I am new to NoSQL and I'm trying to figure out a good way to represent my data. I have a series of workers that need to request vacations via mobile app.
When I try to write a Firebase query with Flutter, I can do this:
Firestore.instance
.collection("ferie_permessi")
.document("worker1#test.com")
.snapshot();
It works but there are two main errors:
If I try to create another collection called "Woker info" I cannot use worker1#test.com as document ID as it already esists;
I have to sort data client side because firestore doesn't give me the possibility (with this setup I've made).
I'm quite sure that this structure isn't good at all. Each worker needs to have 2 lists: vacations and other. What is wrong?
My guess is that I should move worker1#test.com together with vacations and other so that I can make a query of this kind:
Firestore.instance
.collection("ferie_permessi")
.where("user", "==", "worker1#test.com)
.snapshot();
But now the id? Is an automatic one good?
I had a chance to recently explore creating an app using a firebase-firestore. A couple of things will help here:
Yes, the autogenerated id is good since it is unique, for example, you can have a collections vacation_requests, users you can then use that user_id as a document in vaccation_requests -> user_id -> vacations, instead of using email as a document key.
Or
You can do it like this collections users, vacation_requests, and requests.
store user details in users.
store requests in requests with from and to dates.
store reference of User and Request in vaccation_requests.
Hope this helps!

How to get a list of documents based on an array of document ids?

What's the best way to get a list of documents based on another list of document ids?
If I have User and Profile objects.
Users have a single Profile
Users can save other users' profiles
The same profile can't be saved twice
The documents of the savedProfile sub collection are stored based on the uid of the user it belongs to. It also has an attribute of userRef which stores the uid of the user again. Given that savedProfiles is a list of uids. Is there a way to get a list of profiles based on the savedProfiles subcollection? Currently I am able to make a get request for a users saved profiles which returns a list of uids which I store in a variable. I'm just wondering how I would make the next request to get the full profiles of the savedProfiles based on that list? Saving the whole user profile in a users savedProfiles is also not an option since these profiles can be updated and changed quite frequently and it would be expensive to find and change each users saved profiles if that profile has been saved. (If there was 100,000 users with an average of around 10 saved profiles each).
Please tell me if there's a way to make this sort of query or if there's a better way to structure my data. Thanks.
Ok, so I think I managed to find a way. From my cloud function I used admin.firestore().getAll(refList). refList is an array of document references that I want. This returns a promise which gives a list of documents matching those references. The data for each document is accessible like normal with doc.data() I've either solved the problem, or I'm doing something very wrong. Feel free to comment and let me know if what I'm doing is okay. Thanks

MongoDB storing user-specific data on shared collection objects

I'm designing an application that processes RSS feeds using MongoDB. Currently my collections are as follows:
Entry
fields: content, feed_id, title, publish_date, url
Feed
fields: description, title, url
User
fields: email_address
subscriptions (embedded collection; fields: feed_id, tags)
A user can subscribe to feeds which are linked from the embedded subscription collection. From the subscriptions I can get a list of all the feeds a user should see and also the corresponding entries.
How should I store entry status information (isRead, isStarred, etc.) that is specific to a user? When a user views an entry I need to record isRead = 1. Two common queries I need to be able to perform are:
Find all entries for a specific feed where isRead = 0 or no status exists currently
For a specific user, mark all entries prior to a publish date with isRead = 1 (this could be hundreds or even thousands of records so it must be efficient)
Hmm, this is a tricky one!
It makes sense to me to store a record for entries that are unread, and delete them when they're read. I'm basing this on the assumption that there will be more read posts than unread for each individual user, so you might as well not have documents for all of those already-read entries sitting around in your DB forever. It also makes it easier to not have to worry about the 16MB document size limit if you're not having to drag around years of history with you everywhere.
For starred entries, I would simply add an array of Entry ObjectIds to User. No need to make these subscription-specific; it'll be much easier to pull a list of items a User has starred that way.
For unread entries, it's a little more complex. I'd still add it as an array, but to satisfy your requirement of being able to quickly mark as-read entries before a specific date, I would denormalize and save the publish-date alongside the Entry ObjectId, in a new 'UnreadEntry' document.
User
fields: email_address, starred_entries[]
subscriptions (embedded collection; fields: feed_id, tags, unread_entries[])
UnreadEntry
fields: id is Entry ObjectId, publish_date
You need to be conscious of the document limit, but 16MB is one hell of a lot of unread entries/feeds, so be realistic about whether that's a limit you really need to worry about. (If it is, it should be fairly straightforward to break out User.subscriptions to its own document.)
Both of your queries now become fairly easy to write:
All entries for a specific feed that are unread:
user.subscriptions.find(feedID).unread_entries
Mark all entries prior to a publish date read:
user.subscriptions.find(feedID).unread_entries.where(publish_date.lte => my_date).delete_all
And, of course, if you simply need to mark all entries in a feed as read, that's very easy:
user.subscriptions.find(feedID).unread_entries.delete_all