I have list of Collection IDs. Is there any way I can query and get all the documents under these collection ids? without iterating to each id in the list.
Thank you for your time
According to your last comment, I understand that you want to get all documents within a single collection and not to query multiple collections, which is not possbile for the moment in Firestore.
If you have a list of ids, then simply iterate over it and create for each id in the list the corresponding DocumentReference and then add all those references to a List<DocumentReference>. After that, iterate over the new list and for each reference create a Task and then add all those Tasks objects to List<Task<DocumentSnapshot>>.
In the end, just pass the list of Tasks to Tasks's whenAllSuccess() method:
Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//Do what you need to do with your list
for (Object object : list) {
YourObject yb = ((DocumentSnapshot) object).toObject(YourObject.class);
Log.d("TAG", yb.getPropertyName);
}
}
});
In code it looks like my answer from this post:
Android Firestore convert array of document references to List<Pojo>
Related
I have a Firebase Firestore configuration as show below:
How would I check if eventParticipants's contains a uid matching the current user's uid? The below code used to work when eventParticipants used to be an array of user id's, but since creating more detailed array objects, the code seems to not work:
data["eventParticipants"]
.contains({
"uid": FirebaseAuth
.instance.currentUser!.uid
.toString()
})
Usually the above code would return a bool, and I would use the result in a ternary operator to load a widget, however, I am unable to rework the logic with the new array structure. Subsequently, how would I remove an array object if it's uid field matches the current user's id?
FirebaseFirestore
.instance
.collection(
"events")
.doc(document.id)
.set(
{
"eventParticipants":
FieldValue
.arrayRemove([
{
"uid": FirebaseAuth
.instance
.currentUser
?.uid
}
])
},
SetOptions(
merge: true),
);
Any pointers would be appreciated!
The arrayRemove (and arrayUnion and arrayContains) operators expect you to pass the complete, exact contents of the item in the array. In your case it looks for an item in the array with a single field uid with the value you pass.
So unless you know the values of all properties of the array item that you want to remove, you'll have to:
Read the document with the array in it.
Manipulate the array in your application code.
Write the entire array back to the database.
Also see:
Firestore, how to structure a "likedBy" query
Firestore conditional array query
Firestore array-contains-any is not working properly
I'm creating todo list in which I'll have two lists one for incomplete tasks and another for completed tasks...I'm able to retrieve the data from Firestore by pulling the whole data without any condition, but for the uncompleted list, I want to retrieve the document only with particular field values. For example, each document contains the following fields as shown in the picture:
Here is the code which I've used to get the data from firebase firestore:
static List<Task> getTasks() {
//convert firebase collection to list
List<Task> tasks = [];
FirebaseFirestore.instance.collection('Todos').snapshots().listen((data) {
data.docs.forEach((doc) {
tasks.add(Task.fromMap(doc.data()));
});
});
return tasks;
}
I want similar function like this to retrieve only documents with taskStatus == true. So, I can store all those tasks which have taskStatus of true on separate list and display them accordingly.
You can use the where function. It works like below.
For more: Cloud FireStore Filtering
FirebaseFirestore.instance
.collection('Todos')
.where('taskStatus', isEqualTo: true)
.snapshots().listen((data) {
data.docs.forEach((doc) {
other_tasks.add(Task.fromMap(doc.data()));
});
});
I have a list of documents in firestore. When the user adds a new document I would like to update the document list as models. I have this working but it seems like a bad method.
I get the stream like this:
Stream buildingsStream = FirebaseFirestore.instance.collection('buildings').snapshots();
Then I create an empty list of BuildingModel. I do a forEach on the stream, add convert each document in the stream to BuildingModel and add it to the list. Finally, I update the value of the List of building model with the new list. Like this:
Stream buildingsStream = _firestoreApi.buildingsStream;
buildingsStream.listen((result) {
List<BuildingModel> buildings = [];
result.docs.forEach((document) => buildings.add(BuildingModel.fromJson(document.data())));
_buildings.value = buildings;
});
This is obviously not the best way to do it. Every time I add a single value, it does a for loop through the whole stream and updates with a new list.
How can I do this better?
Thanks
/Countries/Lebanon/Governorates/Mount Lebanon/Districts/Chouf/Cities/Wadi al-Zayneh/Data/Products/Main Categories/Restaurants & Bakeries/Sub Categories/Snack/Sub Categories/Abo Arab Cafe
So as you can see, this is a snippet from my current Firestore structure. So many deeply nested collections. The issue is, I want to keep going deeper as long as a collection called 'Sub Categories' is found which in that case I would render them in the UI. And when eventually I reach a level where 'Sub Categories' is not found, I will render a different UI and show the actual products (The last document "Abo Arab Cafe" contains all the products as maps). The pattern of how many Sub Categories there are is unexpectable and can be modified by the end user.
How can I keep checking for Sub Categories? How to manage my queries in a way that they are dynamically generated at each level at the client-side?
I use Flutter. Here is my current queries structure:
import 'package:cloud_firestore/cloud_firestore.dart';
class FirebaseServices {
final FirebaseFirestore _db = FirebaseFirestore.instance;
CollectionReference mainCategoryCollectionReference() {
CollectionReference mainCategoryCollectionReference = _db.collection(
'/Countries/Lebanon/Governorates/Mount Lebanon/Districts/Chouf/Cities/Wadi al-Zayneh/Data/Products/Main Categories');
return mainCategoryCollectionReference;
}
CollectionReference subCategoryCollectionReference(
String parentSelectedCategory) {
CollectionReference mainCategoryCollectionReference = _db.collection(
'/Countries/Lebanon/Governorates/Mount Lebanon/Districts/Chouf/Cities/Wadi al-Zayneh/Data/Products/Main Categories/$parentSelectedCategory/Sub Categories');
return mainCategoryCollectionReference;
}
bool checkIfSubCategoriesExist(CollectionReference collectionReference) {
bool subCategoriesExist;
collectionReference.get().then((value) => {
subCategoriesExist = value.docs.isNotEmpty,
print('SubCategoriesExist: $subCategoriesExist')
});
return subCategoriesExist;
}
}
This works only if I know for certain how many levels of deepness there are, but since this can be modified by the user, it won't work.
Sorry for the very long question I had no idea how to explain it properly and clearly. Thank you in advance!
The structure is all wrong, there is no point in the structure being this deeply nested. The structure of the database needs to match what has to appear in the UI.
Assuming this is a worldwide application since you are using countries then you have to do the following:
Collection
Document
Fields
Countries
Random ID
countryName - arrayOfDistrict- arrayOfGovernorates
3 Fields under each document id, containing information about the country.
Then regarding Resturants:
Collection
Document
Fields
SubCollection
subCollectionId
Fields
Resturant
Random ID
resturant_name- resturant_location - info_about_resturant
Menu
randomId
dish_name - price -...
The problem with your db structure is that it is very nested instead of making a flat structure and that right now you are harcoding the whole path.
Using the above structure, you can create a dropdown with list of countries if the user chooses Lebanon, then you get the districts and the governorates. Then you can do a call to get the resturants that are inside each district, since in the documents inside Resturant collection you can get location of each resturant and name.
After that on click of each resturant, you will get the data inside the subcollection that will contain the full menu.
I think I found the solution with the help of a friend!
Since the checkIfSubCategoriesExist function is always checking on the very last reached level(using the collectionReference argument) whether Sub Categories exists or not, he suggested that in case it does exist, I can append to its argument collectionReference the new "Sub Categories" String to the path as a variable! This way I can query on it and voila!
I need to query a collection based on a list of parameters.
For example my model is:
public class Product
{
string id{get;set;}
string title{get;set;}
List<string> tags{get;set;}
DateTime createDate{get;set;}
DbReference<User> owner{get;set;}
}
public class User
{
string id{get;set;}
...other properties...
}
I need to query for all products owned by specified users and sorted by creationDate.
For example:
GetProducts(List<string> ownerIDs)
{
//query
}
I need to do it in one query if possible not inside foreach. I can change my model if needed
It sounds like you are looking for the $in identifier. You could query products like so:
db.product.find({owner.$id: {$in: [ownerId1, ownerId2, ownerId3] }}).sort({createDate:1});
Just replace that javascript array [ownerId1, ...] with your own array of owners.
As a note: I would guess this query is not very efficient. I haven't had much luck with DBRefs in MongoDB, which essentially adds relations to a non-relational database. I would suggest simply storing the ownerID directly in the product object and querying based on that.
The solution using LINQ is making an array of user IDs and then do .Contains on them like that:
List<string> users = new List<string>();
foreach (User item in ProductUsers)
users .Add(item.id);
return MongoSession.Select<Product>(p => users .Contains(p.owner.id))
.OrderByDescending(p => p.createDate)
.ToList();