How to update collection documents in firebase in flutter? - firebase

I want to update a document field and I've tried the following code but it doesn't update.
can anyone give me a solution, please?
My Code:
var snapshots = _firestore
.collection('profile')
.document(currentUserID)
.collection('posts')
.snapshots();
await snapshots.forEach((snapshot) async {
List<DocumentSnapshot> documents = snapshot.documents;
for (var document in documents) {
await document.data.update(
'writer',
(name) {
name = this.name;
return name;
},
);
print(document.data['writer']);
//it prints the updated data here but when i look to firebase database
//nothing updates !
}
});

For cases like this, I always recommend following the exact types in the documentation, to see what options are available. For example, a DocumentSnapshot object's data property is a Map<String, dynamic>. To when you call update() on that, you're just updating an in-memory representation of the document, and not actually updating the data in the database.
To update the document in the database, you need to call the DocumentReference.updateData method. And to get from the DocumentSnapshot to a DocumentReference, you call the DocumentSnapshot.reference property.
So something like:
document.reference.updateData(<String, dynamic>{
name: this.name
});
Unrelated to this, your code looks a bit non-idiomatic. I'd recommend using getDocuments instead of snapshots(), as the latter will likely result in an endless loop.
var snapshots = _firestore
.collection('profile')
.document(currentUserID)
.collection('posts')
.getDocuments();
await snapshots.forEach((document) async {
document.reference.updateData(<String, dynamic>{
name: this.name
});
})
The difference here is that getDocuments() reads the data once, and returns it, while snapshots() will start observing the documents, and pass them to us whenever there's a change (including when you update the name).

Update 2021:
Lot of things have changed in the API, for example, Firestore is replaced by FirebaseFirestore, doc is in, etc.
Update a document
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('some_id') // <-- Doc ID where data should be updated.
.update({'key' : 'value'}) // <-- Updated data
.then((_) => print('Updated'))
.catchError((error) => print('Update failed: $error'));
Update nested value in a document:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('some_id') // <-- Doc ID where data should be updated.
.update({'key.foo.bar' : 'nested_value'}) // <-- Nested value
.then((_) => print('Updated'))
.catchError((error) => print('Update failed: $error'));

To update some fields of a document without overwriting the entire document, use the following language-specific update() methods:
final washingtonRef = FirebaseFirestore.instance.collection("cites").doc("DC");
washingtonRef.update({"capital": true}).then(
(value) => print("DocumentSnapshot successfully updated!"),
onError: (e) => print("Error updating document $e"));
Server Timestamp
You can set a field in your document to a server timestamp which tracks when the server receives the update.
final docRef = FirebaseFirestore.instance.collection("objects").doc("some-id");
final updates = <String, dynamic>{
"timestamp": FieldValue.serverTimestamp(),
};
docRef.update(updates).then(
(value) => print("DocumentSnapshot successfully updated!"),
onError: (e) => print("Error updating document $e"));
Update fields in nested objects
If your document contains nested objects, you can use "dot notation" to reference nested fields within the document when you call update():
// Assume the document contains:
// {
// name: "Frank",
// favorites: { food: "Pizza", color: "Blue", subject: "recess" }
// age: 12
// }
FirebaseFirestore.instance
.collection("users")
.doc("frank")
.update({"age": 13, "favorites.color": "Red"});
Update elements in an array
If your document contains an array field, you can use arrayUnion() and arrayRemove() to add and remove elements. arrayUnion() adds elements to an array but only elements not already present. arrayRemove() removes all instances of each given element.
final washingtonRef = FirebaseFirestore.instance.collection("cities").doc("DC");
// Atomically add a new region to the "regions" array field.
washingtonRef.update({
"regions": FieldValue.arrayUnion(["greater_virginia"]),
});
// Atomically remove a region from the "regions" array field.
washingtonRef.update({
"regions": FieldValue.arrayRemove(["east_coast"]),
});
Increment a numeric value
You can increment or decrement a numeric field value as shown in the following example. An increment operation increases or decreases the current value of a field by the given amount.
var washingtonRef = FirebaseFirestore.instance.collection('cities').doc('DC');
// Atomically increment the population of the city by 50.
washingtonRef.update(
{"population": FieldValue.increment(50)},
);

Related

Delete document by content of a field in Firestore (flutter)

I have a subcollection 'favourites' under my 'users' collection and I would like to delete any of its documents by checking that the field 'prod_id' is isEqualTo prodId
THis is how I create the subcollection & document when a user taps the favourite button:
_usersCollectionReference
.doc(userId)
.collection('favourites')
.add({'prod_id': prodId});
Now, I want to be able to delete this document
To delete any document you must know it's ID or have a DocumentReference to it. For that you just need to know the userId and fetch documents where prod_id is equal to the product IDs you want to delete.
FirebaseFirestore.instance
.collection('users')
.doc(userId)
.collection('favourites')
.where('prod_id', whereIn: [...])
.get()
.then((snapshot) {
// ...
});
snapshots is a QuerySnapshot and has a property docs which is an array of QueryDocumentSnapshot. You can loop through that array and delete each document by accessing their reference.
For example, deleting first document in that would be:
snapshot.docs[0].reference.delete()
You can also do a check on the collection itself with where logic
const snapshot = await contentRef.where('favouritething','==', favoriteId)
.get().then((querySnapshot)=>{
// return query snapshot
return querySnapshot.docs
// .map(doc => doc.data());
})
you can do this very easily and return a result
let contentArray: any[] = []
// call the content collection
const contentRef = db.collection('favourites');
// snapshot data on get
const snapshot = await contentRef.get().then((querySnapshot)=>{
// return query snapshot
return querySnapshot.docs
// .map(doc => doc.data());
})
// this statement can be joined to the other
// loop through each snapshot as document
snapshot.forEach((doc)=>{
// create new constant as document of each loop
const newdoc = doc.data()
// push new constant to content array
if(newdoc.YOURTHING==='your thing you want to look for'){
const deleteDocId=newdoc?.id
db.collection('favourites').doc(`${deleteDocId}`).delete().then((result)=>{
return result
})
}
})
})
You can read about why and how here:
https://firebase.google.com/docs/firestore/manage-data/delete-data

Flutter - Deleting a document in Firestore leads to the deletion of a random document and not the selected one

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();

How can I check if a document id that already exists in Firestore using Flutter [duplicate]

I want to add data into the firestore database if the document ID doesn't already exists.
What I've tried so far:
// varuId == the ID that is set to the document when created
var firestore = Firestore.instance;
if (firestore.collection("posts").document().documentID == varuId) {
return AlertDialog(
content: Text("Object already exist"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () {}
)
],
);
} else {
Navigator.of(context).pop();
//Adds data to the function creating the document
crudObj.addData({
'Vara': this.vara,
'Utgångsdatum': this.bastFore,
}, this.varuId).catchError((e) {
print(e);
});
}
The goal is to check all the documents ID in the database and see in any matches with the "varuId" variable. If it matches, the document won't be created. If it doesn't match, It should create a new document
You can use the get() method to get the Snapshot of the document and use the exists property on the snapshot to check whether the document exists or not.
An example:
final snapShot = await FirebaseFirestore.instance
.collection('posts')
.doc(docId) // varuId in your case
.get();
if (snapShot == null || !snapShot.exists) {
// Document with id == varuId doesn't exist.
// You can add data to Firebase Firestore here
}
Use the exists method on the snapshot:
final snapShot = await FirebaseFirestore.instance.collection('posts').doc(varuId).get();
if (snapShot.exists){
// Document already exists
}
else{
// Document doesn't exist
}
To check if document exists in Firestore. Trick is to use .exists method
FirebaseFirestore.instance.doc('collection/$docId').get().then((onValue){
onValue.exists ? // exists : // does not exist ;
});
I know this is a flutter firestore topic but I just want to share my answer.
I am using Vue and I am also doing a validation if the id is already taken on firestore.
This is my solution as of firebase version 9.8.2
const load = async() => {
try {
const listRef = doc(db, 'list', watchLink.value);
let listSnapShot = await getDoc(listRef);
if(listSnapShot._document == null) {
await setDoc(doc(db, 'list', watchLink.value), {
listName: NameofTheList.value
});
throw Error('New list added');
}
else {
throw Error('List already Exist');
}
} catch (error) {
console.log(error.message);
}
}
load();
The watchLink.value is the ID that you want to check
Edit:
if you console.log(listSnapShot), the _document will be set to null if the id does not exist on firestore. See screenshot below
If it does not exist
If ID already exists
QuerySnapshot qs = await Firestore.instance.collection('posts').getDocuments();
qs.documents.forEach((DocumentSnapshot snap) {
snap.documentID == varuId;
});
getDocuments() fetches the documents for this query, you need to use that instead of document() which returns a DocumentReference with the provided path.
Querying firestore is async. You need to await its result, otherwise you will get Future, in this example Future<QuerySnapshot>. Later on, I'm getting DocumentSnapshots from List<DocumentSnapshots> (qs.documents), and for each snapshot, I check their documentID with the varuId.
So the steps are, querying the firestore, await its result, loop over the results. Maybe you can call setState() on a variable like isIdMatched, and then use that in your if-else statement.
Edit: #Doug Stevenson is right, this method is costly, slow and probably eat up the battery because we're fetching all the documents to check documentId. Maybe you can try this:
DocumentReference qs =
Firestore.instance.collection('posts').document(varuId);
DocumentSnapshot snap = await qs.get();
print(snap.data == null ? 'notexists' : 'we have this doc')
The reason I'm doing null check on the data is, even if you put random strings inside document() method, it returns a document reference with that id.

How to get, set, update and delete data from the cloud firestore in Flutter?

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.

Flutter firestore - Check if document ID already exists

I want to add data into the firestore database if the document ID doesn't already exists.
What I've tried so far:
// varuId == the ID that is set to the document when created
var firestore = Firestore.instance;
if (firestore.collection("posts").document().documentID == varuId) {
return AlertDialog(
content: Text("Object already exist"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () {}
)
],
);
} else {
Navigator.of(context).pop();
//Adds data to the function creating the document
crudObj.addData({
'Vara': this.vara,
'Utgångsdatum': this.bastFore,
}, this.varuId).catchError((e) {
print(e);
});
}
The goal is to check all the documents ID in the database and see in any matches with the "varuId" variable. If it matches, the document won't be created. If it doesn't match, It should create a new document
You can use the get() method to get the Snapshot of the document and use the exists property on the snapshot to check whether the document exists or not.
An example:
final snapShot = await FirebaseFirestore.instance
.collection('posts')
.doc(docId) // varuId in your case
.get();
if (snapShot == null || !snapShot.exists) {
// Document with id == varuId doesn't exist.
// You can add data to Firebase Firestore here
}
Use the exists method on the snapshot:
final snapShot = await FirebaseFirestore.instance.collection('posts').doc(varuId).get();
if (snapShot.exists){
// Document already exists
}
else{
// Document doesn't exist
}
To check if document exists in Firestore. Trick is to use .exists method
FirebaseFirestore.instance.doc('collection/$docId').get().then((onValue){
onValue.exists ? // exists : // does not exist ;
});
I know this is a flutter firestore topic but I just want to share my answer.
I am using Vue and I am also doing a validation if the id is already taken on firestore.
This is my solution as of firebase version 9.8.2
const load = async() => {
try {
const listRef = doc(db, 'list', watchLink.value);
let listSnapShot = await getDoc(listRef);
if(listSnapShot._document == null) {
await setDoc(doc(db, 'list', watchLink.value), {
listName: NameofTheList.value
});
throw Error('New list added');
}
else {
throw Error('List already Exist');
}
} catch (error) {
console.log(error.message);
}
}
load();
The watchLink.value is the ID that you want to check
Edit:
if you console.log(listSnapShot), the _document will be set to null if the id does not exist on firestore. See screenshot below
If it does not exist
If ID already exists
QuerySnapshot qs = await Firestore.instance.collection('posts').getDocuments();
qs.documents.forEach((DocumentSnapshot snap) {
snap.documentID == varuId;
});
getDocuments() fetches the documents for this query, you need to use that instead of document() which returns a DocumentReference with the provided path.
Querying firestore is async. You need to await its result, otherwise you will get Future, in this example Future<QuerySnapshot>. Later on, I'm getting DocumentSnapshots from List<DocumentSnapshots> (qs.documents), and for each snapshot, I check their documentID with the varuId.
So the steps are, querying the firestore, await its result, loop over the results. Maybe you can call setState() on a variable like isIdMatched, and then use that in your if-else statement.
Edit: #Doug Stevenson is right, this method is costly, slow and probably eat up the battery because we're fetching all the documents to check documentId. Maybe you can try this:
DocumentReference qs =
Firestore.instance.collection('posts').document(varuId);
DocumentSnapshot snap = await qs.get();
print(snap.data == null ? 'notexists' : 'we have this doc')
The reason I'm doing null check on the data is, even if you put random strings inside document() method, it returns a document reference with that id.

Resources