Is it possible to write to a Cloud Firestore NEW document subcollection while user is offline?
In my app, the user will add a new document with items being added to the document's subcollection. Everything works fine when the device is online. But when offline, the first level collection document seems to be added as expected, however the subcollection items are not added until the devise is back online.
I understand that Firestore have offline persistence enabled by default and it will write documents to cache if the device loses connectivity. But is it only able to write the documents to the first level collections?
Can I add to the subcollection without awaiting until the new document is written on the server (i.e. use cache reference) since the persistence feature is not working with async/await functions?
Below is my sample code for writing to Cloud Firestore. I am not able to fetch the subcollections until back online - the data from get() will return empty.
Can anyone please help me with this? Apologies if this has been answered somewhere - I couldn’t find it.
void main() async {
await Firebase.initializeApp();
FirebaseFirestore _firestore;
_firestore = FirebaseFirestore.instance;
runApp(
MultiProvider(
providers: [
Provider<MyFirestoreFunctions>(create: (_) => MyFirestoreFunctions(_firestore)),
],
child: MyApp(),
),
);
}
class MyFirestoreFunctions {
MyFirestoreFunctions(this._firestore) : assert(_firestore != null);
final FirebaseFirestore _firestore;
Future<DocumentReference> addNewDocument(DocClass doc, ItemClass item,) {
Future<DocumentReference> result = _firestore
.collection('Documents')
.add(doc.toJson())
.collection(‘Items’) // I cannot do this without await and cannot use await while offline.
.add(item.toJson());
return result;
}
}
I think I figured it out with some help from another answered question on here. Obviously, I was doing it wrong… I changed the code to first generate a new DocumentReference and set its values from the map. This way I don’t have to await for the Future to return and will have the reference of the document to add to its subcollection. Now everything works as expected. Here’s the rewrite to the sample code:
Future<void> addNewDocument(
{#required DocClass doc,
#required ItemClass item}) {
final DocumentReference docRef = _firestore.collection(‘Documents’).doc();
doc.id = docRef.id;
_firestore.collection(‘Documents’).doc(doc.id).set(doc.toJson());
final DocumentReference itemRef = docRef.collection('Items').doc();
item.id = itemRef.id;
docRef
.collection(‘Items’)
.doc(item.id)
.set(item.toJson());
}
Related
I have a collection in firebase called "community" and "users". All user records have a field "joinedCommunity" (a list of all joined communities).
I'm trying to figure a code that when a community is deleted, all user records are updated to only remove the community reference from "joinedCommunity" field list.
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 userRecordUpdate(DocumentReference community) async {
final instance = FirebaseFirestore.instance;
final batch = instance.batch();
var collection = instance.collection('users');
batch.update(collection, {
"joinedCommunity": FieldValue.arrayRemove([community])
});
await batch.commit();
}
You're using a CollectionReference, when what you want is a DocumentReference. As per the documentation, WriteBatch.update only works on a DocumentReference.
I have a few suggestions:
Try updating the field without using a WriteBatch. Use a for loop and a regular DocumentReference.update() call.
Then, update your code to use a WriteBatch to update the field. Also, keep in mind a batch is limited to 500 operations.
Finally, consider the security implications of allowing a client to be able to update any User document. You should probably update your security rules so that a user document can only be modified by that user. This code is probably something that should run in a Firebase Cloud Function that gets triggered whenever a community document is deleted.
the following code worked -
Future userRecordUpdate(DocumentReference community) async {
final instance = FirebaseFirestore.instance;
final batch = instance.batch();
var collection = instance.collection('users');
var snapshots =
await collection.where("joinedCommunity", arrayContains:
community).get();
for (var doc in snapshots.docs) {
batch.update(doc.reference, {
"joinedCommunity": FieldValue.arrayRemove([community])
});
}
await batch.commit();
}
void _getQuestions() async {
// Query Firestore for questions with the specified tags
Query query = await _firestore
.collection('questions')
.where('tags', arrayContainsAny: widget.tags);
QuerySnapshot querySnapshot = await query.getDocuments();
setState(() {
_questions = querySnapshot.documents;
});
importing cloud_firestore.dart.
I expected the errors to leave, but they are still around.
The method to get the documents is called get() in Flutter, not getDocuments().
I recommend keeping the Firebase documentation handy for this sort of thing, for this case that'd be the section on getting multiple documents from a collection
On the explore page, I get() the entire users collection to create a user list and search results. Inside each of those user documents is a collection posts that I also need to get to create a GridView of each post. I want to reuse that users collection QuerySnapshot instead of fetching each posts collection again to save money. Is this possible?
Here is my current function:
void fetchUsers() async {
final userRef = FirebaseFirestore.instance.collection('users');
final QuerySnapshot result = await userRef.get();
final docs = result.docs.asMap();
docs.forEach((index, value) {
final profile =
ProfileObject.fromJson(value.data() as Map<String, dynamic>);
usersList.add(UserSearchResult(profile, value.id));
/// Below is the code for getting the posts, not working, need ideas
final QuerySnapshot postsResult = value.get('posts');
final posts = postsResult.docs.asMap();
posts.forEach((index, value) {
final post = Post.fromJson(value.data() as Map<String, dynamic>);
postsList.add(post);
});
});
print(usersList);
print(postsList);
}
Here is the structure of my Firestore:
users
uid (doc)
posts (collection)
info (fields)
uid (doc)
posts (collection)
info (fields)
It is not possible to call a collection to get all sub-collections. You should restructure your database to include sub-collection data in document itself. You can use a map or list for that. But remember, calling everything in one go may end up in slow performance and you might end up losing your customers. So the best way is to include the info in every posts' documents. That way, you won't loss your money and user won't feel lag in performance.
It is not possible. You fetch a document, then fetch the (sub)collection under it.
Subcollection data are not included in the initial document snapshots because Firestore queries are shallow. There shouldn't be any cost savings that you can do there?
See the similar Q&A:
Firestore: Get subcollection of document found with where
I tried with different ways but i can't edit the structure of code
//First way
QuerySnapshot querySnapshot = await db.firestoreInstance.collection('user-history').get();
var list = querySnapshot.docs;
print('MY LIST ===== $list');
//Second way
final CollectionReference collectionRef = db.firestoreInstance
.collection(historyCollection);
print('MY SECOND LIST ===== $list');
collectionRef.get().then((qs) {
qs.docs.forEach((element) {
print('MY doc id ${element.id}');
});
});
In my firebase collection(historyCollection) i have four documents but the debugger returns me empty array []. Is there another way to call all documents in certain collection through flutter?
I'm trying to call this method through FutureBuilder component.
My version of firestore is: "cloud_firestore: ^0.16.0+1"
This should do the trick:
Future<List<dynamic>> getCollection(CollectionReference collection) async {
try {
QuerySnapshot snapshot = await collection.get();
List<dynamic> result = snapshot.docs.map((doc) => doc.data()).toList();
return result;
} catch (error) {
print(error);
return null;
}
}
The entire problem was not from these fragments of code. This problem is came out from this that my collections have subcollections. I read about this and i understand that subcollections can live without their ancestors and the only way to access parents is to do this is directly specify the exact path and name of the document. To work this code in my case was needed to add dummy components of my entire set of collections. For more information please look up these two topics:
-> https://firebase.google.com/docs/firestore/using-console
-> Firestore DB - documents shown in italics
I want to delete a document from a collection in Firebase Firestore. I wrote a method, but it's not working and does not delete anything. I need help, and this is my method:
final couponsReference = FirebaseFirestore.instance.collection("Coupons");
Future<void> Deletecoupon() async {
// displayToastMassage('try1', context);
String s=couponsReference.doc().id;
couponsReference.doc(s).delete().catchError((s){
print(s);
});
displayToastMassage('Coupn Code has been deleted sucsseflly', context);
To delete a document you need the documentId of that specific document. In your deletecoupon method you should pass in the documentId and could then use the await keyword to => await couponsReference.doc(documentId).delete();
To delete a particular document from a collection is very easy you just need one thing that documents documents I'd which you want to delete :
FirebaseFirestore.instance .collection("blogs").doc(widget.postid).delete();