how to delete one to many associations in flutter firebase - firebase

I am developing a flutter application and I am using firebase database as a database service.
Now my since my app is a task management app my database have three main collections:
+roles
+tasks
+users
every role is a document with a unique id and name
every task is a document with a unique id a name and a role id
now in the app the user is able to add roles in the database I just add a new role document to the roles collection
but I am stuck when it comes to deleting a role.
Obviously roles and tasks are a one-to-many association since one role can be associated with none or more than one task but one task can at most have only one role.
So when I want to delete a role I have to delete the associated tasks with it as well and only deleting the role will then generate a null error in the task since it doesn't find the specified role in the database anymore.
Now I really don't find any hint in the flutter firebase documentation on how to delete more than one document based on a selected criteria.
for example deleting one document based on its document id is straightforward
Future<void> deleteRole(Role role) {
final reference = databaseInstance.document('/roles/${role.id}');
return reference.delete();
}
if i want to read a task based on certain criteria like someId i will just write something like this
Stream<List<Task>> readTasksStream({#required String someId}) {
Stream<QuerySnapshot> snapshots = databaseInstance
.collection('tasks')
.where('someId', isEqualTo: someId)
.snapshots();
//now deserialize the data to objects and return it
return snapshots.map((snapshot) => snapshot.documents
.map((snapshot) => Task.fromMap(snapshot.data))
.toList());
}
but how can i actually do the same for deleting a task based on someId in this case the roleId.
Something like this:
deleteTasksAssociatedWithARole({#required String roleId}) {
Stream<QuerySnapshot> snapshots = databaseInstance
.collection('tasks')
.where('someId', isEqualTo: someId)
.snapshots();
//now I want to delete the selected document from the querysnapshot
// but how ????????
}
I hope you get what I want to do here.
Any suggestion or help is very welcomed.
Thank you so much

To delete all documents matches by the query, you loop over the stream for forEach and then delete them each in turn.
Something like:
snapshots.forEach((snapshot) => snapshot.documents
.forEach((snapshot) => Task.fromMap(snapshot.data))
.toList());
}
snapshots.forEach((document) => document.reference.delete());
I find it easiest to figure out how to do this by following the types in the reference documentation, in this case from the snapshots property.

Frank van Puffelen
thank you so much it worked now.
so the final code is like this:
Future<void> deleteTasksAssociatedWithARole({#required Role role}) {
Stream<QuerySnapshot> snapshots = databaseInstance
.collection('tasks')
.where('roleId', isEqualTo: role.id)
.snapshots();
return snapshots.forEach((snapshot) =>
snapshot.documents.forEach((document) => document.reference.delete()));
}
so instead of deserializing the document snapshots to objects you loop through them with a forEach loop extract its reference to delete the document.

Related

Querying FireBase FireStore Document in Flutter

I wants to achieve one query which i'm trying for few hours in FireStore Database, Please check the below image,
The Query I want to do is, In the document which I marked, will have a list of group id (combination of 2 userIds in each , userId1-userId2), I want to query this document by having one userId that contains in the group id (before or after hyphen) and returns a list of snapshots , which should be used for StreamBuilder in a list widget.
For Example:
I may have 'abcdefghijkl' userId, I want to check each document ID that contains this userId, and returns a list of snapshots which matches.
Thanks in Advance!
I don't think the query you want is possible, since there is no operation to check if a document id contains something. I would recommend adding a participants array to the document and than use an array-contains query, see also here.
There are several ways to achieve what you're after - a list of users relating to a given document - but I think you should rethink your data structure first. Each message document should have a users field that is an array of Firestore document IDs which would contain 1 or more document IDs of the users that this message relates to.
With that, you can easily query the database for all messages where a given user is referenced:
final messages = db
.collection('messages')
.where('users', arrayContains: userId)
.get();
BTW, to take things a step further, I would structure my data like this:
user {
displayName: '',
email: '',
messages: [
{
authorId: userId,
content: '',
users: [userId, ...],
sentOn: '',
},
...
]
}
With this you can do two things:
Fetch all messages created by the user:
final userMessages = db.doc(userId).collection('messages').get();
Fetch all messages where user participated:
final allUserMessages = db
.collectionGroup('messages')
.where('users', arrayContains: userId)
.get();
Check out the Firestore docs, as they have plenty of examples there.

Get detail information from master-detail collection in firestore

I have created a simple app using flutter and firebase firestore database. There are 'users' collections and each user has 'posts' collections. Each post may have one or more posts added by different users.
I am trying to get all the posts regardless of users. However, my current function was written for reading records only shows the posts relevant for login user, not all the posts.
Stream<QuerySnapshot> readItems({required String collectionName}) {
CollectionReference detailCollection = _firestore
.collection(mainCollectionName!)
.doc(userUid)
.collection(collectionName);
return detailCollection.snapshots();
}
Here, I pass 'users','login user's uid' and 'posts' as mainCollectionName, userUid and collectionName respectively. Can anybody guide me how do I get all the posts regardless of users?
After searching I found a solution here. The following method gives the desired output.
Stream<QuerySnapshot> readItems({required String collectionName}) {
Query<Map<String, dynamic>> detailCollection = _firestore.collectionGroup(collectionName);
return detailCollection.snapshots();
}
It is possible to get all sub collections data from all the documents in a collection. Try this way
_firestore
.collection(`${mainCollectionName}/*/${collectionName}`)
.get()
Refer to this page for more details about querying collections and sub collections - https://firebase.googleblog.com/2019/06/understanding-collection-group-queries.html

Flutter Firestore - How to get data from a Document Reference in a Document Field?

I'm building a Self-learning app with differente questions types. Right now, one of the questions have a field containing a list of DocumentReferences:
In Flutter, I have the following code:
Query<Map<String, dynamic>> questionsRef = firestore
.collection('questions')
.where('lesson_id', isEqualTo: lessonId);
await questionsRef.get().then((snapshot) {
snapshot.docs.forEach((document) {
var questionTemp;
switch (document.data()['question_type']) {
....
case 'cards':
questionTemp = CardsQuestionModel.fromJson(document.data());
break;
....
}
questionTemp.id = document.id;
questions.add(questionTemp);
});
});
Now, with "questionTemp" I can access all the fields (lesson_id,options,question_type, etc..), but when it comes to the "cards" field, how Can I access the data from that document reference?
Is there a way to tell firestore.instance to get the data from those references automatically? Or do I need to make a new call for each one? and, if so, how can I do that?
Thank you for your support in advance!
Is there a way to tell firestore.instance to get the data from those
references automatically? Or do I need to make a new call for each
one?
No there isn't any way to get these documents automatically. You need to build, for each array element, the corresponding DocumentReference and fetch the document.
To build the reference, use the doc() method
DocumentReference docRef = FirebaseFirestore.instance.doc("cards/WzU...");
and then use the get() method on this DocumentReference.
docRef
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document exists on the database');
}
});
Concretely, you can loop over the cards Array and pass all the Futures returned by the get() method to the wait() method which "waits for multiple futures to complete and collects their results". See this SO answer for more details and also note that "the value of the returned future will be a list of all the values that were produced in the order that the futures are provided by iterating futures."

Getting a single document out of Firestore in Flutter using a StreamProvider

I've followed various guides to successfully get data out of a Firestore collection with a StreamProvider. What I can't quite seem to figure out is how to get a singular document and its associated fields.
For example, let's say I have a collection 'WeatherObs' with a document called '5-13-21' as shown below:
If I wanted to pull in the whole collection with my StreamProvider, I could easily just put it into a list like so:
class FirestoreService {
FirebaseFirestore _db = FirebaseFirestore.instance;
var random = Random();
Stream<List<Weather>> getWeather() {
return _db.collection('TodaysWeather').snapshots().map((snapshot) => snapshot.docs.map((event) => Weather.fromJson(event.data())).toList());
}
I can't for the life of me, however, figure out how if I wanted to just access a single document the same way. I am able to get the document accessible in a key, value pair so I can use fromJson. But when I access the Provider object that calls the method in another class, it always returns null.
Stream<Weather> getWeather() {
return _db.collection('TodaysWeather').doc('5-13-21').snapshots().map((event) => FastFoodHealthEUser.fromJson(event.data()));
}
In the last example, I am not returning a list, but I don't think a list is necessary as I should be able to access the weather object and get access to its attributes like high (type String). Is accessing a single document in the same manner possible?
You can use where for checking on unique field on your documents like this code
Stream<List<VehicleCommentSessionModel>> getSomeoneCommentsList(
{#required String sellerId}) {
return _fbd
.collection('comments')
.where('sellerId', isEqualTo: sellerId)
.snapshots()
.map((qSnap) => qSnap.docs
.map((doc) => VehicleCommentSessionModel.fromJson(doc.data()))
.toList());
}

how do I check collection exists or not in firestore on flutter?

I get some data from firestore. Sometimes when I called to get data, the collection is not created yet.
before calling get request, how do I check collection is exists or not?
Stream<List<ChatModel>> getChat(ChatFieldModel model) {
var ref = _db.collection('chats');
return ref
.document(model.docId)
.collection('messages')
.orderBy('timestamp', descending: true)
.snapshots()
.map((list) =>
list.documents.map((doc)=>ChatModel.fromForestore(doc)).toList());
}
I posted this before
final snapshot = await firestore.collection(roomName).getDocuments();
if (snapshot.documents.length == 0) {
//doesnt exist
}
Hope this helps
Collections are not created or deleted independently of documents. When you create a document and specify it as being part of a certain collection, that collection is created automatically if it does not already exist. Similarly, when you remove the last document in a collection, that collection will automatically be deleted. So there is really no circumstance where you need to worry about whether a collection has been created or not, and you have no explicit control over creating or deleting collections.
Usually a collection in Firestore gets deleted if no Documents exist in it.
However, there might be cases where you want to keep a history of the collection modification events, or let's say for some reason prevent Collections from being deleted.
Or for example, you want to know when a collection was created in the first place. Normally, if the Documents are deleted, and then the Collection gets created again, you will not know the initial creation date.
A workaround I can think of is the following:
Initialize each collection you want with a Document that will be specifically for keeping generic info about that collection.
For example:
This way, even if all other Documents in the Collection are deleted, you'll still keep the Collection in addition to some info that might be handy if In the future you need to get some history info about the Collection.
So to know if a Collection exists of no, you can run a query that checks for a field in Info Documents (eg CollectionInfo.exists) to know which Collections have been already created.
This is for the most recent update
final snapshot = await FirebaseFirestore.instance
.collection('collection name').get();
if ( snapshot.size == 0 ) {
print('it does not exist');
}
Feb 2022
Get QuerySnapshot and return querySnapshot.docs.isNotEmpty or isEmpty
Future<bool> isItems() async {
CollectionReference collectionReference =
_firestore.collection("users").doc(_user!.uid).collection("cart");
QuerySnapshot querySnapshot = await collectionReference.get();
return querySnapshot.docs.isNotEmpty;
}
await FirebaseFirestore.instance
.collection('collectionName')
.limit(1)
.get()
.then((snapshot) {
if (snapshot.size == 0) {
print("No collection");
}
});

Resources