I am trying to delete a field from my firebase array. I am using this code but it deletes then whole activity array.
FirebaseFirestore.instance
.collection(
widget.user.user.uid)
.doc(documentName)
.update({
"activities":
FieldValue.delete()
Instead I want to delete a specific field from 'activities' like activities[2]. How can I do to fix this?
To remove an item at a specific index you can use the arrayRemove method from FieldValue
FirebaseFirestore.instance.collection(widget.user.user.uid).doc(documentName).update({
"activities": FieldValue.arrayRemove("itemToRemove")
})
You should try using FieldValue.arrayRemove() method and then pass the value that you want to remove using this :
var val=[]; //blank list for add elements which you want to delete
val.add('$addDeletedElements');
FirebaseFirestore.instance
.collection(
widget.user.user.uid)
.doc(documentName)
.update({
"activities":
FieldValue.arrayRemove(val])
You have to tell firebase full path like this
FirebaseFirestore.instance
.collection("chat_dialog")
.doc("India")
.collection("dialog_details")
.where(['firestore_id'].contains('your_value'))
.get()
.then((value) {
print(value.docs.length);
for (var element in value.docs) {
print(element.id);
}
});
Related
I have two collections. One collection "User", who contains the user info (name...) And one collection "Post" who contains all posts of my flutter application. A post document contains many fields like a "Title", "Name" of the user. I add an option in my application to allow the user to change his name. But I must change the name in the "User" collection and in all posts it creates in the "Post" collection. How should I do it? Can anyone help me with an example?
There's nothing magical here. You'll need to get all post documents for that user with a query, and then update them one by one or in batches.
I'd also recommend checking out: What is the fastest way to write a lot of documents to Firestore?
Depends on which firestore are you using.
For Cloud Firestore:
You can update like this way, this is the case where you are updating just one field of your user.
final docRef = db.collection("users").doc("user_id");
final updates = <String, dynamic>{
"timestamp": FieldValue.serverTimestamp(),
};
docRef.update(updates).then(
(value) => print("DocumentSnapshot successfully updated!"),
onError: (e) => print("Error updating document $e"));
For updating a nested field:
// Assume the document contains:
// {
// Title: "Post Tittle",
// Name: "Post Name"
// user: { id: "1", name: "User Name" }
// date: "2022-12-08"
// }
db
.collection("posts")
.doc("post_id")
.update({"date": "2022-13-08", "user.name": "New name"});
You can see more details here: https://firebase.google.com/docs/firestore/manage-data/add-data
Assuming there is a user id attached to each post, you can query from the posts collection where the user (that is changing his/her name) id matches the id that is in the post (from the posts collection) and then modify the user property/attribute from the results.
A sample code would look like this,
To modify from the user collection
final docRef = FirebaseFirestore.instance
.collection('users')
.doc(id);
final response = await docRef
.update(updatedDateInJson)
.then((value) => value)
.onError((error, stackTrace) => error);
To modify from the posts collection where the user is found in a post
final response = FirebaseFirestore.instance
.collection('posts')
.where('name', isEqualTo: 'John')
.get()
.then((value) async {
// modify each doc
});
Quick Note: use a onError block to check for errors in updating docs.
I’m working on an app with Flutter.
Each of my users has a map with their to-dos stored in cloud firestore:
I want to create a future that updates every to-do to false, which is true.
(In my example, that would be ’sport' and ’eat healthy')
My Problem:
I don’t really know how to do this. . .
I’d be very grateful if someone could help me.
You want to set every field inside the To-Dos map to false which is true. So I would suggest updating every field to false. You might think of checking if it is true and then updating it. But for your use case it is not a good option as anyway you want to set every field to false. Checking and then updating will only incur extra charges for querying. The following should work for your use case.
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> updateUserToDos() {
return users
.doc('your_document_id')
.update({
'To-Dos.Drink Water': false,
'To-Dos.Sport': false,
'To-Dos.eat healthy': false
})
.then((value) => print("User To-Dos Updated"))
.catchError((error) => print("Failed to update user: $error"));
}
To know more about updating document fields of Firestore in Flutter you can refer to this document.
EDIT
So you don’t know which fields are there inside the To-Dos map and you need to update every field inside the To-Dos map to be false. For that you can iterate over the keys of the map and update a new Map having keys same as that of the existing map and values to be false, then update the To-Dos Map field with the updated new Map. You can use the following piece of code. It will update every document inside the users collection.
FirebaseFirestore.instance
.collection('users')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
var updateMap = new Map();
var toDos = doc['To-Dos'];
for (var item in toDos.keys) {
updateMap[item] = false;
}
doc.reference.update({"To-Dos":updateMap });
});
});
To update the To-Dos map in any specific document you can do the following -
FirebaseFirestore.instance
.collection('users')
.doc("your_document_id")
.get()
.then((DocumentSnapshot doc) {
var updateMap = new Map();
var toDos = doc['To-Dos'];
for (var item in toDos.keys) {
updateMap[item] = false;
}
doc.reference.update({"To-Dos": updateMap});
});
There is no magic solution here. With your current data model, the only approach is to:
Read all documents
Change any false todo's to `true
Write back the document if you made any changes
The only way to make this cheaper (both in monetary cost as in the time it'll take) is to change #1 to only read documents that have a false value. In your current model that is only possible if To-Dos is an array, and then only for up to 10 values in there with an array-contains-any filter:
collectionRef.where("To-Dos", arrayContainsAny: [{
"Drink Water": false,
"Sport": false,
"eat healthy": false
}]
If the keys in this field are entered by the user, the above may become unfeasible because you can only have up to 10 values in there. The solution in that case would be to change your data model to fit your needs (as is quite common when working with NoSQL databases). For example, you could store a simple top-level hasUncompletedTasks field that you update each time you also write the document, and then use that for the query.
_getLatestCompletedWorkout() async {
try {
QuerySnapshot workouts;
workouts = await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('workouts')
.get();
for (var workout in workouts.docs) {
print('WORKOUT = ');
print(workout);
}
.....
What I really need is to get the last document saved; but before that I am just trying to fetch the "workouts" collection; the workouts.docs list always has 0 items. There are 2 items in the DB. What is wrong with this code? Also how to get the last saved item?
As mentioned by Frank :
You can refer Alex answer here :
The simplest way to achieve this is to add a date
property to each object in your collection, then simply query it
according to this new property descending and call limit(1) function.
This is the required query:
this.historyRef = afs.collection<History>('history', ref => ref.orderBy('date', 'desc').limit(1));
this.history = this.historyRef.snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data() as Hisotory;
const docId = a.payload.doc.id;
return { docId, ...data };
});
});
This has been found since cloud_firestore updates that prevent app that not regiter in App Check to take updates seriously "but it store it with lighter id color" which Protect your Cloud Firestore resources from abuse, such as billing fraud or phishing
Kindly check and ensure your app is registered inside App Check in Firebase console
I have a couple of writes that I want to be done together.
So I used a write batch.
Since the write batch requires a document reference, I have been creating the document before that write batch operation.
DocumentReference accountHistoryDoc = await queryResult.reference.collection('accountHistory').add({});
This led to many empty documents. Since I'm still testing and debugging the app, I assume because of an exception after the creation of the document.
How can I make sure that an empty document isn't created in the case of failure?
I'm thinking of changing this line
wb.set(
accountHistoryDoc, // Change this line
{
'account': newAccount,
'serverTimestamp': FieldValue.serverTimestamp(),
'type': 'hisab',
},
);
to
wb.set(
await subscriberDoc.collection('accountHistory').add({}),// new line
{
'account': newAccount,
'serverTimestamp': FieldValue.serverTimestamp(),
'type': 'hisab',
},
);
Is this my thinking correct?
Rest of code:
QuerySnapshot query = await FirebaseFirestore.instance
.collection(CurrentUser.getCurrentUser().uid)
.where('mobile', isEqualTo: mobile)
.get();
QueryDocumentSnapshot queryResult = query.docs.first;
DocumentReference subscriberDoc = queryResult.reference;
DocumentReference accountHistoryDoc = await queryResult.reference.collection('accountHistory').add({}); // < -- new empty document here
WriteBatch wb = FirebaseFirestore.instance.batch();
// update the total account
wb.update(
subscriberDoc,
{
'totalAccount': subscriber.totalAccount + newAccount,
},
);
// add new document in account history
wb.set(
accountHistoryDoc,
await subscriberDoc.collection('accountHistory').add({}),
{
'account': newAccount,
'serverTimestamp': FieldValue.serverTimestamp(),
'type': 'hisab',
},
);
If you call CollectionReference.doc() without an argument, it generates a new unique DocumentReference without already creating that document in the database. You can then use this DocumentReference to create the new document inside the batched write.
Also see the documentation for the FlutterFire doc() method.
The following transaction completely replaces the data in a Firestore Map when run:
DocumentSnapshot docSnapshot = await tx.get(docRef); // doc Snapshot
if (docSnapshot.exists) {
await tx.update(
docRef,
<String, dynamic>{
'Users': {
'user.uid': {'name': user.displayName, 'date': DateTime.now()},
}
},
);
What would be the correct way to add to a map instead of replacing the existing data, the same way FieldValue.arrayUnion works?
Since you already fetched the data you could take the map out from the snapshot, replace the data there and call the update with the altered map.