No output when running Query using Java and Google's Firestore - java

I have a program which is an event creator/locator. The problem I am having is pulling a query from the database. With this instance, I am just trying to pull a specific zip code from the database and display details of the appropriate document provided from the query.
private void findEvent() {
Log.d(TAG,"Start FindEvent Function");
eventRef.whereEqualTo("zip", searchZip.toString())
.limit(1).get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Task Successful! Starting For Loop!");
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId()+"=>"+document.getData());
updateUI(document);
}
} else {
Log.w(TAG, "Error getting documents.", task.getException());
}
}
});
}//end findEvent function
When I run the program, on my phone and hit the search button which starts the function above, nothing is outputted to my activity. I check the logs, which is the reason I put them in, and in the log, I get the Debug: "Task Successful! Starting for loop!" and then nothing after this when I am expecting a Debug log that I have in the updateUI function which tells me the function is starting. Therefore telling me that it is not running the updateUI function.

Your query is probably just not returning any results, and your code is not checking for that case.
Before iterating the QuerySnapshot delivered to the task:
QuerySnapshot qsnapshot = task.getResult();
You could use its isEmpty() method to determine if it actually contains any documents:
if (qsnapshot.isEmpty()) {
Log.d(TAG, "Query resulted in no documents");
}

Related

How to fix Android Cloud Firestore document snapshot

I am checking if the user has made an appointment in the database if not add the user(add a new document which has the user details). The problem with my app is that it runs both AddUser() and AlertUser() functions:
DocumentReference docRef = firebaseFirestore.collection(group).document(userIdentity);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot documentSnapshot = task.getResult();
if (documentSnapshot != null) {
if (!documentSnapshot.exists()){
//User does not exist create the new user
addUser();
} else {
//User has already made an appointment show dialog
AlertUser();
}
}
}else {
Log.i("Key","Task not successfull");
}
}
});
This is happening because when the onComplete() method is triggered for the first time, the documentSnapshot does not exist, thus the addUser() method is called. This means that the corresponding user is added to the database, which also means that the onComplete() method is triggered again (since a change was made), but this time, the else part of the statement is triggered and therefore AlertUser() is called. This the reason why both of your methods are called.
This is a Cloud Firestore question and not a Firebase realtime database one, so I've changed the tag accordingly.

Android - How to handle Firestore exceptions at user level?

I'm using Firestore in my app and I can't figure out how to handle at user level the exceptions thrown by that. (I mean what to display to the user when such exceptions occur).
For example, to perform any CRUD operation on Firestore (DocumentReference#get, DocumentReference#set, DocumentReference#update) a Task is returned, which might contain an exception, but in the documentation I can't find why this exception might be thrown by Firestore.
Is there something better that we can do, rather than simply log the exception and show a generic message like "an error occurred, please try again later"?
As in the official documentation regarding getting data, you can get the exception from the task object like this:
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d(TAG, "DocumentSnapshot data: " + document.getData());
} else {
Log.d(TAG, "No such document");
}
} else {
//Log the error if the task is not successful
Log.d(TAG, "get failed with ", task.getException());
}
}
});
And remember, a Task is complete when the work represented by the Task is finished, regardless of its success or failure. There may or may not have been an error, and you have to check for that. On the orter side, a Task is "successful" when the work represented by the task is finished, as expected, with no errors.
As #Raj mentioned in his answer, you can also use addOnFailureListener but note, if there is a loss of network connectivity (there is no network connection on user device), neither onSuccess() nor onFailure() are triggered. This behavior makes sense, since the task is only considered completed when the data has been committed (or rejected) on the Firebase server. onComplete(Task<T> task) method is called also only when the Task completes. So in case of no internet connection, neither onComplete is triggered.
You can use onFailureListener() method of Firestore and get the errors while getting, setting or updating the data. In this example I have used it in setting data:-
firestore.collection("User").document(uid).set(user).addOnSuccessListener(this, new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid)
{
//Data Saved Successfully
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(Exception e)
{
//Toast error using method -> e.getMessage()
}
});
If you want to catch exceptions in firebase authentication module then refer:- How to catch a Firebase Auth specific exceptions

Firestore SetOptions.mergeFields doesn't work

I'm trying to use .set on my document to set 2 values inside it, but it gives me this error
Field 'friendRequests' is specified in your field mask but not in your input data.
Error is triggered by this line in my code:
mCollRef.document(documentID).set(tempUser, SetOptions.mergeFields(fieldsToUpdate))
Everything works fine when i'm not using SetOptions, just mCollRef.document(documentID).set(tempUser)
What did i do wrong here?
My Code here:
mCollRef.whereEqualTo("userID", clickedUserID)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()) {
List<String> fieldsToUpdate = Arrays.asList("friendRequests", "friends");
for(QueryDocumentSnapshot document : task.getResult()) {
User tempUser = document.toObject(User.class);
tempUser.updateFriends(mUsersName, mUsersID, 1);
tempUser.updateFriendRequests(myNoDotEmail, 2);
String friendName = tempUser.getUserName();
String documentID = document.getId();
mCollRef.document(documentID).set(tempUser, SetOptions.mergeFields(fieldsToUpdate))
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(FriendsActivity.this, "Friend Added", Toast.LENGTH_SHORT).show();
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(FriendsActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
It's worth pointing out that sometimes it works, like 1/10 it'll work just for no reason whatsoever and other 9 times it just doesn't
So, turns out that error was caused by my code few lines above SetOptions call:
As you can see above, field friendRequests isn't empty, but i'm setting it empty with this code from the question tempUser.updateFriendRequests(myNoDotEmail, 2); and that when it shows an error. Everything works when there are 2 or more friendRequests and i delete one of them, not leaving field completely empty.
Turns out SetOptions.mergeFields can't be called to set nested maps inside documents to empty maps.

Read Data from Cloud FireStore Android

Is this script wrong, because the data I receive is null while I've added data on the Cloud Firestore. I do not use RecyclerView because I only need one data only.
This is the script:
private void getCustomer(){
firestoreDB.collection("customer")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
customers = new ArrayList<>();
for (DocumentSnapshot doc : task.getResult()) {
Customer customer = doc.toObject(Customer.class);
customer.setId_customer(doc.getId());
customers.add(customer);
}
} else {
// Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
firestoreListener = firestoreDB.collection("customer")
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
// Log.e(TAG, "Listen failed!", e);
return;
}
customers = new ArrayList<>();
for (DocumentSnapshot doc : documentSnapshots) {
Customer customer = doc.toObject(Customer.class);
customer.setId_customer(doc.getId());
customers.add(customer);
}
}
});
id_customer = customers.get(0).getId_customer();
}
and this is my firestore:
You cannot use something now that hasn't been loaded yet. With other words, you cannot simply use the following line of code:
id_customer = customers.get(0).getId_customer();
Outside the onSuccess() method because it will always be null due the asynchronous behaviour of this method. This means that by the time you are trying to use the id_customer variable outside that method, the data hasn't finished loading yet from the database and that's why is not accessible.
A quick solve for this problem would be to use that result only inside the onSuccess() method, or if you want to use it outside, I recommend you see the last part of my anwser from this post in which I have exaplined how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Very simple Firestore transaction fails

I'm struggling with a super simple transaction. It always fails with the message "Transaction failed all retries" but there are no error messages besides that on the logcat.
When I debug it, I see that it's being retried several times. I really don't know why, as other transactions run without issue.
I just want to clone one document from one collection into another collection. Think from "videos" to "favorites" (I know this can be done outside of a transaction as #Alex points out, but this is just the failing part, the real transaction is longer)
private void copy(
final DocumentReference SOURCEDOCREF,
final CollectionReference TARGETCOLREF) {
Transaction.Function<? extends Void> transaction = new Transaction.Function<Void>() {
#Nullable
#Override
public Void apply(#NonNull Transaction transaction) throws FirebaseFirestoreException {
DocumentSnapshot doc = transaction.get(SOURCEDOCREF);
if (doc.exists()) {
DocumentReference favoriteRef = TARGETCOLREF.document("FV_" + doc.getId());
Map<String, Object> data = doc.getData();
transaction.set(favoriteRef, data);
return null;
// NOTE: This is reached, ie. the source doc exists
// the data recovered, and set into the transaction.
} else
throw new FirebaseFirestoreException("Item does not exist", FirebaseFirestoreException.Code.NOT_FOUND);
}
};
setMode(MODE_SPLASH);
FirebaseFirestore.getInstance().runTransaction(transaction)
.addOnSuccessListener(
(Activity) getContext(),
new OnSuccessListener<Object>() {
#Override
public void onSuccess(Object aVoid) {
setMode(MODE_FOLLOW);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
hide();
DialogHelper.customToast(getContext(), e.getMessage());
}
});
}
According to the documentation about transactions:
If a transaction reads documents and another client modifies any of
those documents, Cloud Firestore retries the transaction. This feature
ensures that the transaction runs on up-to-date and consistent data.
So, you can expect that your transaction will be retried if the source document has been modified before the transaction complete.
You can also expect a transaction can fail.
A transaction can fail for the following reasons:
The transaction contains read operations after write operations. Read operations must always come before any write operations.
The transaction read a document that was modified outside of the transaction. In this case, the transaction automatically runs again.
The transaction is retried a finite number of times.
A failed transaction returns an error and does not write anything to
the database. You do not need to roll back the transaction; Cloud
Firestore does this automatically.
There is no need to use transaction in such a case. To copy a document from a location to another, please use the following method:
public void cloneFirestoreDocument(DocumentReference fromPath, final DocumentReference toPath) {
fromPath.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document != null) {
toPath.set(document.getData())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully written!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error writing document", e);
}
});
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
In which fromPath is the location of the document that you want to be moved and toPath is the loaction in which you want to move the document.
The flow is as follows:
Get the document from fromPath location.
Write the document to toPath location.

Resources