I have 2 branches in my JSON tree, which look like this:
users {
profile-data {
user-key {
//here is users profile fields like nickname and other
}
......
}
friends {
user-key {
friend's-user-key:true
another-friend's-user-key:true
......
}
}
}
The problem is to fetch all users-profile data of users, that are friends of any user. I didn't find the way to fetch all this data by 1 query so I need to write something like this:
usersFriendsRef.child("userId").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
String userId = child.getKey();
usersProfileDataRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
////Only here i can get user's profile data
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
So the problem is that I need to make many requests to fetch friends' profile data one by one, so I need to restructure my database or find the way to write this as 1 query
That's the correct way in which you can achieve this. There's no problem with nested queries. You need to find all those id's and then based on them, to query. I could move the friends node under profile-data -> user-key to create a single query but knowing that in Firebase is best to have the data as flatten as possible, it's not a solution.
Keep in mind not to forget to remove the listener, when it's no more needed, like this:
databaseReference.removeEventListener(valueEventListener);
Hope it helps.
Related
CABARAN is an unknown Uid. it is not a text. Right now I have the uid, and I want to get value for the tajukPenuh.
This is the code and I still can't get the value.
FirebaseDatabase.getInstance().getReference().child("karangan").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Karangan karangan = child.child(karanganID).getValue(Karangan.class);
if (karangan != null) {
String tajukPenuh = karangan.getTajukPenuh();
holder.getTextViewKaranganID().setText("Karangan Tajuk: " + tajukPenuh);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
You could in theory do a query like this:
FirebaseDatabase.getInstance().getReference().child("karangan")
.orderByChild("-LYgFIl4Xiv_Ls51Slvh/uid").equalTo("-LYgFIl4Xiv_Ls51Slvh")
.addListenerForSingleValueEvent(new ValueEventListener() {
But the problem is that you'd need a lot of indexes in your rules, which may be technically possible, but is unfeasible for most real usage.
Your current data structure makes it easy to find all the child nodes for CABARAN, but it does not make it easy to find CABARAN for a given child node. To allow that use-case to run efficiently, you should expand your data structure with a so-called reverse index that maps back to CABARAN from the value that you know. So something like:
"myIndex": {
"-LYgFIl4Xiv_Ls51Slvh": "CABARAN",
"-LzfFIl4Xasas51Slads": "CABARAN",
"-Lasddas981398asdh1h": "CASITWO"
}
This is an additional data structure, that you will have to keep up to date when you're writing the rest of the data. But with this structure, it now becomes very easy to determine that -LYgFIl4Xiv_Ls51Slvh maps to CABARAN.
For more on this, see my answer here: Firebase query if child of child contains a value
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'm trying to implement firebase database in my app, but I'm having problems in retrieving data from multiple nodes at same time and updating the ui.
I'm storing all the Firebase methods in a separate class.
here is an example of one of the methods to retrieve one datum from a node.
public static void getTrainerCity(final Trainer trainer) {
dbref.child(USER_CITY).child(trainer.getId()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
trainer.setCity(dataSnapshot.getValue(String.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Then, to get for example one complete trainer, I do
public static void getTrainer(final boolean isAthlete, final String trainerId, Trainer trainer) {
trainer.setId(trainerId);
getTrainerCity(trainer);
getTrainerName(trainer);
getTrainerAge(trainer);
getTrainerNumber(trainer);
getTrainerEmail(trainer);
getTrainerGym(trainer);
getTrainerPhoto(trainer);
getTrainerStudyDegree(trainer);
getTrainerDescription(trainer);
if(!isAthlete) {
getTrainerAthletes(trainer);
}
}
when I call this method in UI, I'm not getting the entire Trainer object in one shot as those methods don't run on the main thread. This means that the trainer is constantly updated. However, I want to notify the UI only when all fields of the object related to the method have been set (I have to populate lists and initialize user). How would you do this?
I thought about attaching listeners, but it seems like I need a tremendous amount of them. Thank you guys
EDIT: This is how Db is structured
It's scenarios like this where RxJava shines.....what you could do is wrap firebase queries in Observable.create() and then use something like RxJava zip operator to combine all the queries. The alternative (to be avoided if possible) is to nest all the queries which leads to dreaded callback hell
For example you might have something like following for getTrainerCity
public static Observable<String> getTrainerCity(int trainerId) {
return Observable.create(subscriber -> {
dbref.child(USER_CITY).child(trainerId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String city = dataSnapshot.getValue(String.class);
subscriber.onNext(city);
subscriber.onCompleted();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
});
}
You should get trainer node first and then parse the fetched data.
I assume you have a Trainer node which contains all the info (city, name, age, ...)
public static void getTrainer(final Trainer trainer) {
dbref.child(trainer_node).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Trainer trainer = dataSnapshot.getValue(Trainer.class);
String name = trainer.getName();
String city = trainer.getCity();
int age = trainer.getAge();
.
.
.
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
EDIT:
Data structure maybe like this
-users_node
--user1
---id:userid
---name: username
---city:usercity
---email:useremail
---workouts
----workoutid1:true
----workoutid2:true
--user2
---id:..
-athletes_node
--user1:true
--user2:true
--userX:true
-workouts_node
--workoutid1
---workout details here
--workoutid2
---workout details here
I'm making a post app with Firebase Realtime Database.
The app needs a bookmark function, but I don't know how to load the bookmarked (selected) post.
Database structure:
Code:
databaseReference = databaseReference.child("post").child(getLen()).child("all-posts").child("-KYbMBatKdoWYw45-2pp").child("bookmarkUsers").orderByChild(myUserId).equalTo(myUserId);
but this code is not working. Why doesn't it work? I think that it only returns bookmarkUsers child node.
Do I have to make another node only for bookmark?
I already made a node only for bookmark, but I could't manage bookmardCount and starCount synchronization.
Thanks!
DatabaseStorage Posts=FirebaseDatabase.getInstance().getReference().child("posts");
Posts.child("0").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot DS: dataSnapshot)
{
//Get all other Values Except bookmarkusers and store them
String POST_NAME = dataSnapshot.getKey();
Posts.child("0").child(POST_NAME)addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshotf) {
for(DataSnapshot BOOK: dataSnapshotf)
{
//Collect all BookMarks and store them
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Do I have to make another node only for bookmark?
Yes, you do. To get more information, read about structuring you data in firebase here.
I already made a node only for bookmark, but I could't manage bookmardCount and starCount synchronization.
You will need to keep the bookmarkCount and starCount inside the post node itself.
I followed the documentation, but no matter what, I cannot figure out how to return all the objects from a single node. For example, I want to return a list of all company objects from the companies node. Once I have that list, I want to parse them all into JSON objects. This is my first time with a NoSQL database so I'm sure that I'm missing something small.
Currently I have:
DatabaseReference companiesRef = FirebaseDatabase.getInstance().getReference("12265");
companiesRef.child("companies").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("Count ", dataSnapshot.getChildren().toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But it just returns null for the value: DataSnapshot { key = companies, value = null }.
Here's my database:
You create your reference like this:
FirebaseDatabase.getInstance().getReference("12265");
This means that Firebase looks at the root of the database and returns the child 12265 from under there. It does not automatically search the tree for a node with a matching name.
So you'll need to specify the entire path:
FirebaseDatabase.getInstance().getReference("android/users/12265");
Don't add any parameters to your getReference() (let it go to the root of database) and then set the addListenerForSingleValueEvent. And you have not used getvalue() on you datasnapshot as well. Try this code:
DatabaseReference companiesRef = FirebaseDatabase.getInstance().getReference();
// this is the patch that I see from the image that you have attached.
companiesRef.child("telenotes").child("android").child("user").child("12265").child("companies").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("Count ", dataSnapshot.getChildren().getValue().toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});