I can read data from Firestore, but cannot write data.
I confirm these writing function is not called, from firebase console. The problem occurs in local. How can I write data?
Future updateFavoriteArtists(List<Map> artists) async {
List newlist = artists.map((e) => e['id']).toList();
var ref =
_db.collection('users').document(uid).collection('favorite_artists');
var qs = await ref.getDocuments(); // this can read data.
List<DocumentSnapshot> toDeleteList = List.from(qs.documents);
toDeleteList.map((e) async => await e.reference.delete()); // this cannot delete data.
List<Map> toAddList = List.from(artists);
toAddList.map((e) {
ref.document(e['id']).setData({'id': e['id'], 'name': e['name']}); // this cannot set data.
});
}
This solved by changing toAddList.map to toAddList.forEach. map function just return Iteration and not to be evaluated until someone call it.
Related
I'm trying to run a query that retrieves a single row given a where clause and updates it. I understand that Firebase doesn't support an UpdateWhere operations so I'm trying to use a Transaction instead.
I'm having difficulty making it work, maybe I'm too used to sql dbs... Here's my broken code
try {
final whereQuery = _db
.doc(userPath(user))
.collection("someInnerCollection")
.where("active", isEqualTo: true)
.limit(1);
await _db.runTransaction((transaction) async {
final entry = await transaction.get(whereQuery); // This doesn't compile as .get doesn't take in a query
await transaction.update(entry, {
"someValue": "newValue",
});
});
} catch (e) {
...
}
From the test I’ve made, I would suggest the following to achieve what you mention:
Based on the following answer:
As you can see from the API documentation, where() returns a Query object. It's not a DocumentReference.
Even if you think that a query will only return one document, you still have to write code to deal with the fact that it could return zero or more documents in a QuerySnapshot object. I suggest reviewing the documentation on queries to see examples.
After doing the query consult, you have to get the DocumentReference for that given result.
Then, you can use that reference to update the field inside a Batched writes
try {
final post = await firestore
.collection('someInnerCollection')
.where('active', isEqualTo: true)
.limit(1)
.get()
.then((QuerySnapshot snapshot) {
//Here we get the document reference and return to the post variable.
return snapshot.docs[0].reference;
});
var batch = firestore.batch();
//Updates the field value, using post as document reference
batch.update(post, { 'someValue': 'newValue' });
batch.commit();
} catch (e) {
print(e);
}
You are passing the DocumentSnapshot back in the update() operation instead of DocumentReference itself. Try refactoring the like this:
final docRefToUpdate = _db.collection("colName").doc("docId");
await _db.runTransaction((transaction) async {
final entry = await transaction.get() // <-- DocRef of document to update in get() here
await transaction.update(docRefToUpdate, {
// Pass the DocumentReference here ^^
"someValue": "newValue",
});
});
You can use a collection reference and then update single fields using .update().
final CollectionReference collectionReference = FirebaseFirestore.instance.collection('users');
await collectionReference.doc(user.uid).collection('yourNewCollection').doc('yourDocumentInsideNestedCollection').update({
'singleField': 'whatever you want,
});
Same code using "where"
collectionReference.doc(user.uid).collection('yourNewCollection').doc().where('singleField', isEqualTo: yourValue).update({
'singleField': 'whatever you want,
});
I want to query two Firestore collections, but only want to return the data of the collection which returns a result quicker then the other one.
My approach was to use 2 streams and to wait till one of them gives back data, which I then can return.
I used the onData Parameter, but the compiler never jumps in the onData Method even if there is data in the collections which matches the query.
var someId;
CollectionReference collection1 = _db.collection('collection1');
CollectionReference collection2 = _db.collection('collection2');
Query collection1Query = collection1.where('users', arrayContains: uid);
Query collection2Query = collection2.where('users', arrayContains: uid);
var resultStream1 = collection1Query.snapshots().listen((doc) {});
var resultStream2 = collection2Query.snapshots().listen((doc) {});
while(someId == null){
resultStream1.onData((data) {
var someId = data.docs.first.id;
});
resultStream2.onData((data) async {
var someId = data.docs.first.id;
await doOneMoreThing(someId);
});
}
resultStream1.cancel();
resultStream2.cancel();
return someId;
For that check the async package there is a class named StreamGroup, and you can combine N streams on that class writing:
final newStream = StreamGroup.merge([streamOne, streamTwo]);
Then you can use that newStream as a common stream.
For an instance, let's suppose I am calling a function in initState which gets some documents from the Firebase collection. I am iterating on those documents using async forEach and need to perform an await operation to get some data from another collection from firebase and saving them in a list then returning after the forEach is finished. But the returned list is empty as the second await function completes after return statement. How do I handle this? I have a hard time understanding Asynchronous programming so please a detailed explanation will be highly appreciated.
The code here is just an example code showing an actual scenario.
Future getList() async {
//These are just example refs and in the actual refs it does print actual values
var collectionOneRef = Firestore.instance.collection('Collection1');
var collectionTwoRef = Firestore.instance.collection('Collection2');
List<Map<String, dynamic>> properties = [];
QuerySnapshot querySnapshot = await collectionOneRef
.getDocuments()
.then((query) {
query.documents.forEach((colOneDoc) async {
var colOneID = colOneDoc.documentID;
await collectionTwoRef.document(colOneID).get().then((colTwoDoc) {
Map<String, dynamic> someMap = {
"field1": colTwoDoc['field1'],
"field2": colTwoDoc['field2']
};
properties.add(someMap);
properties.sort((p1, p2) {
//sorting based on two properties
var r = p1["field1"].compareTo(p2["field1"]);
if (r != 0) return r;
return p1["field2"].compareTo(p2["field2"]);
});
print(properties); //this prints actual data in it
));
});
});
});
print(properties); //This prints a blank list as [] and obviously returns blank list
return properties;
}
And now when I call this function in an initState of a stateful Widget and display it somewhere, it display a blank list. BUT, after I "Hot Reload", then it displays. I want to get the data and display it without hot reloading it. Thanks in advance
Sounds like you need to await the whole function itself in the block of code where you are calling getList().
you are async'ing and awaiting inside the function, and that looks fine which is why you are getting results when hot reloading. But it might be the case that you're not actually awaiting the entire function itself in the code that is calling this. Please Check.
I have a Firebase Firestore database as you can see in the picture. I'd like to get every document from the questions collection with their fields.
I found the following solution on the internet:
//in this list we store the data
List adatok = [];
Future<void> getData(){
databaseReference.getDocuments().then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f){
data = f.data;
adatok.add(data);
print(adatok.length);
print(adatok);
}
);
}
);
}
My problem is that when I call this function (in an onPresed() function of a button), for the first time of pressing the adatok list is empty. But when I press the button for the second time, it contains every data which I wanted to acquire.
Did I do something wrong? Is there another way to get the data?
The layout of the database
Use the code below:
List adatok = [];
Future<void> getData() async{
QuerySnapshot snapshot = await databaseReference.getDocuments();
snapshot.documents.forEach((f){
data = f.data;
adatok.add(data);
print(adatok.length);
print(adatok);
}
}
By this way, The program waits until it gets all the documents.
and then adds it to your snapshots.
Also you have to change your onPressed() function to async/await like the following:
onPressed:() async{
await getData();
/...
}
So the program waits until getData() finishes.
I'm a total newbie to Flutter and I'm trying to add some data from Cloud Firestore to a list in Flutter, but having issues. I try to add the element, but after executing, the element isn't there. It's not throwing an exception or anything either. Maybe someone else has some advice for me!
I have tried changing the type of list (capture the doc from Cloud Firestore instead of data within the doc, same issue), I have also debugPrinted the data I am trying to store to make sure it exists, it does. I have done basic troubleshooting like running flutter clean as well. I am on the latest version of Flutter.
Firestore db = firestore();
List<String> getString() {
var dataList = new List<String>();
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
});
debugPrint(dataList.first);
return dataList;
The list is empty, though it should contain the "First name" field on this Cloud Firestore doc. Again, verified the data does exist and prints when calling debugPrint.
The db.collection('Users').get() is a async function, so debugPrint(dataList.first); executes before of the end of your firestores get, because that your array returns empty.
If you try it:
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
});
You will see your data.
You can use await to wait the call finishes, so you must return a Future and use async key word on function declaration. This is a conceipt that you must know of flutter async functions (Async Flutter). So, the code below can solve your problem.
Firestore db = firestore();
Future <List<String>> getString() async {
var dataList = new List<String>();
var result = await db.collection('Users').get();
result.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
return dataList;
}