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.
Related
We have a collection called customer that has all the details and another collection called merchant with a sub-sub-collection called merchant customers where we are paginating the customers.
We need to search the customer using the main customer document fields with pagination using the reference from merchant sub-collection.
Customer Main records
Sub-Sub-collection
Firestore queries can only filter (and thus also only paginate) on values that are present in the documents that it returns. There is no way to filer a document on values from somewhere else in the database.
So to allow the use-case, you will either have to replicate the relevant information from the subcollection into the main customer document, or replicate the information from the main customer document into the documents in the subcollection and then query that.
I have a firestore DB where I'm storing polls in one collection and responses to polls in another collection. I want to get a document from the poll collection that isn't referenced in the responses collection for a particular user.
The naive approach would be to get all of the poll documents and all of the responses filtered by user ID then filter the polls on the client side. The problem is that there may be quite a few polls and responses so those queries would have to pull down a lot of data.
So my question is, is there a way to structure my data so that I can query for polls that haven't been completed by a user without having to pull down the collections in their entirety? Or more generally, is there some pattern to use when you need to query for documents in one collection that aren't referenced by another?
The documents in each of the collections look something like this:
Polls:
{
question: string;
answers: Answer[];
}
Responses:
{
userId: string;
pollId: string;
answerId: string;
}
Anyhelp would be much appreciated!
Queries in Firestore can only return documents from one collection (or from all collections with the same name) and can only contain conditions on the data that they actually return.
Since there's no way to filter based on a condition in some other documents, you'll need to include the information that you want to filter on in the polls documents.
For example, you could include a completionCount field in each poll document, that you initially set to 0, and then update only every poll completion. With that in place, the query becomes a simple query on the completionCount field of the polls collection.
For a specific user I'd actually add all polls to their profile document, and remove them from there. Duplicating data is usually the easiest (and sometimes only) way to implement use-cases such as this.
If you're worried about having to add each new poll to each new user profile when it is created, you can also query all polls on their creation timestamp when you next load a user profile and perform that sync at that moment.
load user profile,
check when they were last active,
query for new polls,
add them to user profile.
I'm currently working on an application where users can create groups and invite others in it.
I would like people in the same group to be able to see their first and last names.
To do that, I have a collection named Users where each of the users have a document contains all their personnal data, like first and last names, phone, position , ...
I have also another collection named Groups, where all of my groups are stored, with their name, and an array contaning the ID of the members.
When an user open the app, a first request is done for request his groups (he recieve the groups names and the arrays of members). After, if he want to know the user in a certain group, another request is done for search only the first and last name of all the members.
So, I imagine that there is a query that will return me only the fields that I would like to retrieve, and that there is a rule allowing a potential hacker to be refused access to the entire user document except if the user is the owner of the document.
// For retrieving my user's groups
Stream<List<Group>?> get organizations {
return firestore
.collection('Groups')
.where('members', arrayContains: this.uid)
.snapshots()
.map(_groupsFromSnapshot);
}
// For retrieving names of the members of a group
Stream<List<Member>?> getMembers(Group group){
return firestore
.collection('Users')
// and i dont know what to do here ...
}
With the Client SDKs and the Flutter plugin it is not possible to get only a subset of the fields of a Document. When you fetch a Document you get it with all its fields.
If you want to get only a subset of the fields of a document, you can implements the two following approaches:
Denormalize your data: You create another collection which contains documents that only contain the fields you want to expose. You need to synchronize the two collections (the Users collection, which is the "master", and the new collection): for that it's quite common to use a Cloud Function. Note also that it's a good idea to use the same documentID for the linked documents in the two collections.
Use the Firestore REST API to fetch the data: With the REST API you can use a DocumentMask when you fetch one document with the get method or a Projection when you query a Collection. The DocumentMask or the Projection will "restrict a get operation on a document to a subset of its fields". You can use the http package for calling the API from your Flutter app.
HOWEVER, the second approach is not valid if you want to protect the other users data: a malicious user could call the Firestore REST API with the same request but without a DocumentMask or a Projection. In other words, this approach is interesting if you just want to minimize the network traffic, not if you want to keep secret certain fields of a document.
So, for your specific use case, you need to go for the first solution.
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.
I have two collections - Tickets and Users. Where a user can have one to many tickets. The ticket collection is defined as follows
Ticket = {_id, ownerId, profile: {name}}
The ownerId is used to find all tickets that belong to a specific person. I need to write a query that gets me all users with no tickets.
How can i write this query without having to loop through all users, checking if the userID shows up in any Tickets?
Would a bidirectional storage cause me any performance problems ? For example, if i were to change my users collection and add an array of tickets: [ticketID, ticketID2, ...]?
I'd go with the array of tickets being stored in users. As far as I know, Mongo doesn't really have a way to query one collection based on the (lack of) elements in another collection. With the array, though, you can simply do db.users.find({tickets:[]}).