Callback Problems with Firebase and Android - java

I have a problem when I am fetching my Data from Firebase Firestore. I want to get multiple Documents out of my "Posts" collection and that works fine. But to get my user documents out of my "Users" collection i need to start multiple tasks(Task) that ALL need to be completed before I want to call my callback function and i cant figure out how. Is there even a way to do it with a callback? Ive tried to solve it with Continuations but had a hard time.
Thanks in advance.
Here some simple code i wrote so u can maybe understand the problem a little bit better.
public void getPosts(final postCallback callback) {
final FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference postsRef = db.collection("Posts");
Query postsQuery = postsRef.orderBy("createTime", Query.Direction.DESCENDING).limit(20);
// Starting the post documents
Task<QuerySnapshot> task = postsQuery.get();
task.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()){
QuerySnapshot querySnapshot = task.getResult();
List<DocumentSnapshot> docsList = querySnapshot.getDocuments();
for(DocumentSnapshot docSnap : docsList){
String userID = docSnap.getString("originalPoster");
// getting user documents
Task<DocumentSnapshot> userTask = db.collection("Users").document(userID).get();
userTask.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot userDoc = task.getResult();
String userID = userDoc.getId();
String firstName = userDoc.getString("first_name");
String surname = userDoc.getString("surname");
User userObject = new User(firstName, userID, surname);
// cant call my callback right here otherwise its called for every
// completed user fetch
}
});
// cant call my callback right here since its too early
}
}else if(task.isCanceled()){
System.out.println("Fetch failed!");
}
}
});
}

Related

Query not going deep enough?

I am using Firestore & Java.
I am trying to change multiple values by query and batch.
My DB
Kullanıcılar
-deneme#deneme.com
--Hayvanlar
---RandomId1
"ozel", "E65" //I WANNA UPDATE THIS (First))
---RandomId2
----Dogru
-----RandomId
------Acep
"ozel", "E65" //AND THIS (Second)
My Code
CollectionReference cr = fs.collection("Kullanıcılar/" + deneme#deneme.com + "/Hayvanlar");
Query query = cr.whereEqualTo("ozel", "E65");
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = fs.batch();
for (DocumentSnapshot ds : task.getResult()) {
batch.update(ds.getReference(), "ozel", "E75");
}
batch.commit();
}
}
});
When i run this codes my only first value updates.
I never reach to second value :/
Move batch.commit() outside your for loop

firestore query in for loop android java

I am new to java. I have a firestore_member_list. In firestore_member_list, it contains values: ["steve","ram","kam"]. I am using for loop to pass values one by one.
loadingbar.show()
for( int k=0; k<firestore_member_list.size();k++){
String member_name = firestore_member_list.get(k);
final DocumentReference memDataNameCol = firestoredb.collection("member_collection").document(member_name);
memDataNameCol.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
// In here, I am retreiveing the document data from firestore and assigning it to the ArrayList which is `all_mem_data`
all_mem_data.add(document.get("member_name").toString());
all_mem_data.add(document.get("member_address").toString());
Toast.makeText(getActivity(), "all mem data array"+all_mem_data.toString(),
Toast.LENGTH_LONG).show();
}
}
}
});
}
Log.d("all_mem_data",all_mem_data)
loadingbar.hide()
I know firestore executes asynchronously. Since firestore retrieves data asynchronous, before filling the array of all_mem_data, the last line gets executed and shows the empty array. How to wait for the array to get filled and after filling,execute the last two lines. Please help me with solutions.
Any code that needs the data from the database, needs to be inside the onComplete that fires when that data is available.
If you want to wait until all documents are loaded, you can for example keep a counter:
loadingbar.show()
int completeCount = 0;
for( int k=0; k<firestore_member_list.size();k++){
String member_name = firestore_member_list.get(k);
final DocumentReference memDataNameCol = firestoredb.collection("member_collection").document(member_name);
memDataNameCol.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
...
if (completeCount++ == firestore_member_list.size()-1) {
Log.d("all_mem_data",all_mem_data)
loadingbar.hide()
}
}
});
}

Firestore updating 2 different documents

I am using Cloud Firestore in my app and have 2 collections Customers and Properties. I have an activity where the user can update the data contained in a customer document name address etc. This code shown below is working fine.
db.collection("Customers").document(customer.getCustomerId())
.update(
"name", c.getName(),
"email", c.getEmail(),
"phoneNo", c.getPhoneNo(),
"address", c.getAddress(),
"creatorId", c.getCreatorId()
)
.addOnCompleteListener(new OnCompleteListener<Void>() {
I have the document reference of the customers saved in the Properties documents so I can reference which customer owns which property. Using this reference I want to search for Properties containing that reference and update the name field if it has been changed. I have tried adding the code into my method after the onComplete checks for the above code, but it doesn't update the name field every time only every few attempts.
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference propRef = rootRef.collection("Properties");
propRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
Map<Object, String> map = new HashMap<>();
map.put("customer", customerName);
propRef.document(document.getId()).set(map, SetOptions.merge()).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Is there a way to achieve what I am trying to do?
I did think I could do it using batch but from what I have read this does not allow searching.
#AlexMamo
This is a document from my Customers collection
This is a linked document from my Properties collection
Customers Structure
Properties Structure
According to your comments, to solve your the issue, please use the following lines of code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference propertiesRef = rootRef.collection("Properties");
CollectionReference customersRef = rootRef.collection("Customers");
customersRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
String customerName = document.getString("name");
propertiesRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot doc : task.getResult()) {
propertiesRef.document(doc.getId()).update("customer", customerName);
}
}
}
});
}
}
}
});
See you should use different CollectionReference objects, propertiesRef and customersRef, you are using a single one.
you need to implement the Interface from firestore OnDataChange and receive the new values of your variables bind to your views.
in that case when you modify or update a value in your firestore the change triggers this interface and you can assign the new change.
ValueEventListener myListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Customer customer = dataSnapshot.getValue(Customer.class);
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
rootRef.addValueEventListener(myListener);
hopefully it works for you.
https://firebase.google.com/docs/database/android/read-and-write?authuser=0

How to get the id when call add in one step in Firestore?

I know to get id:
String cityId = db.collection("cities").document().getId();
db.collection("cities").document(cityId).set(city);
But is more easy:
db.collection("cities").add(city);
But how to get id? It not work .add(city).getId(). No information in docs.
How to get the id when call add in one step in Firestore?
There is no way in which you can get the document id in a single step, as you do when using a document() call. To solve this, you should add a complete listener. Try this:
db.collection("cities").add(city).addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
if (task.isSuccessful()) {
DocumentReference document = task.getResult();
if (document != null) {
String id = document.getId(); //Do what you need to do with the document id
Log.d(TAG, id);
}
}
}
});

Firestore getting most liked posts

I'm making a simple app in which I want to introduce most liked posts. I'm using Cloud Firestore. My question is how the query should look like in this case? (I'm using Java)
Here's the Firestore tree:
-ROOT
--Posts
---Post
----Likes
Likes collection is set of users' ids.
Assuming that the Likes property is of type number and not String, please use the following code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
DocumentReference ref = rootRef.collection("Posts").document("Post");
ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
long numberOfLikes = document.getLong("Likes");
Log.d(TAG, String.valueOf(numberOfLikes));
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
The output in your logcat will be the number of likes.

Categories