This is how my firebase database looks like.
I want to access the node accepting-status and say patient23 for example. I can easily do this by using addListenerForSingleValueEvent on nodes accepting-status and patient23 separately but then it would take me two different requests to get the required data. I can also get the required data by single request by adding addListenerForSingleValueEvent on the parent node beacon-final but then that would retrieve huge data because the patients are in large numbers.How do I do it in a single request and yet only retrieve only the required nodes. Thanks.
...but then it would take me two different requests to get the
required data.
The Firebase database is all one JSON object. If you select one part, you'll get everything underneath.
But, Firebase uses WebSockets, which is a persistent connection to the server. This means that you don't have to worry about making a request, because the only HTTP request that gets made is in the very beginning to establish the socket.
So in your case it's completely feasible to make two separate "requests" for the data, because there's no real overhead to consider. The device's radio is already on, and a WebSocket header is merely 6 bytes.
You can easily create a listener on /accepting-status, as well as /patients/patient23. This is a standard Firebase practice to have multiple listeners.
// Get a root reference
Firebase rootRef = new Firebase("<my-firebase-app>");
// accepting-status ref
Firebase statusRef = rootRef.child("accepting-status");
// patient23 ref
Firebase patientRef = rootRef.child("patients").child("patient23");
// Listen for status updates
statusRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
// error
}
});
// Listen for patient updates
patientRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
// error
}
});
Related
I have a firebase database consisting of a bunch of cases. I want to loop through all these cases and find the count of Male cases only, male is represented by "M".
Picture of my database.
How I am trying to query for this data:
databseCOVIDCases = FirebaseDatabase.getInstance().getReference();
databseCOVIDCases.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot data : snapshot.getChildren()) {
if (data.child("Sex").getValue(String.class) == "M") {
numMaleCases++;
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) { }
});
When I set the text of the text view it shows 0 and then crashes with an out of memory error.
Instead of looping through all cases and counting the ones where Sex is M, I'd recommend using a query to only read those nodes. That saves you (and your users) the bandwidth of loading all the nodes where Sex is not M.
In code that'd be:
Query query = databseCOVIDCases.orderByChild("Sex").equalTo("M");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.i("Cases", "M case count: "+snapshot.getChildrenCount());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
throw error.toException(); // Never ignore errors
}
});
While the above works, a few things to keep in mind:
Any code that needs the count has to be inside the onDataChange method or be called from there. Other code may not run when you expect it to run.
For more on this, see
getContactsFromFirebase() method return an empty list
and
Setting Singleton property value in Firebase Listener
Reading all these nodes to only show the count it wasteful. It is much more practical (though unusual if you come from a SQL background) to store the actual count you need in the database, and update it whenever you add/remove a M node. While your write operations becomes more complex when you do this, your reads become much simpler, cheaper, and more scalable.
For more on this, see How to get size of an element/list in Firebase without get it all? and Is the way the Firebase database quickstart handles counts secure?
I want to start a Fragment with information I have to get from the Firebase database when I click the button. For the sake of an example, say I want to open a user profile and I need to load all data from database > users > uid AND I need information from database > messages (e.g. the posts of the user).
Now my approach would be this:
(The following code is a part of the #Override public void onClick(View v) { } method of the button that shall start the Fragment)
dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
User user = snapshot.getValue(User.class);
// start the fragment here ...
}
});
But since I need information from database > messages as well, I'm not sure how to do this in a good way. The following would be possible:
(The following code is a part of the #Override public void onClick(View v) { } method of the button that shall start the Fragment)
dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
User user = snapshot.getValue(User.class);
// just add another listener on the other reference
dbRef_2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
MessagesInfo message = snapshot.getValue(MessagesInfo.class);
// start the fragment here ...
}
});
}
});
But this seems not to be the best way to do it since we just got two SingleValueEvent listeners nested. And if we'd need more information, like 10 different database "locations", we'd need to nest 10 of these listeners.
So what is a good way?
Unfortunately, there is no method to fetch multiple locations at once, you could do this with a custom script if you are expecting to re-use it multiple times but it would always result in a loop that fetches each item.
It's also important to note that the order in which the queries were sent is also the order in which the server processes them.
Right now I'm developing an android app, and I just started to work with Firebase.
My question is: How can I retrieve data from the firebase database, without use listeners ?
In my game, I'm saving the high scores of all the users, and I need to take the data from the database when user go into "leader-boards" page.
I saw some solutions, which is not good for my case.
One of them is:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
This solution is not good for me, because I cant afford to my app to go into the server every time high score of user is changing, because if I would have 20,000 people who playing at the same time, then the game will stuck.
so I cant use this listener, because it will make the game very slow.
My goal is to to find a way to change the high score, without alerting the other people who is currently playing the game, I mean that I need to update every user score for his own, and when user go to the "leader boards" page, only then I will go to the server.
what is the right solution here ?
Or can I use this listener in another way?
If my question is not clear, then ask me in the comment please.
Thank you !!
my lines:
public static void setUserHighScoreToServer(Context context,boolean isClassic,int scoreNum)
{
com.firebase.client.Firebase mRef;
mRef= new com.firebase.client.Firebase("...");
String name = InternalStorage.getUserName(context);
String classic = "";
if(isClassic)classic="Classic";
else classic="Arcade";
com.firebase.client.Firebase mRefChild = mRef.child(name+classic);
mRefChild.setValue(String.valueOf(scoreNum));
}
This is the OFFICIAL way to retrieve data once without listening for data changes.
// Add all scores in ref as rows
scores.addListenerForSingleValueEvent( new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot child : snapshot.getChildren()) {
...
}
}
}
more information here: https://firebase.google.com/docs/reference/android/com/google/firebase/database/DataSnapshot
If you donĀ“t need to update on real time, you can always do a Rest api call to your database.
Just do a GET call to retrieve your data
https://[PROJECT_ID].firebaseio/[path].json
and you are good to go
You can also update or create new keys using rest api calls.
My app is using Firebase and there are almost 200 users live at the a given time. Most of the users are complaining that the data doesn't load. I was using ChildEventListener for obtaining the data which keep the connection alive and reflects live changes. There is a limit of 100 connections in the free plan. I guess that is the reason my data is not loading at times. After reading the doc I found another way to read data using ValueEventListener. Below is the code I'm currently using
public void getImages() {
Query imagesQuery = FirebaseDatabase.getInstance().getReference().child("englishDps").child(mChildName).orderByKey().limitToLast(21);
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Image image = dataSnapshot.getValue(Image.class);
image.setNodeKey(dataSnapshot.getKey());
mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
if (isAdded()) {
Toast.makeText(getActivity(), "Problem loading more images...", Toast.LENGTH_LONG).show();
}
}
};
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot imageSnapshot : dataSnapshot.getChildren())
{
Image image = imageSnapshot.getValue(Image.class);
image.setNodeKey(imageSnapshot.getKey());
mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
// imagesQuery.addChildEventListener(childEventListener);
// imagesQuery.addValueEventListener(valueEventListener);
imagesQuery.addListenerForSingleValueEvent(valueEventListener);
}
According to the docs
"While using a ChildEventListener is the recommended way to read lists
of data, there are situations where attaching a ValueEventListener to
a list reference is useful.
Attaching a ValueEventListener to a list of data will return the
entire list of data as a single DataSnapshot, which you can then loop
over to access individual children.
Even when there is only a single match for the query, the snapshot is
still a list; it just contains a single item. To access the item, you
need to loop over the result:.
This pattern can be useful when you want to fetch all children of a list in a single operation, rather than listening for additional
onChildAdded events."
I was thinking this will solve the data loading problem but my previous version of the app will still keep using live connection and I'm still seeing random success and failures for data load call in new version of the app with more than 150+ users live right now on old version of the app. What will happen if the old version of the app opens more than 100 connection and the new version of the app tries to load data ? i.e. if 100 connections in the free plan are used will a query with
imagesQuery.addListenerForSingleValueEvent(valueEventListener);
succeed or fail ?
When an Android app first uses the Firebase Database SDK, it makes a connection to the Firebase servers. If there are at that moment already as many connection as are allowed to your database, the new connection will be rejected. The type of listener has no influence on this.
For a lot of discussions covering this already, see this list. Some good ones:
Limitation of free plan in firebase
How the Connection is calculated in Firebase
When are new connections allowed after limit of 100 concurrent connection is reached in firebase?
How exactly are concurrent users determined for a Firebase app?
How to limit concurrent connections on Firebase Android
Having looked at your code. I recommend inserting a closing connection once the read of images from json are completed. In the free package there is a limit of connections so once they read the images, they're technically still connected.
Looking at your Datasnapshot, they don't do anything but still querying the Firebase. I also recommend look into indexing too.
https://firebase.google.com/docs/database/rest/save-data
https://firebase.google.com/docs/database/security/indexing-data
Following situation:
User not registered, I save data while offline (Firebase setOffline())
Cannot read the local data (populate listview etc) - the ValueEventListener and ChildEventListener dont fire
I set setOnline() on Firebase instance
Data is synced with web and displayed (listeners fire)
I set setOffline() again.
I save local data and read local data, works (listeners fire)
Question:
How to read local data stored BEFORE going online?
Scenario is: User uses the android app offline and decides later to register
Scenario 1:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().goOffline(); // <--------NOTE THIS
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(App.get().getUid()).child("items").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// THIS IS NOT FIRING
}
#Override
public void onCancelled(DatabaseError databaseError) {
...
}
});
After Scenario 1 I change code to this and run:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().goOnline(); // <--------NOTE THIS
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(App.get().getUid()).child("items").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// THIS IS FIRING. ALL GOOD
}
#Override
public void onCancelled(DatabaseError databaseError) {
...
}
});
After this I change code to following and it works
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().goOffline(); // <--------NOTE THIS
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(App.get().getUid()).child("items").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// THIS IS FIRING. ALL GOOD
}
#Override
public void onCancelled(DatabaseError databaseError) {
...
}
});
I added 3 segments (code blocks).
I execute first block - does not work
Second block - works
Third block - works
Here is a gist with the code.
Problem is that first block does not work before being online with setOnline()
Since you force the client to go offline in scenario 1 before it has a chance to synchronize any data, I indeed expect it to not fire onDataChange() in that scenario. In the 3rd fragment it will fire, because it has had a chance to synchronize data to the local cache.
But why are you explicitly trying to manage online/offline state? By doing this you're digging a hole that you may find it hard to get out of.
If you want to avoid having the user sign-in, you can start off with Anonymous Authentication and then upgrade that to a email/password or social account later.
Just keep in mind that starting offline and only enabling synchronizing later is not an ideal way of working with the Firebase Database, which is primarily an online database that continues working offline.