I have add data on firestore Firebase but I don't have specific document because I use addSnapShotListener to retrieve the data. How can I delete the document when I don't know the name of it. Here is the code where I add the data:
mondayCollectionReference.document().set(userMap, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(TimeForm.this, "Submitted", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("ERROR", e.getMessage());
}
});
document() returns a DocumentReference object. That object gives you everything you need to know to delete it, especially its own delete() method. It also has a getId() method to help you remember its id.
So, you should store the DocumentReference object first before calling methods on it:
DocumentReference ref = mondayCollectionReference.document()
String id = ref.getId();
ref.set(...);
// use ref or id later if you want to delete it
Related
This question already has answers here:
How to return a DocumentSnapShot as a result of a method?
(2 answers)
Closed 2 years ago.
I'm building an APP and using Firestore to create a collections of Users. Before adding the new user to the collection i need to check within the collection if the email is already in use and for this i've built two methods: one for reading the collection looking for an user with that email and the other one to adding the new user IF everything is ok. But no matter what I do, the add method always executes first leading to the validation being useless. I guess it's has something to do with the methods priority withing Firebase but i really couldn't pull out with a solution
Here's the two methods
The first one it's validation and the second one it's the add
private boolean createFirestoreUser(final String identificador) {
final boolean[] isValid = {true};
FirebaseFirestore.getInstance().collection("Usuarios")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
for (QueryDocumentSnapshot document : task.getResult()) {
if(identificador.equals(document.getString("identificador")))
isValid[0] = false;
}
}
});
return isValid[0];
}
private void createUser(Usuario novoUsuario) {
FirebaseFirestore.getInstance().collection("Usuarios")
.add(novoUsuario)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(final DocumentReference documentReference) {
documentReference
.update("id", documentReference.getId())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
loginSessionManager.createLoginSession(documentReference.getId());
loginSessionManager.checkLogin(this.getClass());
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
snackbar.showMensagemLonga(v,e.getMessage());
}
});
}
The second one is ALWAYS being called first no matter the order i use on the button listener. I've debbuged and it really enters in the isValid[0] = false after the user is added
Where are you calling the methods?
You could just call the createUser inside of the .addOnSuccessListener this way it will not be called until the valdiation is returned.
Something like:
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
for (QueryDocumentSnapshot document : task.getResult()) {
if(identificador.equals(document.getString("identificador")))
isValid[0] = false;
else
createUser(novoUsuario)
}
}
The reason why I'm asking where you are calling the methods is because your variable might be by default true which would trigger the second function before the async listener is returned therefore calling the second method before the validation is made.
So the alternative would be to call the register method inside the same mehrod where you are validating or if what I'm assuming that you have a boolean declared first to see if you call the create method, just have it false as default and make sure to be calling it after the async .OnCompleteLister is finished.
I am trying to delete a document from Firestore. I am trying to do this based on the task ID that was randomly generated by Firestone. when a particular task is selected on android, I want to be able to delete that task. However, when I tried debugging the code, it shows a random ID that doesn't exist on the database and tries to delete that, sending me a success message in the console. I am not sure where I am going wrong. Please advice.
public void deleteTasks(View v) {
userId = mFirebaseAuth.getCurrentUser().getUid();
String tskid= fStore.collection("usersData").document(userId).collection("tasks").document().getId();
DocumentReference taskref = fStore.collection("usersData").document(userId).collection("tasks").document(tskid);
taskref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.d("tag", "Task Deleted Successfully");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("tag", "Task Deletion Unsuccessful");
}
});
}
the above delete method is called on the button using android:OnClick
The Task I'd like to delete is Circled
When you are using the following line of code:
String tskid= fStore.collection("usersData").document(userId)
.collection("tasks").document()
.getId();
You are generating a new random ID. Actully, you are reserving a key for a document that you'll be writing in the future. When using this line:
DocumentReference taskref = fStore.collection("usersData").document(userId)
.collection("tasks").document(tskid);
You are creating a reference to that location. However, when using this line:
taskref.delete().addOnCompleteListener(/* ... */);
You are trying to delete a document that does not exist and this is because you didn't create it in the first place. If you need to delete a specific document, you need to know the ID. So the following lines of code will do the trick:
public void deleteTasks(View v) {
userId = mFirebaseAuth.getCurrentUser().getUid();
String tskid = "CQ45RKh8Ohd6DXjSQ8RO";
DocumentReference taskref = fStore.collection("usersData").document(userId)
.collection("tasks").document(tskid);
taskref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.d("tag", "Task Deleted Successfully");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("tag", "Task Deletion Unsuccessful");
}
});
}
In order to delete that document, I have used the exact same ID that exists in the database.
When a new user registers in my app using firebase-authentication custom sign in using email and password, I need to update that data into my firestore.
But Firebase only has FirebaseAuth.createUserWithEmailAndPassword(email, password) to create a new account and hence I cannot update my user's username at the same time.
To update the E-Mail in Firestore, I use Firebase cloud functions. Here's the code:
export const onNewUserJoined = functions.auth.user().onCreate((user) => {
//const newUserDisplayName = user.displayName //CAN'T USE THIS. REASON is BELOW
const newUserUID = user.uid
const newUserEmail = user.email
const timeCreated = Date.now()
console.log(`${newUserUID} has joined.`)
return admin.firestore().collection('Agent').doc(`${newUserUID}`).set({"E-Mail": newUserEmail, "Time": timeCreated})
})
OK, great now I have updated the E-Mail and time created in Firestore successfully.
But next challenge is I need to update the user's username in the same Firestore document. I do it instantly after the createUserWithEmailAndPassword() like this:
DocumentReference dDocRef = FirebaseFirestore.getInstance().document(documentPath);
Map<String, Object> updateUsernameAndPhone = new HashMap<>();
updateUsernameAndPhone.put("username", username);
updateUsernameAndPhone.put("phoneData", phoneModel);
dDocRef.update(updateUsernameAndPhone).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(), "Data successfully stored in Firestore", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
}
});
Now, it depends on who acts first, the cloud function or the user's device.
If the cloud functions act first, then there is no issue. The username and phoneModel both get updated into the document successfully. No issues.
But incase, the phone acts first then I get the following error:
As this error has occurred, username isn't in the document and only email and timeCreated are in the document updated by the cloud function which got late to create document so that user's device can update the username with ease.
I CAN'T use .SET instead of .update() in my app because if I use .set() and the cloud functions create the email and timeCreated fields first. Then the device will DELETE them and put username and phoneModel.
So how can I do this?
I can forcefully delay updating the username by putting it in the next activity so that cloud functions get enough time to do their job, but my signUpActivity asks for username along with email and password edit texts. I don't want to create a separate activity for that.
I used to use .update() when my data was stored in realtime database and it used to create the child even if the path didn't exist. But it looks firestore won't update if the field doesn't exist.
Any solution for this?
I tried as per #DougStevenson said and here's my code:
final String newUserUID = Objects.requireNonNull(signUpAuth.getCurrentUser()).getUid();
final String documentPath = "Agent/" + newUserUID;
FirebaseFirestore fFirestoreRef = FirebaseFirestore.getInstance();
final DocumentReference dDocRef = fFirestoreRef.document(documentPath);
fFirestoreRef.runTransaction(new Transaction.Function<Void>() {
#Nullable
#Override
public Void apply(#NonNull Transaction transaction) throws
FirebaseFirestoreException {
DocumentSnapshot documentSnapshot = transaction.get(dDocRef);
transaction.update(dDocRef, "username", username);
transaction.update(dDocRef, "phoneData", phoneModel);
return null;
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(), "Data updated in Firestore . . .", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
}
});
But no luck. It still gives me error: Cannot update a document which does not exist.
Use a transaction to resolve conflicts from multiple clients that are all trying to modify the same document. The transaction handler will be retried on the client if it detects the document was modified before the change could take place.
I have the following transaction using Firestore:
mDb.runTransaction(new Transaction.Function<Void>() {
#Override
public Void apply(final Transaction transaction) throws FirebaseFirestoreException {
DocumentReference documentReference = mDb.collection("collectionOne").document("documentOne");
/*
some code
*/
transaction.update(documentReference, App.getResourses().getString(R.string.field_one), FieldValue.increment(1));
transaction.update(documentReference, App.getResourses().getString(R.string.field_two), FieldValue.increment(1));
return null;
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d("Debugging", "Transaction correctly executed.");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w("Debugging", "Transaction failure.", e);
}
});
My question is: when updating, for example, two fields of the same document within the same transaction, will such a transaction yield to one or two documents reads?
when updating, for example, two fields of the same document within the same transaction, will such a transaction yield to one or two documents reads?
Doesn't matter how many fields you change in a document in one operation, you'll always be charged with one write operation. If you make the writes, one after the other, you'll be charged with two write operations.
I have a document ref user/userid23435534 and I want to update a single (document has several fields) field(nick) in that document.
I call this method: ref.ref.update("nick","test123") and I can see from logging that ref.getpath() is user/userid23435534 indeed.
However, after calling this method, and getting success from my OnSuccessListener I still see that my field is not updated in the firestore database. What did I get wrong here?
EDIT:
public static void updateDocument(){
final DocumentReference ref = db.collection("user").document("userid23435534");
ref.update("nick", "test123" ) //logging shows that red.getPath is "user/userid23435534"
.addOnSuccessListener(aVoid -> {
//success is called when calling method that runs this code
}).addOnFailureListener(e -> {
//....
})
}
I just ran this code in my local emulator, and it updates the document without problems:
DocumentReference ref = db.collection("56246892").document("uid");
ref.update("nick", "test123" ).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
System.out.println("Updated");
}
});
Are you sure the document already exists? That is required for update() to work. If you're not sure whether the document exists, use set(..., SetOptions.merge()).