Deleting document(s) inside a collection with documentReference - flutter

I have a collection in firebase called "community" and "events".
When an event document is created, a field "communityRef" is included as docRef to community.
I'm trying to figure a code that when a community is deleted, all events related to the community are also deleted.
building this in flutterflow using custom action
onTap on a button in UI, the code is included as one of the actions before community document is deleted.
Future deleteAllRefEvents(DocumentReference community) async {
final instance = FirebaseFirestore.instance;
final batch = instance.batch();
var collection = instance.collection('events');
var snapshots = await collection.where('communityRef', isEqualTo: DocumentReference).get();
for (var doc in snapshots.docs) {
batch.delete(doc.reference);
}
await batch.commit();
}

You are passing DocumentReference type instead of variable community
Your code should be as following
Future deleteAllRefEvents(DocumentReference community) async {
final instance = FirebaseFirestore.instance;
final batch = instance.batch();
var collection = instance.collection('events');
var snapshots = await collection.where('communityRef', isEqualTo: community).get();//Here you used DocumentReference instead of community
for (var doc in snapshots.docs) {
batch.delete(doc.reference);
}
await batch.commit();
}

Related

Is it possible to prevent two people from adding products to the cart at the same time with Firebase?

I am importing a product catalog using Firebase DB with Flutter. Only 1 person can add the product to the cart. If 2 or more people want to add the product to the basket at the same time, the product is added to all of them. What can I do to get this back?
void addBasket(Map<String, dynamic> data, index) async {
var _storage = await GetStorage();
final CollectionReference user_basket =
await FirebaseFirestore.instance.collection('user basket');
final CollectionReference product =
await FirebaseFirestore.instance.collection('product');
final DocumentReference ref = await product.doc(data['id']);
await FirebaseFirestore.instance.runTransaction((Transaction tx) async {
DocumentSnapshot snap = await tx.get(ref);
if (snap.exists) {
await product.doc(data['id']).delete();
await tx.update(ref, data);
await user_basket
.doc(_storage.read('uid'))
.collection('basket')
.doc(data['id'])
.set(data);
inArea.removeAt(index);
Get.back();
Get.back();
} else {
await user_basket
.doc(_storage.read('uid'))
.collection('basket')
.doc(data['id'])
.delete();
Get.back();
Get.defaultDialog(
title: "Wrong",
middleText:
"This product is being reviewed by another person.");
}
});
}
I tried to use Transaction as you can see in the code. I tested this code the first time I wrote it. when two people pressed the button at the same time, one of the 2 could add the product to the cart. I wanted to test again today. I pressed the add product to cart button, as a result both the If and Else blogs worked. So I started getting errors. The exact result I want to get is when more than 1 person wants to add the same product to the cart, to get the opposite. Let the first click on the button add the product to the basket, and the others will be informed through the Alerd Dialog.
I believe you are creating a race condition between clients. In all of my implementations of Firestore, I do not use await on the collection or query references. You should only need to await the get() method itself.
When you look at Flutter Firestore documentation for their implementation of transactions, you'll see they do not await the collection reference at the top.
https://firebase.google.com/docs/firestore/manage-data/transactions#dart
Instead of:
final CollectionReference user_basket =
await FirebaseFirestore.instance.collection('user basket');
final CollectionReference product =
await FirebaseFirestore.instance.collection('product');
final DocumentReference ref = await product.doc(data['id']);
Try using:
final CollectionReference user_basket = FirebaseFirestore.instance.collection('user basket');
final CollectionReference product = FirebaseFirestore.instance.collection('product');
final DocumentReference ref = product.doc(data['id']);
I hope that helps! I will try and put together a Firestore transactions demo to see if I can replicate and resolve that issue you described.
It could be data race issue. you can use transaction class to prevent it.

How to return a Stream using an async* function in Flutter

I am working on a chat app using Flutter and Firebase. I am new to Dart and so got stuck when I wanted to create a function which fetches (using await) a particular document from one collection (forums) and use an array property of the forum document to query and return a Stream from another collection (openMessages). The problem with my current solution is that it always returns an empty array. I am sure I am using the keywords or logic incorrectly. Can you please help me refactor my method.
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSent', descending: true)
.snapshots()
.map((event) {
for (var document in event.docs) {
messages.add(ChatMessage.fromMap(document.data()));
}
});
//print('LENGTH:'+messages.length.toString());
yield messages;}
You can use the following method.
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
final firestore = FirebaseFirestore.instance;
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
final result = firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSent', descending: true)
.snapshots();
await for (final r in result) {
final docs = r.docs;
for (final document in docs) {
messages.add(ChatMessage.fromMap(document.data()));
yield messages;
}
}
}
Or
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
final firestore = FirebaseFirestore.instance;
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
yield* firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSend', descending: true)
.snapshots()
.map((event) =>
event.docs.map((e) => ChatMessage.fromMap(e.data())).toList());
}

Firebase Reference

How can I reference the collection in the document I auto-id in Firebase?
final CollectionReference _olanaklar5 = _database
.collection("Kategoriler")
.doc("Hoteller")
.collection("5_Yıldızlı")
.doc() //======> here
.collection("Olanaklar");
You can get list all document id with doc.id
List<String> _listDocId = [];
await fireStore
.collection("Kategoriler")
.doc("Hoteller")
.collection("5_Yıldızlı")
.get()
.then((QuerySnapshot querySnapshot) {
for (var doc in querySnapshot.docs) {
_listDocId.add(doc.id);
}
});
and query in list doc id
for (var id in _listDocId) {
final CollectionReference _olanaklar5 = _database
.collection("Kategoriler")
.doc("Hoteller")
.collection("5_Yıldızlı")
.doc(id)
.collection("Olanaklar");
}
If you're using Firebase auth, it's preferred to keep User's UID as doc, else you can use .where() as a query to match the fields in all documents. But as the app scales, it will be a hectic process and will consume many reads.

Flutter - Stream not returning data

I have a document collection called posts_favorites, which stores the reference to all the Posts that a user has bookmarked. The posts_favorites collection look as follows:
I have created a Stream to get all posts references that belong to a specific user, then I want to get the Post documents from the posts collection using the posts references.
I created a Stream to produce the data I need, but I am not getting any data returned from my Stream. Here is my code for the Stream:
Stream<List<PostsRecord>> getFavoritePostsStream() async* {
List<PostsRecord> myList = [];
await FirebaseFirestore.instance
.collection("posts_favorites")
.where("user", isEqualTo: currentUserReference)
.get()
.then((favoriteList) {
favoriteList.docs.forEach((element) async {
String documentPath = element['post'].toString();
var start = documentPath.indexOf("/");
var end = documentPath.indexOf(")");
var documentRef = documentPath.substring(start+1, end);
//DocumentReference docRef = FirebaseFirestore.instance.doc(documentPath);
DocumentReference docRef = FirebaseFirestore.instance.collection('posts').doc(documentRef);
await docRef.get().then((DocumentSnapshot documentSnapshot) async {
if (documentSnapshot.exists) {
print('Document exists on the database');
PostsRecord postsRecord = PostsRecord.getDocumentFromData(
documentSnapshot.data(), element['post']);
//return myList.add(postsRecord);
//print(postsRecord);
return postsRecord;
}
});
});
});
}
I know this function works because the commented code produces the Post Records that I expect to get from Firestore.
I am not sure how to get the Stream function to return data that I can use in the Stream.
Thank you

How to retrieve one specific document from a collection on cloud firestore?

I need to retrieve a document from the 'announcements' collection of my document. Each of the documents in the collection is named a day of the year, for example '2020-07-02'. I want to retrieve the document for the current day. In the document, I need to access a map array titled 'details' containing the various announcements. How would I write that into my app? Here is what I am doing currently, but I don't know where to go from there.
Future getAnnouncements() async {
var currentDate = DateFormat("yyyy-MM-dd").format(DateTime.now());
var firestore = Firestore.instance;
Stream<DocumentSnapshot> qn = firerestore.collection('announcements').document(currentDate).snapshots();
return qn; //I want to only return the map array of the various documents
}
Try this
Future getAnnouncements() async {
var currentDate = DateFormat("yyyy-MM-dd").format(DateTime.now());
var firestore = Firestore.instance;
Future<DocumentSnapshot> qn = await firerestore.collection('announcements')
.document(currentDate)
.get();
return List<Map>.castFrom(qn["details"]);
}
To parse through the returned List, you could try this
List<Map> retrievedData = await getAnnouncements();
for(int i = 0; i < retrievedData.length; i++)
{
print(retrievedData[i]["title"]);
print(retrievedData[i]["text"]); // Thanks #VLXU
}