Reputation: 808
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
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
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:
Create a DatabaseReference
that points to the location of the user
who's friends list you want.
Create a DatabaseReference
that points to your users location
Create your FirebaseListAdapter
.
When you override populateView
get the key for each item
Use a addListenersingleValueEvent()
to retrieve each friends user information and populate your listview row items with the data from
this query.
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
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
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
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