fetch data from firebase having a randomly generated key - java

Below is the code that I am using to save value into firebase. It generates a random key for every value. (I want to save redundant data, so this method was necessary).
GenderOfNameHelperClass GONHC = new GenderOfNameHelperClass();
reference = FirebaseDatabase.getInstance().getReference().child("GenderOfName");
GONHC.setName(Name);
GONHC.setGender(Gender);
reference.push().setValue(GONHC);
This creates a structure as below:
db-ai-34201
-...GenderOfName
-...M4oLyB1ADGLWGJG4EgR
...gender:"Female"
...name: "akira"
Now how can I fetch the data from this.
I want to fetch the gender, when name is searched from my android UI.

This is the listener to the GenderOfName node to extract data under the random keys:
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot snapShot:dataSnapshot.getChildren() ){
//these are the values of gender and name for every random key
//in GenderOfName node
String gender = snapShot.child("gender").getValue(String.class);
String name = snapShot.child("name").getValue(String.class);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
};
Finally attach the listener to your reference of that node to start listening to it
reference = FirebaseDatabase.getInstance().getReference().child("GenderOfName");
reference.addValueEventListener(listener);
UPDATE
you can always use a query to filter results:
instead of this:
reference = FirebaseDatabase.getInstance().getReference().child("GenderOfName");
reference.addValueEventListener(listener);
try this,it will give you a specific snapshot based on the name you need:
Query query = FirebaseDatabase.getInstance().getReference().child("GenderOfName").orderByChild("name").equalTo(theNameYouWant);
query.addValueEventListener(listener);

Related

How to update entry in Firebase Database based on an attribute value

I am using the Firebase Realtime Database with Android in Java. I have the following database screenshot:
I would like to change the availability value (from 0 to 1) for the ingredient with the attribute "ingredient_name = Lime". The attribute ingredient_name is actually something like a primary key meaning that there will be no other database entry with this specific name.
I tried the following code
DatabaseReference rootRef;
rootRef = FirebaseDatabase.getInstance("https://....app").getReference();
String ingredientToBeUpdate = "Lime";
rootRef.child("ingredients").orderByChild("ingredient_name").equalTo(ingredientToBeUpdate).child("availability").setValue(1);
But I get the error "Cannot resolve method 'child' in 'Query'". Can you tell me how to do this update properly? So I would like to update the value from the database entries who attribute "ingredient_name" is equal to a certain string ingredientToBeUpdate.
Firebase doesn't support so-called update queries, where you send a condition and the new data to the database and it them writes the new data for all nodes matching the condition.
Instead you will need to execute the query in your application code, loop through the results, and update each of them in turn:
rootRef
.child("ingredients")
.orderByChild("ingredient_name")
.equalTo(ingredientToBeUpdate)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ingredientSnapshot: dataSnapshot.getChildren()) {
ingredientSnapshot.getRef().child("availability").setValue(1);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
Also see:
Firebase Android, set value where x = x?
Is it possible to update a specific child's value without ID or key in firebase realtime database from android on button click?

Is there any way to only proceed once I have obtained data from Firebase in Android?

I am working on an app for a hotel, which enables hotel management to report and view concerns and issues. I am using Android and Firebase for this app.
Here is the database structure of a reported concern:
To minimize data download and optimize speed, I am adding "Active" and "Resolved" nodes in the database, like below:
Now, the hotel wants me to add the function to create an Excel report of concerns closed/resolved within the past month. For this, I will be attaching a Single Value Event Listener on the "resolved" node, get keys of resolved concerns, then for each key, fetch data from "allConcerns" node, store each node's data into an ArrayList of String. After which I will use this JSON to Excel API for Android to create Excel file.
I am able to access keys of resolved concerns with this code:
DatabaseReference resolvedReference = FirebaseDatabase.getInstance().getReference()
.child(getApplicationContext().getResources().getString(R.string.concerns))
.child(getApplicationContext().getResources().getString(R.string.resolved));
final ArrayList<String> keys = new ArrayList<>();
resolvedReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
//Getting keys of all resolved concerns in keys arraylist here
for (DataSnapshot ds : snapshot.getChildren()){
keys.add(ds.getValue(String.class));
}
//Storing JSON data in this arraylist
final ArrayList<String> data = new ArrayList<>();
for(int i = 0; i<keys.size() ; ++i){
String key = keys.get(i);
//Getting data of each concern here
FirebaseDatabase.getInstance().getReference().child(getApplicationContext().getResources().getString(R.string.allConcerns))
.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String type = snapshot.child("type").getValue().toString();
Log.i("Type", type);
if(type.equals("0")) {
SafetyConcernClass s = snapshot.getValue(SafetyConcernClass.class);
Log.i("Snapshot of key", s.toString());
data.add(s.toString());
}
else{
GembaWalkClass g = snapshot.getValue(GembaWalkClass.class);
Log.i("Snapshot of key", g.toString());
data.add(g.toString());
}
Proof proof = snapshot.child("proof").getValue(Proof.class);
Log.i("Proof", proof.toString());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
//Issue I am facing is here
Log.i("Data size", String.valueOf(data.size()));
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
The real issue here is while logging data.size(). Since Firebase is asynchronous, FOR loop ends before data is fetched and entered into the data ArrayList, hence it gives me a size of 0. And since no data is fetched, I can't create an Excel file.
My question is, how can I make sure I am proceeding to log data.size() ONLY after data of respective resolved concerns is stored in the ArrayList?
The typical approach is to keep a counter or a countdown latch to track how many of the concern snapshots you've already downloaded. Once the counter reaches keys.size() you know that you're done.
Also see Setting Singleton property value in Firebase Listener
You should write your method
addListenerForSingleValueEvent
using AsyncTask or Kotlin coroutines
and in onPostExecute() of AsyncTask, you can proceed to further action.

What is the best way to update a record getting the key from another table?

I am learning how realtime databases work, so be patient.
To better explain my doubt, I take an image showing the relevant part of the realtime database
https://prnt.sc/p0wmvs
All I want is the best way to update the field "name" of a record "grps".
The starting point is the "usrs" table where I can reference
mFD.getReference("usrs/"+user.getUid()+"/asAdm/grps/"+grpId)
My function to update "name" of group name
public void editGroupFromUser(long grpID, Group group, final DataStatus dataStatus) {
DatabaseReference mFRUser = mFirebaseDatabase.getReference(
"usrs/"+user.getUid()+"/asAdm/gprs/"+grpID
);
mFRUser.child("grp").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String groupKey = (String) dataSnapshot.getValue();
DatabaseReference mFRGroup = mFirebaseDatabase.getReference("grps");
mFRGroup.child( groupKey ).setValue(group).addOnSuccessListener(aVoid -> {
dataStatus.DataIsUpdated();
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
The need to receive the group key to then send the group update. This does not seem efficient at all.
Also, the need to send the user Uid key when the user is identified in firebase does not seem safe at all.
Is there any better way to do this?

How to get a child's data when it has been pushed?

I am using the "push()" function to store objects into my firebase database and since it stores data with unique keys I can't change the stored objects values using what's mentionned in the documentation:
myRef.child("child name").setValue(model);
because I simply don't know the key. I wan wondering is there an other way to modify the data of firebase database.
I want to add a new child called "conversations" to the users and programmatically add childs to him .
and this is the databse structure:
If you have the following database:
Users
pushId
name : john
age : 100
In case you dont know the pushId, you can do the following:
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("Users");
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()){
String key = ds.getKey();
String name = ds.child("name").getValue().toString();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
The above will get you the name and the key using getKey()

Reading a value from a specific userid child node in database

I know this has been asked 100 times already but none of the solutions seem to be working for me.
Want to read the database of "user_preferences" for the user that is signed in (userID) and read the gender/age/weight/height values and store them in the variables shown below. currently returns null on everything (the log statement and the values). Feel like i havent got the path set up properly or something. help would be great!
and my code
mAuth = FirebaseAuth.getInstance();
mFirebaseDatabase = FirebaseDatabase.getInstance();
myRef = mFirebaseDatabase.getReference();
userID = mAuth.getCurrentUser().getUid();
DatabaseReference testRef = myRef.child("user_preferences");
testRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) { //loop through the firebase nodes
UserPreferences userPreferences = new UserPreferences();
userPreferences.setAge(ds.child(userID).getValue(UserPreferences.class).getAge());
userPreferences.setHeight(ds.child(userID).getValue(UserPreferences.class).getHeight());
userPreferences.setWeight(ds.child(userID).getValue(UserPreferences.class).getWeight());
userPreferences.setGender(ds.child(userID).getValue(UserPreferences.class).getGender());
genderSet = userPreferences.getGender();
age = userPreferences.getAge();
height = userPreferences.getHeight();
weight = userPreferences.getWeight();
Log.d(TAG, "onDataChange: " + genderSet);
//
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
These two lines of code:
DatabaseReference testRef = myRef.child("user_preferences");
testRef.addValueEventListener(...)
are effectively querying the entire node called user_preferences. That means everything at that location - all users. It sounds like this is not what you want. If you want to query just a single user, you should be specific about that in your query by adding the userID that you want to the query location:
DatabaseReference testRef = myRef.child("user_preferences").child(userID);
testRef.addValueEventListener(...)
Also, these lines of code are confusing to me:
userPreferences.setAge(ds.child(userID).getValue(UserPreferences.class).getAge());
userPreferences.setHeight(ds.child(userID).getValue(UserPreferences.class).getHeight());
userPreferences.setWeight(ds.child(userID).getValue(UserPreferences.class).getWeight());
userPreferences.setGender(ds.child(userID).getValue(UserPreferences.class).getGender());
You're deserializing a UserPreferences object for each and every field you want to populate, which is wasteful. It seems to me that you really just want to deserialize it once and remember the object:
UserPreferences userPreferences = dataSnapshot.getValue(UserPreferences.class);
Regarding the null values, you seem to be using external fields, which will not be set until the Firebase returns the network call after at least a second. Your values will be null in the meantime, so you should not be setting them onto a UI element outside of onDataChange.
Also, you have a lot of gets/sets going on, when you only need to call one getValue() for the class, then additional ones for the fields.
Then, you don't seem to want to loop over anything, so you should directly access the user node from the top reference.
For example,
mAuth = FirebaseAuth.getInstance();
mFirebaseDatabase = FirebaseDatabase.getInstance();
myRef = mFirebaseDatabase.getReference();
userID = mAuth.getCurrentUser().getUid();
DatabaseReference testRef = myRef.child("user_preferences/"+userID);
// or .child("user_preferences").child(userID)
testRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserPreferences userPreferences = dataSnapshot.getValue(UserPreferences.class);
Log.d(TAG, "onDataChange: " + userPreferences.getGender());
// TODO: Update some UI element here
}
#Override
public void onCancelled(DatabaseError databaseError) {
// TODO: Add error handling
}
});
}
If you only want to read the values once, use testRef.addListenerForSingleValueEvent()

Categories