Ilonpilaaja
Ilonpilaaja

Reputation: 1249

Firebase & Filtering ordering by value/key

I have the following structure in Firebase:

enter image description here

I would like to retrieve only the dates falling within a given range, and for that I was using the following return getMealsRefForUser().orderByValue().equalTo("2018-11-17");, where the reference is mealsRefForUser= firebaseDatabase.getReference(MEALS_OF).child(user.getUid());. However, the filtering does not happen. What am I missing?

EDIT: So, I am always getting the /mealsOf/<userId> node, even if I qualify it by orderByKey().startAt().endAt(), although my aim is to get a data snapshot that contains a bunch of nodes whose <year>-<month>-<day> "tag" is within the given range.

EDIT: What I am trying to achieve is to bind LiveData to a query like this:

public FirebaseQueryLiveData( Constraints c ) {
    if ( c == null )
        this.query= FirebaseDatabase.getInstance()
            .getReference("/mealsOf")
            .child(FirebaseAuth.getInstance().getCurrentUser().getUid());
    else
        this.query= FirebaseDatabase.getInstance()
            .getReference("/mealsOf")
            .child(FirebaseAuth.getInstance().getCurrentUser().getUid())
            .orderByKey().startAt(c.dayFrom).endAt(c.dayTo);
}

But this ends up giving me the entire list, all the same -- regardless of the constraints. The whole source code of the class:

// https://firebase.googleblog.com/2017/12/using-android-architecture-components.html
public class FirebaseQueryLiveData extends LiveData<DataSnapshot> {

    private static final String TAG = "FirebaseQueryLiveData";
    private boolean listenerRemovePending= false;
    private final Handler handler= new Handler();

    private final Runnable removeListener= new Runnable() {
        @Override
        public void run() {
            query.removeEventListener(listener);
            listenerRemovePending= false;
        }
    };

    private static final String LOG_TAG= "FirebaseQueryLiveData";

    private final Query query;
    private final InnerValueEventListener listener= new InnerValueEventListener();

    public FirebaseQueryLiveData( Constraints c ) {
        if ( c == null )
            this.query= FirebaseDatabase.getInstance()
                .getReference("/mealsOf")
                .child(FirebaseAuth.getInstance().getCurrentUser().getUid());
        else
            this.query= FirebaseDatabase.getInstance()
                .getReference("/mealsOf")
                .child(FirebaseAuth.getInstance().getCurrentUser().getUid())
                .orderByKey().startAt(c.dayFrom).endAt(c.dayTo);
    }

    public FirebaseQueryLiveData( DatabaseReference ref ) {
        this.query= ref;
    }

    @Override
    protected void onActive() {
        //Log.d(LOG_TAG, "onActive");
        if ( listenerRemovePending )
            handler.removeCallbacks(removeListener);
        else
            query.addValueEventListener(listener);
        listenerRemovePending= false ;
    }

    @Override
    protected void onInactive() {
        //Log.d(LOG_TAG, "onInactive");
        handler.postDelayed(removeListener,2000);
        listenerRemovePending= true ;
        //query.removeEventListener(listener);
    }

    private class InnerValueEventListener implements ValueEventListener {
        @Override
        public void onDataChange( @NonNull DataSnapshot dataSnapshot ) {
            setValue(dataSnapshot);
        }
        @Override
        public void onCancelled( @NonNull DatabaseError databaseError ) {
            Log.e(LOG_TAG, "Can not listen to query " + query,databaseError.toException());
        }
    }
}

I think I know what is happening. I have this piece in my code: viewModel= ViewModelProviders.of(this, new CustomViewModelFactory(c)) .get(MealListViewModel.class);

It is not firing when I supply new query constraints. Does it mean that I can get only one viewmodelprovider per run of my application?

EDIT: In the above updated image, under the meals child of a date I have the division by hours -- e.g. 20 being 8PM etc. How would I restrict my query from both axes -- days and hours? Before I hear (I must admit appropriate) comments that I need to restructure my database schema, I would like to know if that is possible. Would we need something along the lines of the following?

 return await dbroot.ref(`mealsOf/${userId}`)
        .orderByKey().startAt(dayFrom).endAt(dayTo)
        .orderByChild("meals")
        .startAt(`${hourFrom}:00`).endAt(`${hourTo}:59`)

Or alternatively, should I keep another location such as duplicate/{userId}/{day}/{hour} to enable queries such as

return await dbroot.ref(`duplicate/${userId}`)
            .orderByKey().startAt(dayFrom).endAt(dayTo)
            .orderByKey()
            .startAt(`${hourFrom}:00`).endAt(`${hourTo}:59`)

Upvotes: 0

Views: 671

Answers (1)

sketchthat
sketchthat

Reputation: 2688

You shouldn't be using a query to grab that key. Queries are used if you want a list returned, or if you don't know the exact path you're trying to reach.

As you know the uid and the date you can query the object directly.

JavaScript:

getMealsRefForUser(userId, date) {
  return firebase.database().ref(`/mealsOf/${userId}/${date}`);
}

Android:

DatabaseReference root = FirebaseDatabase.getInstance().getReference("mealsOf");
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
return root.child(uid).child("2018-11-17");

Otherwise if you want to query you should use the startAt or endAt methods.

JavaScript:

getMealsRefForUser(userId, startDate) {
  return firebase.database().ref(`/mealsOf/${userId}`).orderByKey().startAt(startDate);
}

Android:

return root.orderByKey().startAt("2018-11-17");

Upvotes: 2

Related Questions