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" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
},
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : {
"emailAddress" : "bg#b.com",
"level" : 1,
"userName" : "tim"
},
"rNtYvwF8LBhTRM1Wk8ybBJyrFIg2" : {
"emailAddress" : "c#c.com",
"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?
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
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.
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.
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" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"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
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 :)
Related
im working with firebase database. i want to get specific data after searching from "data" node. Below im sharing my database Screenshot and java function that i have already tried.
I'm working on it from two days. but i can't solve it.
please help me.
Thanks in advance.
private void getSearchedData(String selectedItem) {
reference = FirebaseDatabase.getInstance().getReference().child("Category");
Query query = reference.orderByChild("data/dataName").equalTo(selectedItem);
ArrayList<ChannelData> channelDataArrayList = new ArrayList<>();
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot requestSnapshot : snapshot.getChildren()) {
DataSnapshot channelsSnapshot = requestSnapshot.child("data");
for (DataSnapshot ds : channelsSnapshot.getChildren()) {
ChannelData channelData = new ChannelData(ds.child("dataName").getValue(String.class),
ds.child("dataImage").getValue(String.class));
channelDataArrayList.add(channelData);
searchAdapter adapter = new searchAdapter(channelDataArrayList, getApplicationContext());
rvSearchedChannel.setAdapter(adapter);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(MainActivity2.this, "Error: " + error, Toast.LENGTH_SHORT).show();
}
});
}
And this is my database structure. i want to get "dataImage", "dataName" and "dataLink" after searching "dataName".
if you are using edit text for as a search box. add addTextChangedListener on edit text and you will get 3 methods on afterTextChanged method creat a function and pass it editable.tostring you get it in function.
and function code is :
FirebaseDatabase.getInstance().getReference().child("Category").child("data/dataName").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChildren())
{
list.clear();
for (DataSnapshot dss : dataSnapshot.getChildren())
{
Register register = dss.getValue(Register.class);
if (register.getUsername().toLowerCase().contains(s.toLowerCase())){
list.add(register);
}
if (Objects.equals(register.getId(), FirebaseAuth.getInstance().getCurrentUser().getUid())){
list.remove(register);
}
}
SearchUserAdapter adapter = new SearchUserAdapter(list, getContext());
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
search_rv.setLayoutManager(linearLayoutManager);
search_rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Queries in the Firebase Realtime Database search the direct child nodes of the path you run them at for a value at a fixed path that you specify.
In your example, the path of the value in the JSON is not fixed as you have relevant nodes in data/01/dataName and data/02/dataName, which isn't possible. Since the path that you order/filter on (data/dataName) doesn't even exist under Category/01, so you won't get any results.
If you want to allow this query on the database, you'll need to change the data structure (or add an additional one) where the values you want to order/filter on are at a fixed path under the direct child nodes of the path you query. So you'll have to lose a level of your JSON structure, but can keep the information by for example duplicating the categoryName value into each child node:
CategoryData: {
"0101": {
CategoryName: "Pakistani",
dataName: "News",
...
},
"0102": {
CategoryName: "Pakistani",
dataName: "Dramas",
...
},
"0201": {
CategoryName: "India",
dataName: "news",
...
},
"0202": {
CategoryName: "India",
dataName: "movies",
...
}
}
My Firebase Realtime Database is like this :
{Singer :
Billie Eilish :
01 :
songType : "type01"
songName : "bad guy"
02 :
songType : "type02"
songName : "bury a friend"
Lauv :
01 :
songType : "type01"
songName : "I Like Me Better"
02 :
songType : "type03"
songName : "lonely"
Anne Marie :
01 :
songType : "type02"
songName : "2002"
...
...
...
}
If I want to get all the song that "type01", what should I do? This is my Adapter class that show the data in recyclerView in MainActivity.
Adapter class :
public class MyAdapter extends Recycler.Adapter {
...
#Override
public void onBindViewHolder(#Nonnull ViewHolder holder, int i) {
...
Query query = FirebaseDatabase.getInstance().getReference().child("Singer");
options = new FirebaseRecyclerOptions.Builder<ItemModel>().setQuery(query, new SnapshotParser<ItemModel>() {
#NonNull
#Override
public ItemModel parseSnapshot(#NonNull DataSnapshot snapshot) {
if (snapshot.child("songType").getValue().toString().equals("type01") {
String song = snapshot.child("songName").getValue().toString();
return new ItemModel(song);
}
return null;
}
}).build();
recyclerAdapter = new AnotherAdapter(options);
recyclerAdapter.startListening();
}
I think it is weird in the query. It called only the child "Singer". I want to call all the child and get what songType is "type01". AnotherAdapter is just extending FirebaseReyclcerAdapter and just set the text in its AnotherViewHolder.
why don't you just go with fetch data which contains only songType : type01 try this
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference.child("Singer").orderByChild("songType").equalTo("type01");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot issue : dataSnapshot.getChildren()) {
//set featched value to recyclerview
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Since you're passing a reference to Singers into the recycler view, your parseSnapshot is called for each singer. In there you need to make sure you check the songType of each song child of the singer, where now you check the songType of the singers themselves (which never exists, so never goes into the if block).
So it'd be something like this:
Query query = FirebaseDatabase.getInstance().getReference().child("Singer");
options = new FirebaseRecyclerOptions.Builder<ItemModel>().setQuery(query, new SnapshotParser<ItemModel>() {
#NonNull
#Override
public ItemModel parseSnapshot(#NonNull DataSnapshot snapshot) {
String song = "No song found";
for (DataSnapshot songSnapshot: snapshot.getChildren()) {
String songType = songSnapshot.child("songType").getValue(String.class)
if (songType.equals("type01") {
song = songType;
...
I'm not sure though if this is what you want, because you seem to want a list of songs, while you're passing in a list of singers. The above will only work if each singer has exactly one song of type01 (not 0, not more).
If that is not the case, the adapters in FirebaseUI won't fit your needs with your current data structure, since they show a list of items from the database, while you want to show items from a tree-like structure.
The two main options that come to mind in that case:
Build your own adapter, typically based on ArrayAdapter as shown here.
Change your data structure so that all songs are in a flat list (and the singer then becomes a property of each song).
this is my first question in Stackoverflow.
I use firebase in my android app, and my DB schema is as follows:
Users schema:
and
Posts schema:
Now, I have used a recyclerview to show the posts by users. I have implemented SingleValueEventListener where I get list of Posts by users and another eventlistener nested inside to fetch the User's name and profile picture.
The code is as follows:
Query query = databaseReference.child("Posts");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot data : dataSnapshot.getChildren())
{
final PostsPOJO postsPOJO = data.getValue(PostsPOJO.class);
images = postsPOJO.getcontent_post();
//Nested listener to fetch User's name and profile picture from another node "Users/UserID"
final Query userDetails = databaseReference.child("Users/"+postsPOJO.getUserID());
mListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot1) {
String username = dataSnapshot1.child("Username").getValue(String.class);
String profilePicturePath = dataSnapshot1.child("ProfilePicture").getValue(String.class);
list.add(new PostsPOJO(postsPOJO.getUserID(),profilePicturePath ,username, postsPOJO.getTimestamp(),postsPOJO.getPostText(),postsPOJO.getLocation(),postsPOJO.getcontent_post()));
Log.d("datalist", postsPOJO.getUserID()+","+profilePicturePath +","+username+","+postsPOJO.getTimestamp()+","+postsPOJO.getPostText()+","+postsPOJO.getLocation()+","+postsPOJO.getcontent_post());
newsFeedListAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
userDetails.addListenerForSingleValueEvent(mListener);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now, the problem is that the first UserID which the nested listener gets, it fetches all the Posts from that particular UserID first and then goes on to the next UserID for example if it gets UserID : 1, then it will give all the posts from that User first and then go on to the next one, if its UserID : 2, vice versa. I want the Posts data by the UserID associated with that post.
I have implemented .orderByKey() but no success.
Thank you.
You can put post Ids under user data like
Users -> 1 -> Posts -> [postId-1,postId-2,postId-3]
I have a firebase database architecture set up like the docs recommend (data fan out). I have a node with all "athletes" and another node with "teams." I'd like to retrieve the athlete's information from the "athletes" node based on the Athlete's ID inside the "teams" node.
My code looks like this:
if (eventListener == null) {
eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mAdapter.clear();
athleteCount = 0;
final List<Athlete> athletes = new ArrayList<>();
final long childrenCount = dataSnapshot.getChildrenCount();
if (childrenCount == 0) {
hideProgressBar();
}
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String key = ds.getKey();
athleteCount++;
if (!TextUtils.isEmpty(key)) {
mDatabaseReference.child(Athlete.ATHLETE).child(key)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Athlete athlete = dataSnapshot.getValue(Athlete.class);
if (athlete != null) {
athletes.add(athlete);
if (athleteCount == childrenCount) {
Collections.sort(athletes, new Athlete.AthleteComparator());
mAdapter.setRoster(athletes);
hideProgressBar();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mDatabaseReference.child("teams")
.child("-KnyqjH0acSol5cxHLM1")
.child("athletes")
.addValueEventListener(mValueEventListener);
Now this code does work fine, but I do think it's a little over the top to keep adding a listener, then populating my adapter after keeping track that the singleValueListeners have completed. There must be an easier way to do this. I'm asking because I'm also going to need to retrieve other data from another node and that's going be very messy.
If anyone knows a better way please let me know. Thank you.
Here's an image of my architecture:
I can think of a few approaches:
Extract the code that gets the athlete into a helper method, either with its own callback, or by passing the adapter (or view) along.
Preload all the athletes and then just do a direct lookup.
Duplicate the data that you immediately need about each athlete under the team node. Frequently this is just their name, so you'd have $athleteId: "Athlete's name".
There might be more options, but these are my typical go to's.
Oh btw, the key will never be empty, so this check (and indentation level) is not needed: if (!TextUtils.isEmpty(key)) {.
I am learning to use Firebase and want to know if I am doing it right. If I understood correctly, you can only retrieve data asynchronously using a listener in Firebase. I will try to explain my question with an example. Say I have the following database data for a simple chat application:
chat_info:
chatID_1:
participants:
uId_1: true
uId_2: true
users:
uId_1:
display_name: "David"
participated_chats:
chatID_1: true
chatID_2: true
uId_2:
display_name: "Jason"
participated_chats:
chatID_1: true
chatID_2: true
Now, I am trying to list the chats that David is participated in. So I do something like the following:
ArrayList<String> chatIdList = new ArrayList<String>();
// Retrieve chat Ids of participating chats
usersRef.child(mFirebaseUser.getUid()).child("participated_chats").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
chatIdList.clear();
// Save each chat Id to an arraylist
for (DataSnapshot child : dataSnapshot.getChildren()) {
chatIdList.add(child.getKey());
// when loop hits the last user of the dataSnapsot
if(chatIdList.size() >= dataSnapshot.getChildrenCount()) {
// For each chat Id, retrieve participants' uIds
for(String chatId : chatIdList) {
chatInfoRef.child(chatId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Chat chat = dataSnapshot.getValue(Chat.class); // In a Chat class, there is public Map<String, Boolean> participants = new HashMap<>();
chatDetailList.add(chat);
chatListAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now, I have participants' uIds for each chat that a user is participated in. But, since I want to display the username, not the uId, I have to retrieve data from another node again. Here is my worry because I have to add another asynchronous listner to retrieve data from different node. If it was something like MySQL, it would not be a problem, but Firebase listener is asynchronous. This idea of asynchronous listener to retrieve data is very confusing and wonders if I am doing it right. What should I do here?
You can just attach the first listener to the /users/uId_1 to get the whole user object, and then you can simply get the user's username / display name from the dataSnapshot value.
Here's an example.
ArrayList<String> chatIdList = new ArrayList<String>();
// Retrieve chat Ids of participating chats
usersRef.child(mFirebaseUser.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
chatIdList.clear();
User user = dataSnapshot.getValue(User.class);
String username = user.getDisplay_name();
Map<String, Boolean> participated_chats = user.getParticipated_chats();
// Save each chat Id to an arraylist
for (Map.Entry<String, Boolean> child : participated_chats.entries()) {
chatIdList.add(child.getKey());
// ... continues
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});