Query not going deep enough? - java

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

Related

Firestore database java add where condition before get collection

I am using the Firestore database for my web application. When I query documents from a collection I need to append a where condition. Is there any method available to append specific conditions before fetch CollectionReference?
CollectionReference collectionReference = dbFirestore.collection("collectionName").someMethod("Where conditions to fetch specific documents")
Or any other options to send a full query with conditions from Java or Spring to Firestore and get results? I don't want to have database codes in my front-end designs. If no other option, I would end up using javascript to do so. But it would be not a proper design.
Query query = dbFirestore.collection("collectionName").whereEqualTo("colmn1", "value").whereEqualTo("column2", "value");
How to iterate values from query (com.google.cloud.firestore.Query) object?
If you need to get the result of the following query:
Query query = dbFirestore.collection("collectionName").whereEqualTo("colmn1", "value").whereEqualTo("column2", "value");
You have to call get(), and then attach a listener, as you can see in the following lines of code:
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
if (document != null) {
//Get the data out from the document.
}
}
} else {
Log.d(TAG, task.getException().getMessage()); //Never ignore potential errors!
}
}
});
If you're using Java for a Non-Android project, then you should consider using the following lines of code:
ApiFuture<QuerySnapshot> future = dbFirestore.collection("collectionName").get();
QuerySnapshot querySnapshot = future.get();
List<QueryDocumentSnapshot> documents = querySnapshot.getDocuments();
ArrayList<String> ids = new ArrayList<>();
ArrayList<String> names = new ArrayList<>();
for (QueryDocumentSnapshot document : documents) {
String docId = document.getId();
ids.add(docId);
System.out.println(docId);
String name = document.getString("name");
names.add(name);
System.out.println(name);
}

Callback Problems with Firebase and Android

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!");
}
}
});
}

iterate all data in firestore

I am making an android app to average all rating data in Firestore. This is the structure of my Firestore. I want to retrieve all rating_num which are [1, 4, 5] using DocumentSnapshot
reviews
|-my_document1
|- rating_num:1
|-my_document2
|- rating_num:4
|-my_document3
|- rating_num:5
This is what I have done so far:
Query query = FirebaseFirestore.getInstance().collection("reviews").whereEqualTo(rating_num,??) //no clue
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
Log.d("debug", "print all rating number using for loop");
for (QueryDocumentSnapshot document : task.getResult()) {
String cum_rating = document.getString("avg_quality");
Log.d("debug", cum_rating);
}
}
});
I have no clue how to match the only rating_num in all of my documents, and it does not show anything in my for loop, any help would appreciate.
Please also note that there is no need to call any .whereEqualTo(), as you don't need to match any property to a specific value.
To get the value of rating_num property from all documents and create an average, please use the following lines of code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference reviewsRef = rootRef.collection("reviews");
reviewsRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
long count = 0;
for (QueryDocumentSnapshot document : task.getResult()) {
count =+ document.getLong("rating_num");
}
long average = count / task.getResult().getDocuments().size();
Log.d("TAG", "avarage: " + average);
}
}
});
If you only have three documents with those three values, 1, 4, and 5, the result in the logcat will be:
3.33

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

Firestore not querying second collection

I'm kind of new to android studio and firestore database and
I'm having some trouble with querying my second firestore collection. As the title says, i am querying two collections, first one is:
with the code :
firestore = FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.build();
firestore.setFirestoreSettings(settings);
firestore.collection("Obiective").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
//<--------- Check if firestore entry is already downloaded into file --------->
SingletonObjectivesId.getInstance().getIds().clear();
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, task.getResult().size() + " number of documents");
SingletonObjectivesId.getInstance().setSize(task.getResult().size());
if(document.exists() && document != null) { ...
and the second collection have the following format:
with the code:
firestore.collection("Routes")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, task.getResult().size() + " = task.getResult().size()");
for (QueryDocumentSnapshot document : task.getResult()) {
objectives_id.clear();
id_route = document.getId();
if(document.exists() && document != null) {
Map<String, Object> map = document.getData();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String field_name = entry.getKey() + "";
String id = document.getString(field_name) + "";
objectives_id.add(id);
}
}
routes.add(new Route(objectives, objectives_id, id_route));
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
As you can see in the second code i added a Log.d ( after if (task.isSuccessful()) ) who will display the number of documents. In my case, the first query Log.d returns 3 and the second returns 0 despite the fact that i have 2 documents in there. How can i access this 2 documents ?
Thank you.
Firebase APIs are asynchronous, meaning that the onComplete() method returns immediately after it's invoked, and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, the number of documents that you try to log, is not populated from the callback yet.
Basically, you're trying to use a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to move the code that queries the second collection inside the first callback (inside the onComplete() method) so-called nested queries, otherwise I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
After i followed the steps from the video, i updated the code like this:
I have a global variable firestore created at the beginning of my class
private FirebaseFirestore firestore;
I have two methods readDataObjective and readDataRoute and two interfaces FirestoreCallback and FirestoreCallbackRoutes
readDataRoutes
private void readDataRoute(FirestoreCallbackRoute firestoreCallbackRoute){
firestore.collection("Trasee").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) { ...
readDataObjective
private void readDataObjective(FirestoreCallback firestoreCallback){
firestore.collection("Obiective").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
SingletonObjectivesId.getInstance().getIds().clear();
for (QueryDocumentSnapshot document : task.getResult()) { ...
Interfaces
private interface FirestoreCallback{
void onCallback(ArrayList<Objective> list);
}
private interface FirestoreCallbackRoute{
void onCallback(ArrayList<Route> list);
}
And in onCreate method i call readDataObjective and readDataRoute like this
firestore = FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().build();
firestore.setFirestoreSettings(settings);
readDataObjective(new FirestoreCallback() {
#Override
public void onCallback(ArrayList<Objective> list) {
for(Objective item : list){
//Create plainText Object - delimiter "/~/"
String data = "Title:" + item.getTitle() + "/~/" +
............................
} else if(str.contains("Longitude:")){
obj.setLongitude(str.substring(10,str.length()));
}
start = crt + 2;
}
}
SingletonObjectivesArray.getInstance().getObjectives().add(obj);
}
readDataRoute(new FirestoreCallbackRoute() {
#Override
public void onCallback(ArrayList<Route> list) {
Log.d(TAG, 2 + " ");
ArrayList<Objective> routeObjectives = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
routeObjectives.clear();
for (int j = 0; j < SingletonObjectivesArray.getInstance().getObjectives().size(); j++){ ...
With the mention that readDataRoute is called inside readDataObjective, at the end of it.
I noticed that the problem is not only with the second query, but with the first one too. I added a new document into the first collection and after running the code, the first query return the old data ( without my new entry ).

Categories