2

Today I'm experiencing an issue with Firebase to make a query such as: "Give me all the items of a user having that specific UID (user unique id)"

The items are added using the push method which creates a Unique ID. Is there some way to bypass the Unique ID for such a request?

Any help would be higly appreciated.

Thanks a lot in advance,

Firebase Structure: current firebase structure

Current code:

//New firebase instance
Firebase mFirebase = new Firebase(Constants.FIREBASE_ITEMS_URL);

//Query on records to find items related to users only
mQuery = mFirebase.child(uid).orderByChild(uid).equalTo(uid);

        mQuery.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                getUpdates(dataSnapshot);
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                getUpdates(dataSnapshot);
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                getUpdates(dataSnapshot);
            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {
                getUpdates(dataSnapshot);
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.i(LOG_TAG, String.valueOf(firebaseError));
            }
        });
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Isabelle
  • 1,457
  • 5
  • 19
  • 44
  • I do not know how to formulate the query so that it returns all items of user having a defined uid. I do not know how to reach the uid field. So far I have this: mQuery = mFirebase.child("items").orderByChild("uid").equalTo(uid); but it returns an error which makes sense. Do you have any clue on how you would make the request? Thanks a lot, – Isabelle May 12 '16 at 14:04
  • 1
    You're treating firebase too much like a relational database that you can perform queries on, you need to flatten out all your data, this may lead to duplication of data but what you loose in duplication you make up for in query speed -- see my answer, if that doesn't help feel free to ask more – Grant May 12 '16 at 14:07
  • I'm investigating a bit to find the way to formulate the query that would look like this: mQuery = mFirebase.child("items").child(generatedID).child(uid); I definitely have no clue on how to tell to Firebase that one of my child is a generated unique ID. I would totally understand a "I don't understand your question" from your side ;) – Isabelle May 12 '16 at 14:35
  • To tell firebase that one if your children is a generated unique Id, you would only need to do the following: (this bit sets your item) `var newItemRef = itemRef.push({ itemData or w.e })` and then go `userRef.child('items').child(newItemRef.key()).set(true);` – Grant May 12 '16 at 14:43

4 Answers4

2

The best way I can see to accomplish "Give me all the items of a user having that specific UID (user unique id)" would be to modify your user firebase structure so it's as follows:

{
  users: {
    user1: {
      user_details { //whatever you need in here// }
      items: {
        -KHZlfskXT_aQtVOZytd: true,
        item1: true,
        item2: true,
        item3: true
      }
    }
  }
}

You can then query against that and get a list of all the itemIds for each user, then you can use that list of itemIds to retrieve the corresponding items -- To make a request with this layout: (i've written it in javascript)

var userItemsRef = mFirebase.child('users').child(uid).child('items')
var itemsRef = mFirebase.child('items');

userItemsRef.on('child_added', function(dataSnap) {
  itemsRef.child(dataSnap.key()).once('value', function(snap) {
    // do whatever you need to with the item
  });
});

For future reading on this type of thing, check out this article on denormalising your data in firebase

Grant
  • 446
  • 6
  • 24
  • Thanks Grant, I need to investigate this further; it's a bit hard for me to understand the purpose of fields/values like: someItemId: true and nextItemId: true; and you wouldn't generate Unique Ids for nodes like "user1"? Thanks a lot, – Isabelle May 12 '16 at 14:21
  • 1
    A push id that gets created by firebase is a unique identifier, when I write things like someItemId, nextItemId, they are place holders for the push ids that would appear there, i.e they are item1, item2, item3 -- I would recommend you keep using firebase push ids as they aren't just unique they also act as a form of timestamp due to the way they are generated – Grant May 12 '16 at 14:23
  • I will try this straightaway – Isabelle May 12 '16 at 14:44
1

I'm definitely with Grant that it's probably better to store the items per user.

But as far as I can see, you should also be able to get the items in the current structure with:

String uid = "bd4dd...a670";
Firebase firebase = new Firebase("https://<app-name>.firebaseio.com");
Query query = firebase.child("items").orderByChild("uid").equalTo(uid);

Queries with Firebase sort-of "skip a level". So the above says: order all items by their uid and then give me the ones for the indicated user.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you Frank for your answer. I guess that I will update my Firebase structure then. I will try your code so I can get it "working", but will definitely update my structure since it's not too late. – Isabelle May 12 '16 at 16:34
  • So I tried again and again, and I keep having the same error. It is said that: uid: No such instance field: 'uid' and same for mQuery. Since my path is /items/generated_id/uid, I really don't see what to put. Your help would be highly appreciated. – Isabelle May 12 '16 at 17:44
  • The `equalTo(uid)` is where you put the uid of the user whose items you want to select. Both that and `mQuery` came from your original code. You can declare the latter as a local variable with `Query mQuery`. – Frank van Puffelen May 12 '16 at 17:58
  • Sure, I mean this: (sorry for the formatting :-/ ) //We retrieve the uid in the sharedPreferences `mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); uid = mSharedPreferences.getString(Constants.KEY_SIGNUP_UID, null);` //New firebase instance `Firebase mFirebase = new Firebase(Constants.FIREBASE_URL);` //Query on records to find items related to users only `mQuery = mFirebase.child("items").orderByChild("uid");` – Isabelle May 12 '16 at 18:02
  • Godness... it worked since a looong time ago; I mean just after you shared the query with me... I focused on the query and the problem was elsewhere. Thanks again for your assistance! – Isabelle May 12 '16 at 18:10
1

Beware that for some unknown reason, orderByChild("X").equalsTo("Y") will sometimes consistently return incomplete or null results.

When I ran into this problem, setPersistence(false) returned the correct results (I spent 2 hours trying to understand why Firebase was only returning a single result by uid value, in a list where all elements were using the same uid). However, when I restored the code to setPersistence(true), the problem returned.

So I setPersistence(false), uninstalled then reinstalled the app with setPersistence(true), and it fixed the problem (for now).

This is a serious issue, since having setPersistence(true) saves a lot of bandwidth. I'm still unsure how to fix it permanently.

Further reading: https://github.com/invertase/react-native-firebase/issues/13

UPDATE:

See this link for a better explanation and a solution: Firebase Offline Capabilities and addListenerForSingleValueEvent

I implemented the recommendations in the link, namely to use addValueEventListener instead of addListenerForSingleValueEvent, and it fixed the problem!

Still, if you're worried about Firebase making endless queries on the data instead of just a single query, just make sure to remove listeners when you got all your data or when you exit your fragment/activity, by using mDatabaseReference.removeEventListener([yourEventListener]).

BarLr
  • 139
  • 1
  • 5
0
FirebaseAuth mAuth = FirebaseAuth.getInstance();
    //getting the current user id
    String uid = mAuth.getCurrentUser().getUid();
    DatabaseReference dbroot = FirebaseDatabase.getInstance().getReference().getRoot();
    DatabaseReference items = dbroot.child("items");
    //firebase query
    Query  q = items.orderByChild("uid").equalTo(uid);
Arjun Thakkar
  • 113
  • 1
  • 1
  • 7
  • 1
    Request you to add some context/commentary around the code you have shared. It will help the asker and other readers to understand your post better. – RBT Dec 14 '16 at 21:53
  • yeah fore sure. actually m new here it will take a while to understand how all this works but thanks for your advice sir :D – Arjun Thakkar Dec 16 '16 at 19:07