How to return document references from firebase collection using dart programing language? - firebase

I am using a Restaurant collection to store all restaurants information. The document ID represent a unique restaurant. The document contain more collections (orders, items etc.). The orders collection contain documents that are same as the user UID. Inside the document contain a collection call "All Orders" that saves all the orders of that particular user.
I want to return the Restaurant document refence ids if the user purchased anything from that restaurants. Then return a list of document references type casted to string.
Here is what I have so far:
Future<List<String>> getResDocIDS() async {
List<String> ids = ["none"];
DocumentReference collectionDoc = _firestore.collection("Restaurant").where(
_auth.currentUser!.uid,
isEqualTo: _firestore
.collection("Restaurant")
.doc()
.collection("Orders")
.doc(_auth.currentUser!.uid))
return ids;
}
Restaurants Collection
Order Collection

Firestore queries can only have conditions on data that is returned by that query. So there's no way to run a query on Restaurant with a condition on each restaurant's Orders subcollection.
If you want to search across all Orders subcollections, you'll need to use a collection group query. Then from the resulting orders, you can determine the parent restaurant document reference, and if needed load those documents.
Alternatively, you can add a orderingUsers field to each restaurant where you track the UID of users who ordered from that restaurant, but you'll have to keep an eye on the size of the document in that case.

Related

getting number documents of a collection in Firebase in dart flutter

im trying to retrieve the number of courses created, the courses created will show in "Courses" collection.
this is the code i used
String course = '';
FirebaseFirestore.instance
.collection("Courses")
.get()
.then((QuerySnapshot querySnapshot) {
course = querySnapshot.docs.length.toString();
});
this is a screenshots of my firebase
Collections don't know how many documents they contain so to get the count you have to retrieve all of the documents to then get a count. That could be time consuming as well as a lot of reading (cost).
The simple solution is to keep the count in another collection or at a known document location within the collection.
Generically speaking, suppose we have three collections, Courses, Users and Locations and each one could have 0 to thousands of documents.
Users
user_0
...some fields
user_1
...some fields
Courses
course_0
...some fields
course_1
...some fields
Locations
location_0
...some fields
location_1
...some fields
As previously mentioned, if there are a limited number of documents simply reading Users (for example) and getting the count of the documents from the snapshot works and is simple. However, as Users grows so does the document count and cost.
The better and scalable solution is to keep anther collection of counts
Document_Counts
users_collection
count: 2
courses_collection
count: 2
locations_collection
count: 2
Then when you want the number of users, simply read the Document_Counts/users_collection document and get the count from the count field.
Firestore has a lighting fast and simple increment and decrement function so as a document is added to Users, for example, increment that same count field.
With Cloud Firebase 2.0, there is a new way to count documents in a collection. According to reference notes, the count does not count as a read per document but a metaData request:
"[AggregateQuery] represents the data at a particular location for retrieving metadata without retrieving the actual documents."
Example:
final CollectionReference<Map<String, dynamic>> courseList = FirebaseFirestore.instance.collection('Courses');
Future<int> countCourses() async {
AggregateQuerySnapshot query = await courseList.count().get();
debugPrint('The number of courses: ${query.count}');
return query.count;
}

Flutter cloud firestore : query document->map->array

My db design is above picture. I wanna create a query which returns user where tags are matched. But i didnt any solution to query.
This is my flutter code:
But it doesnt work. How can i query array of map of document?
The courses is an array and not a map so you cannot use the dot notation to query. If the courses is made a collection (or a sub-collection) on it's own then you would be able to query users easily:
users -> {userId}
(col) (doc)
courses -> {courseId}
(col) (doc)
You would have to include a field userId in each course document which would be used to identify which user owns that course.
await firestore.collection("courses").where("tags", arrayContainsAny: tagKeys)
This will return all courses where the tags array contains at least 1 item in the tagKeys list. If you need exact match i.e. all the tags in tagKeys must be present in Firestore document then you would have to restructure the database as mentioned in this answer.
Fetching all matching documents might not be ideal since you just need user IDs that matches the tags. In that case you can store a field which contains tags from all the courses in a single array field in the user document.

arrayContains in subCollection

Cant seem to understand if arrayContains works on subCollections in Firestore.
This code does not return any data
Firestore.firestore()
.collection("all_chatrooms")
.whereField("postmoderators", arrayContains: userId)
However specifying the targeted collection path does...
Firestore.firestore()
.collection("all_chatrooms")
.document("randomId")
.collection("chatroom")
.whereField("postmoderators", arrayContains: userId)
is there a way to search for an array within subCollections without specifying a documentId?
Do you maybe have to store userIds within a map to be able to search within subColletions?
Firestore queries are shallow, meaning that when you query a specific collection "C1", only the documents from this collection are returned, and not the documents in the subcollections of the documents in "C1".
However, in your case, Collection Group queries may "come to the rescue". As indicated in the doc:
A collection group consists of all collections with the same ID. For
example, if each document in your cities collection has a
subcollection called landmarks, all of the landmarks subcollections
belong to the same collection group.
So if all the docs in the all_chatrooms collection have a subcollection named chatroom you can use a Collection Group query as follows:
Firestore.firestore().collectionGroup("chatroom").whereField("postmoderators", arrayContains: userId)
Of course, by doing so you will get ALL the docs (with userId in postmoderators) in ALL the chatroom subcollections of ALL the docs in the all_chatrooms collection.

Is it possible to fetch all documents whose sub-collection contains a specific document ID?

I am trying to fetch all documents whose sub-collection contain a specific document ID. Is there any way to do this?
For example, if the boxed document under 'enquiries' sub-collection exists, then I need the boxed document ID from 'books' collection. I couldn't figure out how to go backwards to get the parent document ID.
I make the assumption that all the sub-collections have the same name, i.e. enquiries. Then, you could do as follows:
Add a field docId in your enquiries document that contains the document ID.
Execute a Collection Group query in order to get all the documents with the desired docId value (Firestore.instance.collectionGroup("enquiries").where("docId", isEqualTo: "ykXB...").getDocuments()).
Then, you loop over the results of the query and for each DocumentReference you call twice the parent() methods (first time you will get the CollectionReference and second time you will get the DocumentReference of the parent document).
You just have to use the id property and you are done.
Try the following:
Firestore.instance.collection("books").where("author", isEqualTo: "Arumugam").getDocuments().then((value) {
value.documents.forEach((result) {
var id = result.documentID;
Firestore.instance.collection("books").document(id).collection("enquiries").getDocuments().then((querySnapshot) {
querySnapshot.documents.forEach((result) {
print(result.data);
});
First you need to retrieve the id under the books collection, to be able to do that you have to do a query for example where("author", isEqualTo: "Arumugam"). After retrieving the id you can then do a query to retrieve the documents inside the collection enquiries
For example, if the boxed document under 'enquiries' sub-collection exists, then I need the boxed document ID from 'books' collection.
There is no way you can do that in a single go.
I couldn't figure out how to go backwards to get the parent document ID.
There is no going back in Firestore as you probably were thinking. In Firebase Realtime Database we have a method named getParent(), which does exactly what you want but in Firestore we don't.
Queries in Firestore are shallow, meaning that it only get items from the collection that the query is run against. Firestore doesn't support queries across different collections in one go. A single query may only use the properties of documents in a single collection. So the solution to solving your problem is to perform two get() calls. The first one would be to check that document for existence in the enquiries subcollection, and if it exists, simply create another get() call to get the document from the books collection.
Renaud Tarnec's answer is great for fetching the IDs of the relevant books.
If you need to fetch more than the ID, there is a trick you could use in some scenarios. I imagine your goal is to show some sort of an index of all books associated with a particular enquiry ID. If the data you'd like to show in that index is not too long (can be serialized in less than 1500 bytes) and if it is not changing frequently, you could try to use the document ID as the placeholder for that data.
For example, let's say you wanted to display a list of book titles and authors corresponding to some enquiryId. You could create the book ID in the collection with something like so:
// Assuming admin SDK
const bookId = nanoid();
const author = 'Brandon Sanderson';
const title = 'Mistborn: The Final Empire';
// If title + author are not unique, you could add the bookId to the array
const uniquePayloadKey = Buffer.from(JSON.stringify([author, title])).toString('base64url');
booksColRef.doc(uniquePayloadKey).set({ bookId })
booksColRef.doc(uniquePayloadKey).collection('enquiries').doc(enquiryId).set({ enquiryId })
Then, after running the collection group query per Renaud Tarnec's answer, you could extract that serialized information with a regexp on the path, and deserialize. E.g.:
// Assuming Web 9 SDK
const books = query(collectionGroup(db, 'enquiries'), where('enquiryId', '==', enquiryId));
return getDocs(books).then(snapshot => {
const data = []
snapshot.forEach(doc => {
const payload = doc.ref.path.match(/books\/(.*)\/enquiries/)[1];
const [author, title] = JSON.parse(atob(details));
data.push({ author, title })
});
return data;
});
The "store payload in ID" trick can be used only to present some basic information for your child-driven search results. If your book document has a lot of information you'd like to display once the user clicks on one of the books returned by the enquiry, you may want to store this in separate documents whose IDs are the real bookIds. The bookId field added under the unique payload key allows such lookups when necessary.
You can reuse the same data structure for returning book results from different starting points, not just enquiries, without duplicating this structure. If you stored many authors per book, for example, you could add an authors sub-collection to search by. As long as the information you want to display in the resulting index page is the same and can be serialized within the 1500-byte limit, you should be good.
The (quite substantial) downside of this approach is that it is not possible to rename document IDs in Firestore. If some of the details in the payload change (e.g. an admin fixes a book titles), you will need to create all the sub-collections under it and delete the old data. This can be quite costly - at least 1 read, 1 write, and 1 delete for every document in every sub-collection. So keep in mind it may not be pragmatic for fast changing data.
The 1500-byte limit for key names is documented in Usage and Limits.
If you are concerned about potential hotspots this can generate per Best Practices for Cloud Firestore, I imagine that adding the bookId as a prefix to the uniquePayloadKey (with a delimiter that allows you to throw it away) would do the trick - but I am not certain.

How to make hierarchy structure for menuplating a field of documents by multiple users in firestore

I am new to Firestore. I have a list of items(documents) in a collection C1. there is a button like to select your favorite item and if a user makes item favorite or not favorite then it should show only to himself, not to all other users.
I am making an array of strings as a field of the document for "like" where I am putting the ids of users who are liking the items if a user dislikes it I remove the id of users. Every time I have to fetch all data and like string, array to check whether my id is there or not but this is really lazy logic as records increase it will be stuck,
I can not perform the long operation in mobile, for example, there may be 50k size of "Like" array, I can't search my id from 50k and then goes to next item and then again finding id. so how I can achieve like this
Firestore.instance
.collection("puppies")
.where('puppy_fav', isEqualTo: true).where('puppy_owner_email', isEqualTo: emailOwner)
.snapshots();
there should some operation on firestore it should return me only yes on not against my id.

Resources