I am trying to update a field in the last document in the Firestore collection. My updating method is below:
updateHours() {
return usersRef.doc(firebaseAuth.currentUser!.uid).collection('posts')
.orderBy('datePublished', descending: true)
.limit(1).get().then((querySnapshot) {
return querySnapshot.docs.map((e) {
usersRef
.doc(firebaseAuth.currentUser!.uid).collection('posts')
.doc(e.reference.id)
.update({"totalTime": FieldValue.increment(1)});
});
});
}
This does not work. If I use .forEach(), then all documents get updated. So, how to update only the last document field?
To be able to update the totalTime field inside the last document, please use the following lines of code:
void updateHours() async{
CollectionReference postsRef = usersRef
.doc(firebaseAuth.currentUser!.uid)
.collection('posts');
QuerySnapshot query = await postsRef.orderBy('datePublished', descending: true)
.limit(1)
.getDocuments();
query.documents.forEach((doc) {
doc.reference.updateData({"totalTime": FieldValue.increment(1)});
});
}
Don't forget that Firebase APIs are asynchronous, and you need to wait for the data until it becomes available.
Related
I am currently learning how to use Firebase and it is not too intuitive to me, yet. Basically, I would like to retrieve only the last document in my Cloud Firestore collection and save it as a Map<String, dynamic>. Here is my code:
final _firestore = FirebaseFirestore.instance;
Future<Map<String, dynamic>> fetchData() async {
Map<String, dynamic> collection;
try {
QuerySnapshot<Map<String, dynamic>> data = await _firestore
.collection('collection')
.orderBy('timestamp', descending: true)
.limit(1).get();
collection = data;
} catch (e) {
print(e);
}
return collection;
}
As of my understanding, the QuerySnapshot returns multiple DocumentSnapshots. I thought if I limit it to 1 and sort it by the generated timestamp I would be able to only get the last added document. However, I am unable to save my QuerySnapshot as a Map<String, dynamic> and could not find a way to make it work.
Many thanks in advance to those taking their time answering to this!
Try this:
collection = data.docs.first.data() as Map;
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,
});
The 'utenti' (users) collection contains a document for each user which in turn contains the 'salvataggi' (saves) collection to save objects within the app.
Each document inside 'salvataggi' (saves) has an automatically generated id containing a series of data (String to be precise).
Documents within 'salvataggi' (saves) can be added by saving objects created from two other collections always in FIrestore.
When, through a button, I want to delete an object from the 'salvataggi' (saves) collection, a random document is deleted and not the one corresponding to the object.
Screenshot of Firestore
Object 1
final CollectionReference _usersRef =
FirebaseFirestore.instance.collection('utenti');
final User _user = FirebaseAuth.instance.currentUser;
//Add
Future _addToSaved() {
return _usersRef.doc(_user.uid).collection('salvataggi').doc().set({
'fonte': elenco.fonte,
'title': elenco.title,
'url': elenco.urlAvv,
'imageEv': elenco.urlAvv,
'p1': elenco.iconaProspettiva1,
'p1url': elenco.urlProspettiva1,
});
}
//Remove
Future _removeFromSaved() async {
CollectionReference userSaved =
_usersRef.doc(_user.uid).collection('salvataggi');
QuerySnapshot querySnap = await userSaved.get();
querySnap.docs[0].reference.delete();
}
Object 2
final CollectionReference _usersRef =
FirebaseFirestore.instance.collection('utenti');
final User _user = FirebaseAuth.instance.currentUser;
//Add
Future _addToSaved() {
return _usersRef.doc(_user.uid).collection('salvataggi').doc().set({
'fonte': widget.single.fonte,
'title': widget.single.title,
'url': widget.single.urlAvv,
'imageEv': widget.single.imageEvAvv,
'lastupdate': widget.single.dataAggiornamento,
'p1': widget.single.iconaProspettiva1,
'p1url': widget.single.urlProspettiva1,
});
}
//Remove
Future _removeFromSaved() async {
CollectionReference userSaved =
_usersRef.doc(_user.uid).collection('salvataggi');
QuerySnapshot querySnap = await userSaved.get();
querySnap.docs[0].reference.delete();
}
What am I doing wrong? Why does this happen?
When the user saves a document try saving the id of that document with it so whenever the user unsaved the document. You can pass the id of that unsaved document to firestore.
It will look something like this
Future _removeFromSaved(String docID) async {
CollectionReference userSaved =
_usersRef.doc(_user.uid).collection('salvataggi');
await userSaved.doc(docID).delete()
}
--UPDATE--
You can save document id by calling the then method after adding the document to firestore and then updating it
Future _addToSaved()async {
await _usersRef.doc(_user.uid).collection('salvataggi').add({
'fonte': widget.single.fonte,
'title': widget.single.title,
'url': widget.single.urlAvv,
'imageEv': widget.single.imageEvAvv,
'lastupdate': widget.single.dataAggiornamento,
'p1': widget.single.iconaProspettiva1,
'p1url': widget.single.urlProspettiva1,
}).then(docRef=>await _usersRef.doc(_user.uid).collection('salvataggi').doc(docRef.id).update({'id':docRef.id}));
}
querySnap.documents.first.reference.delete();
Use this instead of querySnap.docs[0].reference.delete();
In my flutter firebase app, I am able to get the length of a user's activity document in firebase using a query snapshot. However, I want the number of documents to update in real-time without the user needing to refresh the page. Can I do that by converting the codes below using stream builder to get the real-time length and how can I do that?
this is the code am using now which works perfectly well but doesn't update in real-time.
//this is the code I want to convert to stream
//builder.
static Future<int> numActivities(String userId)
async {
QuerySnapshot userActivitiesSnapshot = await
activitiesRef
.document(userId)
.collection('userActivities')
.where('seen', isEqualTo: '')
.getDocuments();
return userActivitiesSnapshot.documents.length;
}
You need to use the docs property, which "returns a List containing DocumentSnapshot classes", as follows:
return userActivitiesSnapshot.docs.length;
To get a stream of documents, you need to use the .snapshots() method which returns a Stream of QuerySnapshot instead of the .getDocuments() (deprecated in favor of .get()) method which returns a Future of QuerySnapshot.
Then you can map Stream<Snapshot> into a stream of the length of the snapshot's documents.
Your numActivities method should look like this:
static Stream<int> numActivities(String userId) {
return activitiesRef
.document(userId)
.collection('userActivities')
.where('seen', isEqualTo: '')
.snapshots()
.map((documentSnapshot) => documentSnapshot.docs.length);
}
Using this in your use case, you need to listen to the stream and update the _activityCount variable.
_setUpactivityCount() async {
final String currentUserId =
Provider.of<UserData>(context, listen: false).currentUserId;
DatabaseService.numActivities(currentUserId).listen((activityCount) {
if (mounted) {
setState(() {
_activityCount = activityCount;
});
}
});
}
Make sure you take care of _activityCount being null in it's initial state.
I tried some code but getting an exception.
The Exception that I'm getting:
java.lang.IllegalArgumentException: Invalid document reference. Document references must have an even number of segments, but Users has 1
I searched for it, according to this, Document references must have an even number of segments like: Collection - document - Collection - document - Collection - document
Query for getting data from firestore:
String getIsNewUSer;
Firestore.instance.collection('Users').document(uid).get().then((DocumentSnapshot document){
print("document_build:$document");
setState(() {
getIsNewUSer=document['IsNewUser'];
print("getIsNewUSe:$getIsNewUSer");
});
});
Query for Updating data to the firestore:
Firestore.instance
.collection('Users')
.document(uid)
.updateData({
"IsNewUser":"1"
}).then((result){
print("new USer true");
}).catchError((onError){
print("onError");
});
These code line at I'm getting above Exception.
initState:
void initState() {
super.initState();
this.uid = '';
FirebaseAuth.instance.currentUser().then((val){
setState(() {
this.uid= val.uid;
print("uid_init: $uid");
});
});
}
Null safe code:
Get data:
var collection = FirebaseFirestore.instance.collection('collection');
var docSnapshot = await collection.doc('doc_id').get();
Map<String, dynamic>? data = docSnapshot.data();
Set data:
var collection = FirebaseFirestore.instance.collection('collection');
collection.add(someData);
Update data:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('foo_id') // <-- Doc ID where data should be updated.
.update(newData);
Delete data:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('some_id') // <-- Doc ID to be deleted.
.delete();
Replace this part in your queries:
Firestore.instance.collection('Users').document(uid)
with
Firestore.instance.document('Users/$uid')
Collection - document - Collection - document - Collection - document
Basically you already had the answer.
It is possible that FirebaseAuth.instance.currentUser() future didn't complete and populte this.uid. So this.uid == '' (empty string). So Firestore is throwing errror as you are trying to updated document at Users which is a collection.
You can validate this by printing this.uid before the update statement.
One way is to use helper method to update
Future<void> update(Map data) async {
final user = await FirebaseAuth.instance.currentUser();
return Firestore.instance.collection('Users').document(user.uid).updateData(data);
}
Then you can use helpr method as update({isNewUser: "1"}).then((r) {...})....
You can follow same approach for fetching the document as well.