How to remove item in list with WriteBatch? - java

I'm using Firebase Cloud database. I queried a collection and got a list of documents. Each document contains a field cars which contains strings. In case the array contains at least three strings I want to remove the string carPath from this array (not the whole array). Otherwise, it should remove the whole document. I'm using WriteBatch. What I did:
fireDB.document(groupPath).collection("users").whereArrayContains("cars",carPath).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful() && task.getResult() != null) {
QuerySnapshot snapshot = task.getResult();
for (DocumentSnapshot curr : snapshot.getDocuments()) {
List<String> cars = (List<String>) curr.get("cars");
if (cars == null) continue;
if (cars.size() <= 2) {
batch.delete(snapshot.getReference());
} else {
// What to do here?
}
}
}
}
});
How should I remove one item in the list with WriteBatch?

What you're trying to do is just a document update. You can do a regular update with FieldValue.arrayRemove(). Except you will do it in the context of a batch using update() instead of a standalone update.
batch.update(snapshot.getReference(), "cars", FieldValue.arrayRemove(carPath));

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

How to retrieve all document ids from a collection?

I'm trying to get all of the document ids in a collection groups. The document id is the group name which is unique. Also each document in groups does not have fields, only pointers to other collections. I wrote the following code:
fireDB.collection("groups").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
QuerySnapshot querySnapshots = task.getResult();
if (querySnapshots != null) {
for (QueryDocumentSnapshot currentDocumentSnapshot : querySnapshots) {
groups.add(currentDocumentSnapshot.getId());
}
Collections.sort(groups);
ArrayAdapter<String> adapter = new ArrayAdapter<>(SignUpActivity.this,android.R.layout.simple_list_item_1,groups);
groupNameText.setAdapter(adapter);
} else {
Log.d(this.getClass().getName(), "No groups in the database");
}
} else {
Log.d(this.getClass().getName(), "addOnCompleteListener:failed");
}
}
});
But groups is always empty because firebase does not give me documents without fields (figured it out after some debugging). How should I do it?
I sounds like you have some subcollections under document paths, without having a document at that path. This is a valid situation, but I don't think it is possible to get those locations in the client-side SDKs, as there is no document there.
See https://www.reddit.com/r/Firebase/comments/b0poug/empty_virtual_docs_this_document_does_not_exist/
The Firebase console shows these locations in italics, since it needs to show the subcollections under each location. It likely uses the show_missing flag in the REST API or Admin SDK for that.

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()
}
}
});
}

Wait until Firestore data is retrieved

I need after inner foreach finish and add all data in list then send to interface
but when but this line " view.setOrders(orderList); " below " orderList.add(order); "
my code run okye but not that is not performance , I need best way to collection list then send to interface ..
public ListenerRegistration getOrders() {
view.showLoading();
ListenerRegistration listenerRegistration = refOrders.addSnapshotListener((queryDocumentSnapshots, e) -> {
view.hideLoading();
if (e != null) {
view.onErrorMessage(e.getMessage());
} else {
List<Order> orderList = new ArrayList<>();
for (QueryDocumentSnapshot snapshot : queryDocumentSnapshots) {
Order order = snapshot.toObject(Order.class);
order.setOrderId(snapshot.getId());
refUsers.document(order.getPhone()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
User user = task.getResult().toObject(User.class);
order.setName(user.getName());
order.setAddress(user.getAddress());
orderList.add(order);
}
}
});
}
//Here Back List size = 0
view.setOrders(orderList);
}
});
return listenerRegistration;
}
Since get() method is asynchronous which means that the code after the CompletionListener will be executed first and then after the data is retrieved the listener will get executed.
Therefore to solve the issue add the following line view.setOrders(orderList); inside the CompletionListener
if (task.isSuccessful()) {
User user = task.getResult().toObject(User.class);
order.setName(user.getName());
order.setAddress(user.getAddress());
orderList.add(order);
view.setOrders(orderList);

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