Regarding the actual limitations in querying data based on subcollections values, what is the suggested way to manage multitenancy in Firestore?
I would like to be able to retrieve and limit access to data related to entities/companies the user is part of.
Example data structure :
/companies/{companyId}/users/
/companies/{companyId}/users/{user}/roles
/companies/{companyId}/docs/
Can /companies/{companyId}/users/ be a collection?
How can I only retrieve companies where user own a role in /companies/{companyId}/users ?
Firestore paths alternate from collection to document and back again:
/collection/document/subcollection/subdocument
So yes, in this case, you would have collections of companies, users, and docs. Collections are also implicit in that they are created automatically when documents exist in them, and removed when no documents exist in them.
At present, subcollection queries (e.g. "all users in a given company") aren't supported, so you'll have to structure your query the other way around: having a users collection with company as a property, when performing a query to find all users in that company.
ref.collection('users').where('company', '==', 'ACME').get().then((document) => {/* Do stuff here */});
Related
[![I want a booking system like as collection->appoinment_user_id->user_id(Who want to book)->date and time.][2]
There is no API to get all subcollections under a document in the client-side SDKs for Firestore. The common pattern is to give collections known names, which you can type in your code, and only use generated IDs for documents.
So in your scenario you'd add a users collection under the 0Sj3n.... document, and then under that you'd add documents for the user IDs again.
Also see:
How to list subcollections in a Cloud Firestore document
How to get names list of collection in firestore?
In the Security Rules! video #6 the suggestion is made to create a security rule using roles defined in a private_data sub collection with a single 'private' document:
allow update: if (get(/databases/$(database)/documents/restaurants/$(restaurantID)/private_data/private).data.roles[request.auth.uid] in ['editor', 'owner'];
Using the web API, how can I query the set of documents where a user has editor or owner permissions? I don't see that a where clause can reference sub-collection documents.
Something like
const q = query(
collection(db, 'restaurants'),
where(new FieldPath('private_data', 'private', 'roles', 'user_123'), 'in', ['editor', 'owner'])
);
Firestore queries can only filter documents based on the data in each document itself. There is no way to filter based on data in another document, either from the same collection or from another (sub)collection.
This means that while it is possible to restrict write and get (single-document read) operations based on the information in the roles subcollection, it is not possible to use those same documents in a query.
If you need this use-case, the common approach is to duplicate the necessary data from the subcollection(s) into the parent document, and the filter on it there. This complicating of data write to allow or simplify a certain read is a pattern you'll see quite regularly when dealing with NoSQL databases.
I have an Orders collection. It contains a field called venueId. And I'm querying against this field using isEqualTo. The venueId is the firebase user uid. I also have a venues collection. It contains this venueId and also has a list of VenueAdmins ids(These ids are also firebase user uids )The app is a point of sales app(pos). I need to query the orders collections so that valueAdmins and venueId see the correct stream. Is quite easy to query with venueId.. venueId,isEqualto, uid. I'm wondering what's the best approach to allow the venueAdmins see the stream as well.
|-Orders // collection
order. //doc
venueId:'2344567788999999'
|-Venues // collection
venue. //doc
venueAdmin: ['3333333333333','55555555555555555']
venueId:'2344567788999999'
My query builder so far: queryBuilder: (query) => query.where('venue.id', isEqualTo: uid)
Firestore does not have the capability to "join" documents from different collections in a single query. A single query can only consider documents in single collection at a time. The way you have your data structured now, it will require at least two queries. First, to find a venue, then second, to find the orders for an admin in a venue.
The only way to make this easier from the perspective of queries is to denormalize your data by duplicating venue data into the order documents. If each order also had a list of admins, then you could reduce this down to a single query.
When using the Firebase console it is possible to see all documents and collections, even subcollections where the path has "documents" that do not exist.
This is illustrated in the picture included here, and as stated in the docs and on the screenshot as well. These documents won't appear in queries or snapshots. So how does the console find these nested subcollections, when a query does not return them?
Is it possible, somehow, to list these documents. Since the console can do it, it seems there must be a way.
And if it is possible to find these documents, is it possible to create a query that fetches all the documents that are non-existant but limited to those that have a nested subcollection? (Since the set of all non-existant documents would be infinite)
The Admin SDK provides a listDocuments method with this description:
The document references returned may include references to "missing
documents", i.e. document locations that have no document present but
which contain subcollections with documents. Attempting to read such a
document reference (e.g. via .get() or .onSnapshot()) will return a
DocumentSnapshot whose .exists property is false.
Combining this with the example for listing subcollections, you could do something like the following:
// Admin SDK only
let collectionRef = firestore.collection('col');
return collectionRef.listDocuments().then(documentRefs => {
return firestore.getAll(documentRefs);
}).then(documentSnapshots => {
documentSnapshots.forEach(doc => {
if( !doc.exists ) {
console.log(`Found missing document: ${documentSnapshot.id}, getting subcollections`);
doc.getCollections().then(collections => {
collections.forEach(collection => {
console.log('Found subcollection with id:', collection.id);
});
});
}
});
});
Note that the Firebase CLI uses a different approach. Via the REST API, it queries all documents below a given path, without having to know their specific location first. You can see how this works in the recursive delete code here.
Is it possible to create a query that fetches all these subcollections that are nested under a document that does not exist.
Queries in Cloud Firestore are shallow, which means they only get documents from the collection that the query is run against. There is no way in Cloud Firestore to get documents from a top-level collection and other collections or subcollections in a single query. Firestore doesn't support queries across different collections in one go. A single query may only use properties of documents in a single collection or subcollection.
So in your case, even if one document does not exist (does not contain any properties), you can still query a collection that lives beneath it. With other words, you can query the queue subcollection that exist within -LFNX ... 7UjS document but you cannot query all queue subcollection within all documents. You can query only one subcollection at a time.
Edit:
According to your comment:
I want to find collections that are nested under documents that do not exist.
There is no way to find collections because you cannot query across different collections. You can only query against one. The simplest solution I can think of is to check if a document within your items collection doesn't exist (has no properties) and then create a query (items -> documentId -> queue), and check if has any results.
Edit2:
The Firebase Console is telling you through those document ids shown in italics that those documents just does not exist. Those documents do not exist because you didn't create them at all. What you did do, was only to create a subcollection under a document that never existed in the first place. With other words, it merely "reserves" an id for a document in that collection and then creates a subcollection under it. Typically, you should only create subcollections of documents that actually do exist but this is how it looks like when the document doesn't exist.
In Cloud Firestore documents and subcollections don't work like filesystem files and directories you're used. If you create a subcollection under a document, it doesn't implicitly create any parent documents. Subcollections are not tied in any way to a parent document. With other words, there is no physical document at that location but there is other data under the location.
In Firebase console those document ids are diplayed so you can navigate down the tree and get the subcollections and documents that exist beneath it. But in the same time the console is warning you that those document does not exist, by displaying their ids in italics. So you cannot display or use them because of the simple fact that there is no data beneath it. If you want to correct that, you have to write at least a property that can hold a value. In that way, those documents will hold some data so you can do whatever you want.
P.S. In Cloud Firestore, if you delete a document, its subcollections will continue to exist and this is because of the exact same reason I mentioned above.
I'd like my web app router slugs to correspond to my Firestore documents data.
For example:
www.mysite.com/restaurants/burger-king
/restaurants <- Firestore Collection
/restaurants/mcdonalds <- Firestore Document
/restaurants/burger-king <- Firestore Document
This is easy enough, as I can assign the name as a slug-friendly UID in Firestore. The difficulty arises with CRUD functionality. I need to be able to rename my item titles, but Firestore does not permit you to rename indexes, which is the issue I'm facing.
One SO solution I saw was to delete the old record and creates a new one at the updated index. That's problematic for me, because sub-collections would be hard to transfer from the client side.
Are there more elegant solutions?
You don't have to identify a document by its ID. If you're able to ensure uniqueness of a document field value, you could instead query a collection for an ID value in a known field, then use the results of that query to satisfy your REST API. Then, you can change the value of that document field as often as you want, in order to satisfy required changes to the public API.