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);
}
Related
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
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.
I am combining two queries to do the "not equals" query for a Firestore collection which isn't possible natively in Firestore so I have to do it locally. I need to get the last visible document of my combined task to use for pagination.
Following is the code that I am writing to get a list of documents from both query results.
Query first = firebaseFirestore
.collection("jobs")
.whereGreaterThan("createdBy",currentUser.getEmail())
.orderBy("createdDate",Query.Direction.ASCENDING)
.limit(2);
Query second = firebaseFirestore
.collection("jobs")
.whereLessThan("createdBy",currentUser.getEmail())
.orderBy("createdDate",Query.Direction.ASCENDING)
.limit(2);
Task<QuerySnapshot> firstTask = first.get();
Task<QuerySnapshot> secondTask = second.get();
final Task<List<QuerySnapshot>> combinedTask = Tasks.whenAllSuccess(firstTask, secondTask);
combinedTask.addOnSuccessListener(new OnSuccessListener<List<QuerySnapshot>>() {
#Override
public void onSuccess(List<QuerySnapshot> querySnapshots) {
List<Job> list = new ArrayList<>();
for(QuerySnapshot qs: querySnapshots){
for(DocumentSnapshot document: qs){
Job job = document.toObject(Job.class);
list.add(job);
}
}
JobAdapter jobAdapter = new JobAdapter(list);
recyclerView.setAdapter(jobAdapter);
DocumentSnapshot lastVisible = combinedTask.getResult().get()
}
});
In the line
DocumentSnapshot lastVisible = combinedTask.getResult().get()
I can only either access the documents from my first query or my second query so I am unsure how to get the last visible document, I think the last document would be from my second query logically but I am unsure.
Here is the tutorial I was following to accomplish this:
https://www.youtube.com/watch?v=KdgKvLll07s
You can solve this, by simply creating the lastVisible as a global variable in your class:
private DocumentSnapshot lastVisible;
And then assignin a value in the for loop:
final Task<List<QuerySnapshot>> combinedTask = Tasks.whenAllSuccess(firstTask, secondTask);
combinedTask.addOnSuccessListener(new OnSuccessListener<List<QuerySnapshot>>() {
#Override
public void onSuccess(List<QuerySnapshot> querySnapshots) {
List<Job> list = new ArrayList<>();
for(QuerySnapshot qs: querySnapshots){
for(DocumentSnapshot document : qs){
Job job = document.toObject(Job.class);
list.add(job);
lastVisible = qs.getDocuments().get(qs.size() - 1);
}
}
Query nextQuery = firebaseFirestore
.collection("jobs")
.whereGreaterThan("createdBy",firebaseAuth.getCurrentUser().getEmail())
.orderBy("createdDate",Query.Direction.ASCENDING)
.startAfter(lastVisible)
.limit(2);
//Use the nextQuery
}
});
See the call to .startAfter(lastVisible)? So this will work because at the end of the for loop, the lastVisible object will always hold the value of the last DocumentSnapshot object.
P.S. Thanks for using my tutorial as an example for your Firestore pagination :)
Actually i'm storing map object under my firestore document with some ids of users which i block like this.
i have all members ids which i block in my social app and i need to get all those members document using firestore query but query doesnot work if arraylist has more than one index.but when i have only one id the query works perfect. here is the code
private ArrayList<String> blocks =new ArrayList<>();
if (logedInMember != null){
blocks = logedInMember.getBlocks();
if (!blocks.isEmpty()){
Query query;
CollectionReference collection = firestore.collection(Constants.MEMBERS);
query = collection;
for (int i = 0 ; i< blocks.size(); i++){
Log.d("block member ID :", blocks.get(i) );
// now its time to query all these ids
String id = blocks.get(i);
if (!id.isEmpty()){
query = query.whereEqualTo(Constants.ID,id);
}
}
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()){
for (QueryDocumentSnapshot document : task.getResult()) {
Member member = document.toObject(Member.class);
Log.d("Member Id :", member.getId());
Log.d("Member Name :", member.getName());
}
}else {
// loader.dismissProgress();
Log.d("error : ","fail to load query");
}
}
});
}
}
query doesnot work if arraylist has more than one index.but when i
have only one id the query works perfect
try this
// RETRIEVING ALL Queries
Query allQueries = firebaseFirestore
.collection("yourcollection")
.orderBy("timestamp", Query.Direction.DESCENDING)
.limit(5);
allQueries.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
for (DocumentChange doc : documentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED) {
// get all ids
String postId = doc.getDocument().getId();
contentProfileDashboard = doc.getDocument().toObject(ContentProfileDashboard.class);
contentListDashboard.add(contentProfileDashboard);
// fire the event
adapterProfileDashboard.notifyDataSetChanged();
}
}
}
});
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 ).