OrderByChild adding one more element to the result - android

I'm trying to get just the events that a certain user has, the thing is that after doing orderByChild() it's filtering one more event that I don't want.
The code
//I find the user that belongs to a certain Tutor
mDatabase.child("tutorjugadores").child(mAuth.getCurrentUser().getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshotJugador) {
for(final DataSnapshot snapshotJugadoresTutor : dataSnapshotJugador.getChildren()){
if(dataSnapshotJugador.exists()){
//If the user has not assisted to a certain event, I get the user that belongs to the user and get the event ID
mDatabase.child("asistenciasjugadores").orderByChild(snapshotJugadoresTutor.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshotEvento) {
for(DataSnapshot snapshotEventos : dataSnapshotEvento.getChildren()){
//Getting the event key from that user
String eventKey = snapshotEventos.getKey();
Log.d(TAG, "Event : "+eventKey + " of the user "+snapshotJugadoresTutor.getKey());
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Now, this is my database structure
The problem
Now, when I run this code and execute the log to get the event for the user 2UHHZWR6B8P9XJPuLYrAg6aEgN63, I'm getting one more event that does not belong to that user, that event is this one -LU930PpCaQXT8CvUbc8
My log
Event : -LU930PpCaQXT8CvUbc8 of the user 2UHHZWR6B8P9XJPuLYrAg6aEgN63
Event : -LTRz61EcBUx-RLSSEaU of the user 2UHHZWR6B8P9XJPuLYrAg6aEgN63
Event : -LTSGFGdW1nQbtd9HOBh of the user 2UHHZWR6B8P9XJPuLYrAg6aEgN63
Event : -LTSIG_lqckgHEZ1q9YS of the user 2UHHZWR6B8P9XJPuLYrAg6aEgN63
As you can see, is listing all the events for that user, but that user only has 3 events associated with it.
When I order with this line
.orderByChild(snapshotJugadoresTutor.getKey())
I'm expecting to get just the nodes with the userID 2UHHZWR6B8P9XJPuLYrAg6aEgN63 and then loop and get each event ID from that user, but instead, I'm getting the event ID from this user as well GVD0bLDf41VDdraN67mXjAS6KPk1.

You can't filter with a string by only using JSON based databases like Firebase.
And orderByChild only changes the sorting of the returned values. So you are basically ordering all the values under that key, there is no filtering going on.
After this point you have 3 options to go on.
Option 1 - Not efficient, recommended for only small apps:
You can order all the values and do the filter afterwards.
Option 2 - Recommended:
You can change your data structure. Create another node like "UserEvents". Under it store UID's. And under that UID's there should only be that user's events.
Option 3 - Recommended and functional:
You can use Elasticsearch. It's great for complex queries. You can use it with Firebase with Google Cloud Functions after you switched to " Pay as you go " plan or you can integrate another server with Firebase database by using Flashlight extension.

Related

How to find the count of specific values from Firebase in Android Studio?

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?

Limit the number of user in Firebase Realtime database

I'm developing an app. The app must able to show the latest 10 registered user detail from real-time database. That is, It removes any user older than latest 10 users. Is there any way I can do this? Right now my app is able to access the user details stored in realtime firebase.
Thanks in advance.
That sounds totally feasible. An incredibly simple way is to retrieve 11 users in your app, and then just remove the last one.
ref.orderByChild("descending_timestamp").limitToFirst(11).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int userCount = 0;
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
if (userCount++ > 10) {
userSnapshot.getRef().remove();
} else {
// TODO: show the user in your app
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "load users", databaseError.toException());
}
});
You'll note that I order on descending_timestamp, which is a property that you must add to the data and that allows you to sort the users in reverse chronological order. For more on this, see Firebase Data Desc Sorting in Android

Firebase iterations are slow

I'm using firebase with android to create a simple chat app. When the user chooses another user to chat with I want to check whether they've chatted together or not.
In onCreate() method I'm retrieving all the rooms that the current user used before, and I'm putting them in an arraylist called MyChatRooms<>.
Then I want to check each room to see the users of the room.
The problem is that the loop I'm using to iterate through rooms name is finishing before I'm able to retrieve any data from the database.
I know there's similar questions to mine, but none of the answers worked for me.
Here's the related code:
if (!MYChatRooms.isEmpty()) {
for (j = 0; j < MYChatRooms.size(); j++) {
roomref.child(MYChatRooms.get(j)).child("First User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot1) {
if (!dataSnapshot1.getValue().toString().equals(Username) && dataSnapshot1.getValue().toString().equals(NUsername)) {
Users += dataSnapshot1.getValue().toString() + ",,, ";
} else if (dataSnapshot1.getValue().toString().equals(Username)) {
roomref.child(MYChatRooms.get(j)).child("Second User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot2) {
if (dataSnapshot2.getValue().toString().equals(NUsername)) {
Users += dataSnapshot2.getValue().toString() + ",,, ";
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
I would suggest that you change the structure of your data. Imagine if a user has 100 chats that means your have to query 200 times to Firebase that of course does not look feasible.
What i would suggest is that your add a recentChat list in every user and whenever a user starts a new chat with someone you add the id of the second user to that list. That way you can track easily with whom the current user has interacted with.
It structure in firebase can look something like this:
User
recentChats
id of the other user
Try to change you database hierarchy or use firestore instead of real time database
Please check the following topic: https://firebase.google.com/docs/database/usage/optimize?
In my case, I had added and index column and limited the query in Firebase Rules.

Contactnames from Phonebook with numbers from Firebase

In my Application I am using Firebase to retrieve the mobilnumbers of the Users. Therefore I use this code:
databaseUsers.orderByChild("uid").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
users.clear();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
User contactlists = postSnapshot.getValue(User.class);
users.add(contactlists);
}
ContactList contactAdapter = new ContactList(ContactListActivity.this, users);
listViewContacts.setAdapter(contactAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now I have the following question: If the number of users is high, is there a possibility to send only the mobilnumbers of your Phonebook maybe in a list? Otherwise I think the traffic to Firebase might be not so efficient?!
The other opportunity would be to send each number individually but this might be quite complex if the user has many contacts.
At the moment I get all numbers from the server but I need to filter for the right contacts AND I need to display the names of the contact.
What is the best solution to use Firebase as efficient as possible and also get the names of the contacts?
Thank you in advance!
You'll have to:
Loop through the local phone book to find the phone number of each contact.
Execute a query to Firebase for each number.
Add the resulting contact (if any) to the list/adapter and update the view.
So say you've done step 1 and have a list of phone numbers. You'd then loop through those and for each:
for (String phonenumber: phonenumbers) {
Query query = databaseUsers.orderByChild("phonenumber").equalTo(phonenumber);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User contactlists = postSnapshot.getValue(User.class);
users.add(contactlists);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore erors
}
});
}
The call to notifyDataSetChanged() ensures that the adapter know that it needs to update the view.
While the code gets a bit convoluted, it is not as slow as you may initially fear, since Firebase pipelines the requests over a single connection. The performance will mostly depend on the number of users you have in the database, but up to a few hundreds of thousands this should be fine.

How to pull specific value from firebase

I am working on the app where I need to pull specific value from the firebase. For this case I need to pull "Score" where "uID" equals current User id. What would be a best way to do so.
Thank you for any help.
Firebase will always return full nodes. So you cannot just return the score for a user. But you can return the common node that both the score and user are under (the one starting with -K...) by using a Firebase query:
DatabaseReference leadersRef = FirebaseDatabase.getInstance().getReference("Leaders");
Query query = leadersRef.orderByChild("uID").equalTo("vUdnKx...");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot child: snapshot.getChildren()) {
System.out.println(child.getKey()+": "+child.child("Score").getValue(Long.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "onCancelled", databaseError.toException());
// ...
}
})
Note that the code loops over the snapshot in the result. That is because a query will potentially have multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
For more reading see the Firebase documentation on handling lists and sorting and filtering data.

Categories

Resources