0

I'm struggling with the Firestore because there is not enough documentation which can describe all the possible working scenario of Firestore.

Let me explain my requirement.

I have the following GUI in android:

  • EditText. (Name)
  • Spinner. (cities)
  • Spinner. (Busses)
  • Add Button

The Firestore have the collections as the following:

  • cities (which contain the multiple cities)
  • busses (which contain the multiple busses)
  • user ( which will contain the username, buss reference, city reference)

Now the problem is that when user click on the Add Button, first I have to get the reference of selected city and selected bus then add the these three values to user collection but to get the reference I have to call the following code.

final Map<String, Object> data = new HashMap<>();
        data.put("username", editText.getText().toString());

        db.collection("cities")
        .whereEqualTo("name", city) // get city by name
        .get()
        .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
            @Override
            public void onSuccess(QuerySnapshot query) {
                data.put("city", query.getDocuments().get(0).getReference());
                // once the city reference got, then get the Buss reference
                db.collection("busses")
                .whereEqualTo("busnumber", bus) // get bus by number
                .get()
                .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                    @Override
                    public void onSuccess(QuerySnapshot query) {
                        data.put("bus", query.getDocuments().get(0).getReference());
                        addData(data); // now the city and bus reference got, add the data now.
                    }
                });
            }
        });

This make the app little slow, and looks like stupid approach, I want to confirm is it the correct approach? or is there better way to do this? or in future may be I have to get the more reference then more nested calls?

Edited

Further more this is really stupid way that I have to recall these code again and again to get the reference of city or bus so a lot of duplication will be in my application, which is not good. So I want to create two methods getCityRefByName(String name) and getBusRefByNUmber(int number) as the following.

public DocumentReference getBusByNumber(int number){
    db.collection("busses")
    .whereEqualTo("busname", number)
    .get()
    .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot query) {
            query.getDocuments().get(0).getReference(); // return this reference, How?
        }
    });
}

public DocumentReference getCityRefByName(String name){
    db.collection("cities")
    .whereEqualTo("name", name)
    .get()
    .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot query) {
            query.getDocuments().get(0).getReference(); // return this reference, How?
        }
    });
}

The Firestore documentation is not good enough that's why I'm facing these stupid core problems.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Asif Mushtaq
  • 3,658
  • 4
  • 44
  • 80
  • If your requirement is that you must add data first to city collection and if its successful then only you need to add data to bus collection then your approach is good because you'll get to have track of whether the first method (citi collection) executed or not and then you may know if second method (bus collection) worked. – Mohammed Farhan Apr 26 '18 at 06:23
  • I'm not adding the data to city or even bus collection, I'm just getting the reference from those collections before adding the data to user collection. – Asif Mushtaq Apr 26 '18 at 06:29

2 Answers2

1

return this reference, How?

You cannot return something now that hasn't been loaded yet. With other words, you cannot simply use that result 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 that result 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 those results only inside the onSuccess() method, 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.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • So my above code is fine as the get the reference of the city and then the bus reference then add to the database? – Asif Mushtaq Apr 26 '18 at 10:44
  • There is no problem regarding nested calls in Firestore. It's a common practice which will help you have access to the data in the upper callback. But remeber to define those lists each inside its corresponding callback. – Alex Mamo Apr 26 '18 at 10:52
  • can you elaborate `access to the data in the upper callback` and `But remeber to define those lists each inside its corresponding callback` – Asif Mushtaq Apr 26 '18 at 16:19
  • When you are using nested calls, one call is a parent call and the other one is a nested call. So you need to declare each list in the corresponding call. First list in the partent call and the second in the nested call. Right? – Alex Mamo Apr 27 '18 at 11:44
  • No I have to wait for all calls to get all required data before adding data in the firestore. – Asif Mushtaq May 04 '18 at 15:59
  • That's correct, this what I have explained in the post that recommened you to read. So do you think that my answer helped you? – Alex Mamo May 04 '18 at 16:01
  • Yes helped to confirm. I have marked as accepted, can you take a look on this similar question? which is related to messing the output due to async behavior https://stackoverflow.com/questions/50191349/how-to-work-with-the-reference-type-in-firestore – Asif Mushtaq May 05 '18 at 16:37
0

I think you should run methods get City and Bus together like this

private boolean gotCity, gotBus;
private void run() {
    final Map<String, Object> data = new HashMap<>();
    data.put("username", editText.getText().toString());
    gotBus= false;
    gotCity = false;
    db.collection("cities")
            .whereEqualTo("name", city) // get city by name
            .get()
            .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                @Override
                public void onSuccess(QuerySnapshot query) {
                    data.put("city", query.getDocuments().get(0).getReference());
                    // once the city reference got, then get the Buss reference
                    gotCity = true;
                    if(gotCity&& gotBus)  addData(data); // now the city and bus reference got, add the data now.
                }
            });
    db.collection("busses")
            .whereEqualTo("busnumber", bus) // get bus by number
            .get()
            .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                @Override
                public void onSuccess(QuerySnapshot query) {
                    data.put("bus", query.getDocuments().get(0).getReference());
                    gotBus = true;
                    if(gotCity&& gotBus)  addData(data); // now the city and bus reference got, add the data now.
                }
            });

}
Nha Phạm Thị
  • 409
  • 4
  • 10