Dportology
Dportology

Reputation: 808

android firebase query based on query

I am trying to pull a list of friend IDs from my firebase database into my app, and then look up any additional data associated with the player (such as username, online status etc..) by looking up their entry under "users" using their unique ID

My schema looks as follows:

{friends
    {
      "Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
        "-KR0aTQGT6pfRfB5qIUz" : {
          "friend_id" : "6vFVAAQfwiYERl03C3lzxdPjnEp2"
        },
        "-KR0aaMAOS3FWOAosBmo" : {
          "friend_id" : "kxrQFVjGv0XUHyV5N764Nq50Q3J3"
        }
      }
    }
}

The first unique ID is the ID of the player, which enables querying their friends list. The child objects of that represent the ID of the relationship, and the friend_id under that shows the other players (the friends) ID

The user schema looks as follows:

{
  "6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
    "emailAddress" : "[email protected]",
    "level" : 1,
    "userName" : "steve"
  },
  "Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
    "emailAddress" : "[email protected]",
    "level" : 1,
    "userName" : "bob"
  },
  "kxrQFVjGv0XUHyV5N764Nq50Q3J3" : {
    "emailAddress" : "[email protected]",
    "level" : 1,
    "userName" : "tim"
  },
  "rNtYvwF8LBhTRM1Wk8ybBJyrFIg2" : {
    "emailAddress" : "[email protected]",
    "level" : 1,
    "userName" : "test account"
  }
}

Now, in my app, I can successfully pull all of the friend_id entries, but am not sure how to then turn around and pull additional information on the friend by using this ID. Ideally I would be able to query each friend one by one by their unique player ID, and populate the friends list fragment I have using a firebaseListAdapter.

This is how I am pulling the friend IDs and populating the list object.

ValueEventListener friendListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                friendIdList.clear();
                for (DataSnapshot messageSnapshot: dataSnapshot.getChildren()) {
                    String friend_id = (String) messageSnapshot.child("friend_id").getValue();
                    friendIdList.add(friend_id);
                }

            }
            @Override
            public void onCancelled(DatabaseError databaseError) {}
        };
        mDatabase.addValueEventListener(friendListener);

Could anybody help me figure out the best way to pull all of this additional information on the friends once the IDs are received from the initial query?

Upvotes: 1

Views: 591

Answers (5)

pastillas
pastillas

Reputation: 81

Sorry sir, I dont know if it will work on FirebaseUI but here is my code that I think has the same problem and also same solution, it will display the list of a user followers then if a follower clicked it will open and pass the id of item that is clicked to other activity. I did not use FirebaseUI.

public class FollowersAdapter extends RecyclerView.Adapter<FollowersAdapter.FollowersHolder>{
public ArrayList<Follower> followers;

public void addFollower(Follower follower) {
    followers.add(follower);
    notifyItemInserted(getItemCount());
}

public FollowersAdapter(Context context) {
    this.followers = new ArrayList<>();
    mDatabase = FirebaseDatabase.getInstance().getReference();
    this.fUser = FirebaseAuth.getInstance().getCurrentUser();

    mDatabase.child("follower").child(fUser.getUid()).addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            Follower follower = dataSnapshot.getValue(Follower.class);
            addFollower(follower);
        }

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

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {
            Follower follower = dataSnapshot.getValue(Follower.class);
        }

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

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
}

@Override
public FollowersHolder onCreateViewHolder(ViewGroup parent, int position) {
    View view = inflater.inflate(R.layout.followers_model, parent, false);
    FollowersHolder holder = new FollowersHolder(view);
    return holder;
}

@Override
public void onBindViewHolder(final FollowersHolder holder, final int position) {
    Query userQuery = mDatabase.child("users").child(followers.get(position).getId());
    userQuery.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            user = dataSnapshot.getValue(User.class);

            holder.name.setText(user.getName());

            final Bundle extras = new Bundle();
            extras.putString(EXTRAS_POSTER_ID, user.getId());

            holder.cont.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (!fUser.getUid().equals(user.getId())) {
                        Intent intent = new Intent(context, ViewUserProfile.class);
                        intent.putExtra(EXTRAS_BUNDLE, extras);
                        context.startActivity(intent);
                    }
                }
            });
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
}

@Override
public int getItemCount() {
    return followers.size();
}

class FollowersHolder extends RecyclerView.ViewHolder {
    View cont;
    TextView name;

    public FollowersHolder(View itemView) {
        super(itemView);

        cont = itemView.findViewById(R.id.cont_followers);
        name = (TextView) itemView.findViewById(R.id.tv_followers_name);
    }
}

}

you get the data using childEventListener then on the onBindViewHolder or populateView add onClickListener and pass the id when it clicked to other activity. sorry sir im a beginner lol :)

Upvotes: 1

Kevin O&#39;Neil
Kevin O&#39;Neil

Reputation: 1421

So I'm just going to connect the answers given by Mathew Berg and pastillas. Based on your question and comments I think combined they provide the solution you are looking for.

Data Structure

The structure you are using for your users location looks good to me so I'd say you can leave that as is:

{
    users: {
       "6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
            "emailAddress" : "[email protected]",
            "level" : 1,
            "userName" : "steve"
        },
       "Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
            "emailAddress" : "[email protected]",
            "level" : 1,
            "userName" : "bob"
        }

    }
} 

For you friends location I agree with the structure given by Mathew Berg:

{
    friends : {
        "Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
            "6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
            "kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
        }
    }
}

Just a quick FYI, you don't need to use a Boolean value with this structure. You can use any allowed data type as the value for each key. In this instance the key is what's important, the value is just a place holder because Firebase doesn't allow keys with null values. That said if you find a reason to use a value that is more useful you could do that. In my opinion using a Boolean value makes the structure a little more readable by team members or someone who may follow behind you.

Retrieving Your Data

There are multiple ways you can query a friends list and get the data for each user. Since you referred to using a FirebaseListAdapter in your question I'll go with that. The same pattern can be used with a FirebaseRecyclerAdapter if you want to use choose to use a RecyclerView with FirebaseUI instead. The basic steps are:

  1. Create a DatabaseReference that points to the location of the user who's friends list you want.

  2. Create a DatabaseReference that points to your users location

  3. Create your FirebaseListAdapter.

  4. When you override populateView get the key for each item

  5. Use a addListenersingleValueEvent() to retrieve each friends user information and populate your listview row items with the data from this query.

  6. Within onDataChange of value event listener use the info for each user to populate views for each list item.

DatabaseReference mFriendsRef = FirebaseDatabase.getInstance().getReference().child("friends").child(someUserID;

DatabaseReference mUsersRef = FirebaseDatabase.getInstance().getReference().child("users");

...

mAdapter = new FirebaseListAdapter<YourModel>(this, YourModel.class, android.R.your_layout, mFriendsRef) {
    @Override
    protected void populateView(final View view, YourModel model, int position) {
        String key = getRef(position).getKey();
        mUsersRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        //get data from dataSnapshot and bind to views for each list item 
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
        });     
    }
};

messagesView.setAdapter(mAdapter);

Check out the comment made on this question for concerns about performance when using this approach: Android Firebase - Denormalized Queries using FirebaseUI

Upvotes: 1

Mathew Berg
Mathew Berg

Reputation: 28750

I'd suggest a different data structure. Here it is based on your data:

{
    friends : {
        "Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
            "6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
            "kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
        }
    }
}

Then loop over each one of them and pull up their individual profiles.

Upvotes: 2

pastillas
pastillas

Reputation: 81

You can use ChildEventListener to get all friends then on your populateview or onBindViewHolder use ValueEventListener to get all data of a friend. also try to use FirebaseRecyclerView instead of FirebaseListView

Upvotes: 3

OS Idris
OS Idris

Reputation: 99

Yes agreed even I have same confusion. What are we struggling is with Contains[*, *, *] query.

Even I have a similar problem

/Users{
  "uuid1":{
    data1:"value1"
    data2:"value2"
  }
  "uuid2":{
    data1:"value1"
    data2:"value2"
   }
}

/Friends:{
  uuid1:{
     "uuid2":true
  ....
  }
  uuid2:{
    "uuid1":true
  ...
  }
}

My Query is how to Query list of only my friends and present their data("/User") with FirebaseRecyclerViewAdapter.

Upvotes: 2

Related Questions