How to update value within firestore query using dart - flutter

Here is a query that returns some data from the firestore database.
After getting the docs, I want to update a specific key(distance_away) value before returning the final List
return db
.collection(Global.marketplaceRef)
.where("point.geohash", isGreaterThanOrEqualTo: range.lower)
.where("point.geohash", isLessThanOrEqualTo: range.upper)
.limit(20)
.snapshots()
.map((list) => (list.docs
.map((e) {
// Update value here before returning doc.
// This doesn't seem to do that
return (e.data().update('distance_away', (value) => 100.toString())).toList();
})
).toList()
);
I want to alter the distance_away value of each doc.

You need to access the reference object not .data() try something like this
.map((list) => (list.docs
.map((e) {
return (e.reference.update({'distance_away': ['100']}
).toList()

Related

How to correctly fetch data from firebase using where clauses and custom filters from firebase using flutter

Im trying to fetch data using
Stream<List<User>> getUsers(User user) {
return _firebaseFirestore
.collection('users')
// .where('interestedIn', isEqualTo: _selectInterest(user))
.snapshots()
.map((snap) {
return snap.docs.map((doc) => User.fromSnapshot(doc)).toList();
});
}
The filter used in the where clause is as follows
_selectInterest(User user) {
if (user.interestPreference == null) {
return ['HIRING', 'WORK'];
}
return user.interestPreference;
}
In firebase I store interestPreference as an Array with 'HIRING' as the only element in the current user's data, when I try to fetch users with 'HIRING' in their interestedIn which is a string I dont get any data. But when I hardcode the where clause as
.where('interestedIn', isEqualTo: 'HIRING')
I get the data, Can anyone help me solve my dilemma?
From that last query, it sounds like the interestedIn field in your database is a single string value, like interestedIn: "HIRING".
Your current query returns documents where interestedIn is an array with exactly the two values you specify, so interestedIn: ['HIRING', 'WORK']
If you want to return all documents where interested in is either "HIRING" or "WORK", you can use an IN condition:
.where('interestedIn', whereIn: ['HIRING', 'WORK'])
Or with your helper function:
.where('interestedIn', whereIn: _selectInterest(user))

How to update nested field inside a document in Firestore Flutter

I am working with Flutter and Cloud Firestore, and I'm stuck on the point where I need to update a nested field inside my collection -> document. Below is the screenshot of my firestore collection structure. I know how to do basic update method like FirebaseFirestore.instance.collection('collection_path').doc('data').update(...), however, in my case, the document structure is a bit complex so I need some help in case of performing update method.
Here, I need to change (on a button press) the field status to true/false under strategies array.
I have the access to strategyName field so that I could get that item in the strategies array and change it...
What I tried:
ElevatedButton(
onPressed: () async {
// status=true
final value = await FirebaseFirestore.instance
.collection(widget.symbol + '_Live')
.doc("data")
.get();
final data = value.data();
final strategies =
data!['strategies'] as List<Map<String, dynamic>>;
final strategy = strategies.firstWhere(
(item) => item['strategyName'] == strategyName,
orElse: () => Map());
strategy['status'] = true;
await FirebaseFirestore.instance
.collection(widget.symbol + '_Live')
.doc("data")
.update(data);
},
child: Text("Start"),
),
Obviously, this won't work because here I'm just changing it locally, i.e. not using the Firestore update method to actually change the field. But I'm confused as to how to update that nested field.
Any leads/help would be really appreciated!
EDIT:
I did as you told and edited my answer, but now the error is gone but nothing seems to work, i.e. in firestore, there is no change in the data doc.
ERROR: Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<Map<String, dynamic>>' in type cast
You need to update value map locally, then store it back:
final value = await FirebaseFirestore.instance
.collection(symbol + '_Live')
.doc("data")
.get();
final data = value.data();
final strategies =
data!['strategies'].map((item) => item as Map<String, dynamic>).toList();
final strategy = strategies.firstWhere(
(item) => item['strategyName'] == strategyName,
orElse: () => Map());
strategy['status'] = toggleValue;
await FirebaseFirestore.instance
.collection(symbol + '_Live')
.doc("data")
.update(data);
The point is to store the entire value.
EDIT by Author : Just needed to map each element to Map<String, dynamic>.

Firebase | Problem with collectionGroup query

I'm trying to use a collectionGroup query to fetch the data from a specified farm, using firebase-functions. Here's my code:
app.get('/api/intervals/:farm_id', async (req, res) => {
const farmId = req.params.farm_id;
try {
const querySnapshot = await db
.collectionGroup('farms')
.where("id", "==", farmId)
.get();
const farmData = [];
querySnapshot.forEach((doc) => {
farmData.push(doc.data());
console.log(doc.id, ' => ', doc.data());
});
return res.status(200).send(farmData);
} catch (error) {
console.log(error);
return res.status(500).send(error);
}
});
There is definitely a farm in the database with the supplied code. For example, if I change the code in try to the below, I get the data as expected:
const farmRef = db
.collection('accounts')
.doc('lz8V32bjQGa9x1oecUu9')
.collection('farms')
.doc(farmId);
let farm = await farmRef.get();
let farmData = farm.data();
return res.status(200).send(farmData);
But I want to use a collectionGroup so I don't have to specify the parent account ID also.
What am I doing wrong here? Thanks in advance!
According to my understanding this is related with the fact that in your working code you are assigning directly from DocumentSnapshot, while in example in of the issue you are iterating over QuerySnapshot returned by get from query.
In documentation we can find that documents in QuerySnapshot are in array property docs.
So I think you should change forEach loop accordingly:
querySnapshot.docs.forEach((doc) => {
farmData.push(doc.data());
console.log(doc.id, ' => ', doc.data());
});
I have worked out that the problem was that my code was looking for a field named id. My query works if I manually add a field called ID as highlighted below:

Firestore Query For Non Existing Key of a Map

I am aware that it is possible to do null queries, for a field that has a null value.
In my case, I have documents containing a Map and I want to query all these that do not contain a given key. In my case, this key is a string formatted date.
I did try to do this with the following code but it is not working. Instead it fetches all documents:
Stream<List<Employee>> availableEmployeesForGivenDesignation(
String designation, DateTime date) {
// in firstore the keys of a map are Strings, thus I have to change DateTime to String
var formatter = new DateFormat('yyyy-MM-dd');
String formatedDate = formatter.format(date);
//todo check why I am getting with null also the false employees
return _employeeCollection
.where('designations', arrayContains: designation)
.where('busy_map.$formatedDate', isEqualTo: null)
.snapshots()
.map((snapshot) {
return snapshot.documents
.map((doc) => Employee.fromEntity(EmployeeEntity.fromSnapshot(doc)))
.toList();
});
}
To my understanding, I am querying on a map key and for some documents this key (date) does not always exist. This is why I tried to fetch these documents that return null on this case.
Database structure:
Found the answer...
I had to change the following line to:
.where('busy_map.$formatedDate', isNull: true)
So instead of using isEqual use isNull

How can I deal with the firestore whereIn limit of 10 when using a switch stream?

I have the following Stream that takes in a Stream of a group and returns a Stream of it's members. I use a switchmap to get the members from the group snapshot.
But I have the following problem. I use a where query with a whereIn filter. But the problem is that the whereIn can only take in a list that have 10 entries or less according to the firestore documentation
Limitations Note the following limitations for in and
array-contains-any:
in and array-contains-any support up to 10 comparison values.
https://firebase.google.com/docs/firestore/query-data/queries#limitations
So I am having some difficulties with dealing with that in this senario.
Stream<List<UserModel>> groupMembersStream(Stream<GroupModel> groupStream) {
return groupStream.switchMap(
(value) => _fireStore
.collection(APIRoutes.users)
.where(FieldPath.documentId, whereIn: value.members.keys.toList(growable: false))
.snapshots()
.map((snapshot) =>
snapshot.documents.map((document) => UserModel.fromFirestore(document)).toList(growable: false)),
);
}
Because I need the group member id's to start with so I need a switchMap. So I cannot just simply split up the group members list and then do a seperate query for each chunk of 10 id's.
So how do I deal with this?
Another possible solution (using Quiver & RxDart packages)
Future<List<QueryDocumentSnapshot>> listItems(
List<dynamic> itemIds) async {
final chunks = partition(itemIds, 10);
final querySnapshots = await Future.wait(chunks.map((chunk) {
Query itemsQuery = FirebaseFirestore.instance
.collection('collection')
.where("id", whereIn: chunk);
return itemsQuery.get();
}).toList());
return querySnapshots == null
? []
: await Stream.fromIterable(querySnapshots)
.flatMap((qs) => Stream.fromIterable(qs.docs))
.toList();
}
Looks more generic this way (you can even factor out the query using a supplier function).
I ended up doing it like this
Stream<List<UserModel>> groupMembersStream(Stream<GroupModel> groupStream) {
return groupStream.switchMap(
(value) => _chunckSizeGroupMembersStream(value),
);
}
Stream<List<UserModel>> _chunckSizeGroupMembersStream(GroupModel group) {
final List<List<String>> memberChunks = chunkSizeCollection(group.members.keys.toList(growable: false), 10);
List<Stream<List<UserModel>>> streams = List<Stream<List<UserModel>>>();
memberChunks.forEach((chunck) => streams.add(_fireStore
.collection(APIRoutes.userCollection)
.where(FieldPath.documentId, whereIn: chunck)
.snapshots()
.map((snapshot) =>
snapshot.documents.map((document) => UserModel.fromFirestore(document)).toList(growable: false))));
return ZipStream(streams, (value) => value.last);
}