Flutter firestore fetch documents with a condition on a map field - firebase

So I have a document in this form:
Field:
NestedField: Value
How can I use the where() method in Flutter to fetch all documents that satisfy a condition on NestedField? I.e:
Firestore.collection("forms").where("Field.NestedField",isEqualsTo: "Op1").getDocuments(). // This returns null

Alright, so this was caused by another error with no relation to Firestore directly. The Field.NesterForm approach works and it returns the data. Since Firestore uses JSON then it's the correct way to access nested fields this way. The code I was testing with was:
List<DocumentSnapshot> docs;
await Firestore.instance.collection('form')
..where("FirstForm.Operator",isEqualTo: _filter.text)
.getDocuments().then((query) {
docs = query.documents;
});
print("DOCS: $docs");
The .. before the where() that I didn't see at first caused the awaitto not actually wait for the return so the print of my docs variable always returned null. Once I removed one dot, it works fine now and the equals is working.

Related

Flutter Firebase local change doesn't update listener stream

I'm relying on Firebase Firestore offline capabilities, so I'm not using await on my queries as stated on the Access Data Offline Firebase doc. I'm expecting that when I write something I'll get an immediate reflection on my read stream, however, I'm only getting an update when the server/remote has been updated. Basically:
Update something in the DB. Note, I'm not using await
_db.doc(parentDoc).collection(DocInnerCollection).doc(childDoc).update({
"name": value,
});
I expect my listeners to be updated immediately. Note I've set the includeMetadataChanges to true as stated in the above doc.
_db.doc(parentDoc)
.collection(DocInnerCollection)
.orderBy('start_date', 'desc')
.limitToLast(1)
.snapshots(includeMetadataChanges: true)
.map((snapshot) {
print(snapshot.metadata.isFromCache)
});
However, I get no such update and instead I only get an update when the server has been updated.
You're requesting only one document with .limitToLast(1), yet are not providing a sort order for your query. This essentially means that you'll get a random document from your collection, and the chances of that being the newly updated document are close to zero.
If you want the latest (not just last) document, you need some ordering criteria to determine what latest means. Typically you'd do this by:
Adding a lastUpdated field to your documents, and setting that to firebase.firestore.FieldValue.serverTimestamp().
Ordering your query on that timestamp with orderBy('lastUpdated', 'desc').
And then limiting to the first result with limit(1).

Firestore realtime listener detect the type of change for a single doc only

I saw on the Firestore documentation for realtime listener that we can view changes between snapshots and see whether each document is added, removed or modified.
I am wondering if it is possible to see the type of changes if I am only attaching onSnapshot to a single document?
I tried to run the docChanges() method on the single doc listener:
db.collection("matching").doc("user1").onSnapshot(async doc => {
doc.docChanges().forEach(function(change) {
if (change.type === "added") {
console.log("added: " + change.doc.data());
}
})
})
But it produced an error of :
Uncaught (in promise) TypeError: doc.docChanges is not a function
I think I simply cannot run docChanges() on a single doc listener. In that case, how to view changes for a single firestore doc realtime listener then?
No, the API will not indicate to you what data or fields changes between snapshots. You just get a callback every time something changed anywhere in the document. You have to compare the previous and current snapshot to figure that out for yourself.
This docChanges() method is applicable on listeners being run on the collection rather than documents, so if you want to check changes between the snapshot put the docChanges() after the collection reference.

The method 'updateData' isn't defined for the type 'Query'. Futter, Cloud Firestore

I would like to update data in a document based on a query. Here is my code:
Firestore.instance
.collection("Categories")
.where("userEmail", isEqualTo: "${user?.email}")
.updateData({"category_budget_remaining": _remainingCategoryBudget2})
.then((value){});
I am getting the following error: The method 'updateData' isn't defined for the type 'Query'
after where() you need to get documents then do updating
where(...).getDocuments().then((val)=>
val.documents.forEach((doc)=> {
doc.reference.updateData({...})
});
});
this code selects and updates all documents that goes true for where condition. If you want do it just for 1 document then just add .limitTo(1) before getDocuments().
As you can see from the API documentation, where() returns a Query, and Query doesn't have a method called updateData(). So, what you're seeing here is no surprise.
Firestore doesn't offer a way to bulk update documents like a SQL "update where" command. What you will have to do is execute the query for the documents to change, iterate the documents in the result set, and update each one individually. Yes, it requires a document read for every document to change, and no, there are no alternatives for this.
For me worked by changing
.updateData
to just
.update
My answer is similar to #Habib Mahamadi but this is the correct and only way to acheive this functionality, my answer is little differente as firebase has changed names of functions.
Firestore.instance
.collection("Categories")
.where("userEmail", isEqualTo: "${user?.email}")
.then((value)=> value.docs.forEach((element){element.reference.update({"" : ""})});

Flutter & Firebase: How to populate an array and then later, return all the contents

I have been trying to get arrays working in Firebase, and I am aware that there are a lot of references and discussions about this online, and I have read through all of these and none of it works.
First off, the Firebase side. The structure containing the array and two example strings inside it:
Firebase Structure
collection -> document -> fields
userData profileImages URLs (array)
: https://firebasestorage.googleapis.com/v0/b/app-138804.appspot.com/o/jRwscYWLs1DySLMz7jn5Yo2%2Fprofile%2Fimage_picker4459623138678.jpg?alt=media&token=ec1043b-0120-be3c-8e142417
: https://firebasestorage.googleapis.com/v0/b/app-138804.appspot.com/o/jRwscYWLs3872yhdjn5Yo2%2Fprofile%2Fimage_picker445929873mfd38678.jpg?alt=media&token=ec3213b-0120-be9c-8e112632
The first issue I am facing is writing to this array in the database:
Firestore.instance.collection('userData').document('profileImages').updateData({
'URLs': _uploadedFileURL,
});
Whenever I add data to this array, it just overwrites the existing data. I need to be able to keep all the existing data intact and simply add the current new line to the array.
Once this is working, I then need to be able to return all of the strings in this array without needing to know how many of them there will be.
For this part, I basically have nothing at this point. I could show some of the things I have tried based on suggestions from other articles on this, but none of it is even close to working correctly.
im assuming that _uploadedFileURL is a String, and you are updating the property URLs, that's why your data gets overwritten, because you are changing the URLs value to a single string which is _uploadedFileURL. to solve this issue, simply get the current data inside profileImages before commiting the update. like so
final DocumentSnapshot currentData = await Firestore.instance.collection('userData').document('profileImages').get();
Firestore.instance.collection('userData').document('profileImages').updateData({
'URLs': [
...currentData.data['URLs'],
_uploadedFileURL
],
});
and for the second part of your question, all you need is to query for the profileImages
Future<List<String>> _getProfileImages() {
final document = Firestore.instance.collection('userData').document('profileImages').get();
return document.data['profileImages]
}
the result of the get method will be a DocumentSnapshot, and inside the data property will access the profileImages which is a List<String>.
Ok guys and girls I have worked this out. Part 1: appending data to an array in Firebase.
Firestore.instance.collection('userData').document('profileImages').updateDataupdateData({
'URLs':FieldValue.arrayUnion([_uploadedFileURL]),
});
Where _uploadedFileURL is basically a string, for these purposes. Now I have read that arrayUnion, which is super groovy, is only available in Cloud Firestore, and not the Realtime Database. I use Cloud Firestore so it works for me but if you are having issues this might be why.
Now what is extra groovy about Cloud Firestore is that you can similarly remove an element from the array using:
Firestore.instance.collection('userData').document('profileImages').updateDataupdateData({
'URLs':FieldValue.arrayRemove([_uploadedFileURL]),
});
So how to get this data back out again. A simple way I have found to get that data and chuck it into a local array is like so:
List imageURLlist = [];
DocumentReference document = Firestore.instance.collection('userData').document('profileImages');
DocumentSnapshot snapshot = await document.get();
setState(() {
imageURLlist = snapshot.data['URLs'];
});
From here at least you have the data, can add to it, can remove from it and this can be a platform for you to figure out what you want to do with it.

What is the difference between getDocuments() and snapshots() in Firestore?

I am a little confused about the difference between these two. My understanding is that getDocuments is a type of Future and seems to get the entire documents according to the query. while snapshots, on the other hand, is a type of Stream and, correct me if I'm wrong, I think it represents the results of the query? I need a more specific explanation of this issue. I will include some code snippets as an example for more clarification
getDocuments()
getUserById(String userId) async {
return await _firestore.collection("users").where("userId", isEqualTo: userId).getDocuments();
}
snapshots()
getUserById(String userId) async {
return await _firestore.collection("users").where("userId", isEqualTo: userId).snapshots();
}
So what's the difference?
When you call getDocuments(), the Firestore client gets the documents matching the query from the server once. Since this may take some time it returns a Future<QuerySnapshot>.
When you call snapshots() the Firestore client gets the documents, and then keeps watching the database on the server for changes that affect your query. So if document is written in the users collection that affects your query, your code gets called again. So this returns a stream of QuerySnapshot.
In both cases the results for the entire query are in the QuerySnapshot object.
I highly recommend reading the Firestore documentation on getting data once and on listening realtime updates. While they don't contain Flutter examples, the explanation in there applies equally to the Flutter libraries.
getDocuments():
It's used to provide data once. Cloud Firestore contains collections and inside these collections, you have documents that may contain subcollections or fields mapped to a value. To retrieve any of the doc fields to used it in widget this is used.
snapshots():
It will be called on every data change in your document query. For this StreamBuilder must be used to fetch fields as modified.
In short, it will do the job of setState() where it gives you the response for every modification so that UI can be updated.

Resources