How to only get child keys in Firebase - java

I want to display only children of location i.e Sindh and Punjab (not their children). Is it possible, and if so how can I do it?

From Best practices for data structure in the docs:
Avoid nesting data
Because the Firebase Realtime Database allows nesting data up to 32
levels deep, you might be tempted to think that this should be the
default structure. However, when you fetch data at a location in your
database, you also retrieve all of its child nodes. In addition, when
you grant someone read or write access at a node in your database, you
also grant them access to all data under that node. Therefore, in
practice, it's best to keep your data structure as flat as possible.
That is how Firebase works: If you get an item, you get its children as well. If you don't want this, you should restructure the database.

Avoid Nesting Data
Because the Firebase Realtime Database allows nesting data up to 32 levels deep, you might be tempted to think that this should be the default structure. However, when you fetch data at a location in your database, you also retrieve all of its child nodes. In addition, when you grant someone read or write access at a node in your database, you also grant them access to all data under that node. Therefore, in practice, it's best to keep your data structure as flat as possible.
Below is a structure of yours on how to implement flat database as well as how to retrieve the location key.
{
"location": {
"punjab": {
"lahore": true,
"multan": true
},
"sindh": {
"karachi": true
}
},
"place": {
"lahore": {
"location": "punjab",
"details": {
"0": "model",
"1": "steel town"
}
},
"multan": {
"location": "punjab",
"contacts": {
"0": "myarea"
}
},
"karachi": {
"location": "sindh",
"contacts": {
"0": "hadeed",
"1": "Steel town"
}
}
}
}
Below is the code that you can use to retrieve the location key.
private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();
mLocation = mDatabase.child("location");
mPlace = mDatabase.child("place");
ValueEventListener placeListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Place place = dataSnapshot.getValue(Place.class);
String location = place.location;
System.out.println(location);
}
};
mPlace.addValueEventListener(placeListener);
For more information on Firebase:
Firebase Security & Rules

DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("location");
final ArrayList<String> statesArrayList= new ArrayList<>();
databaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
statesArrayList.add(dataSnapshot.getKey());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

Yes, you can
ValueEventListener getValueListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get data from firebase
Log.d("The name of child that you need:", dataSnapshot.getKey());
// ...
}
};
databaseReference.addValueEventListener(getValueListener );
Read more: https://firebase.google.com/docs/database/android/read-and-write#listen_for_value_events

Though nesting data is not recommended in NoSQL databases like Firebase, to get the name of child nodes your code will look like this
DatabaseReference mainRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference locationref = mainRef.child("location");
final ArrayList<String> locationNames = new ArrayList<>();
locationref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
locationNames.add(dataSnapshot.getKey());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
You can now use locationNames to do what you want.

Firebase can as quickly look up a node at level 1 as it can at level 32. Depth is not a factor that affects speed on a technical level but years of experience with Firebase has shown that deeply nested data often goes hand in hand with performance problems. As an example, i recomand you reading the offical documentation, Best practices for data structure in Firebase and Structuring your Firebase Data correctly for a Complex App.
If you don't want to change your actual database structure and assuming that location node is a direct child of your Firebase root, please see the code below:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference locationRef = rootRef.child("location");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String key = ds.getKey();
Log.d("TAG", key);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
locationRef.addListenerForSingleValueEvent(eventListener);
Your output will be:
punjab
sindh

I know this question was asked a while ago and for android, but I was looking for a solution to this and came upon this question. I couldn't use the answers provided, so here is the solution I found. This will only get the children and nothing else.
This is in javascript, but you can use whichever method is the alternative in java to make a request from a REST endpoint.
const databaseURL = "https://example.firebaseio.com" // replace value with yours
const endpoint = "location"
const user = firebase.auth().currentUser
user.getIdToken()
.then(token => {
return fetch(`${databaseURL}/${endpoint}.json?auth=${token}&shallow=true`)
})
.then(response => {
return response.json()
})
.then(json => {
console.log({json}) // contains only the children
})
.catch(error => {
console.log(error.message)
})
The important bit here is the &shallow=true otherwise you get all the data in the children.
Using curl this would be
curl 'https://example.firebaseio.com/location.json?auth=INSERT_TOKEN&shallow=true'
This assumes location is a child of the root.
More details can be had by looking at the docs
Before calling this, you have to make sure you have the currentUser available in firebase.
Also, I would heed the warnings mentioned here about proper firebase realtime database structure. However, if you are already in this spot, then this can help.

This is how u can get child names
HashMap<String, Object> allData = (HashMap<String, Object>) dataSnapshot.getValue();
String[] yourChildArray = allData.keySet().toArray(new String[0]);

Related

how do i retrieve a nested object from Firebase?

I am trying to retrieve a nested list of workouts from a Realtime Database and I don't know how to do it.
I made some research and still couldn't really figure out how am supposed to do it.
The Realtime Database JSON file looks like this :
I am looking to retrieve data by workout, for example, if someone presses the workout one button I should retrieve the full workout one object. but I don't know how am supposed to design my query request nor how am supposed to structure my model object that conceives the received data.
As I see in your screenshot, under the "Workout one" node, you have two nested levels of data. So to get all exercises for each day, you have to loop over the children twice:
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
DatabaseReference workoutOneRef = db.child("Fat Loss").child("Workout one");
workoutOneRef.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
for (DataSnapshot daySnapshot : task.getResult().getChildren()) {
for (DataSnapshot exerciseSnapshot : daySnapshot.getChildren()) {
String name = exerciseSnapshot.child("name").getValue(String.class);
Log.d("TAG", name);
}
}
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
Please also don't forget that the Firebase API is asynchronous. So any code that needs data from the database needs to be inside the onComplete() method, or be called from there. To understand better, I recommend you check the following resource:
How to read data from Firebase Realtime Database using get()?
I think below line code help you.
databaseReference = FirebaseDatabase.getInstance().getReference().child("Fat Loss").child("Workout one").child("day 1")
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
allTaskList.clear();
if (snapshot.exists()) {
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
//modal class object
AddTODOListModal model = dataSnapshot.getValue(AddTODOListModal.class);
assert model != null;
model.setId(dataSnapshot.getKey());
allTaskList.add(model);
}
adapter = new TODOListAdapter(TODOListHomeActivity.this, allTaskList);
binding.rvTODO.setAdapter(adapter);
}else {
Utils.showToast("No Data Available");
}
Utils.dismissProgressDialog();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Utils.showToast(error.getDetails());
}
});
Reference link :- https://firebase.google.com/docs/database/android/lists-of-data

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 compare two children nodes from different references in Android Firebase?

In Firebase I have the "Users" node where I store information about the registered users and I have the "tasks" node with the tasks information created by each user and 3 owners email addresses added to these tasks manually. I am trying to compare the email field from Users with each owners email. If the current user's email is one of the 3 owner emails, then I will show only the tasks where his email is among those.
This is a snippet from my Firebase realtime database:
"Users" :
{"SolD4tqjUJd1Xru3mRliwtoik2A3" :
{ "email" : "x#gmail.com",
"id" : "SolD4tqjUJd1Xru3mRliwtoik2A3",
"name" : "AB",
"phone" : "123456789"
}
},
"tasks" :
{ "-LgrtyuTjd2QpNIhUeEi" :
{ "-Lgsjx1c-E6OU3t1SbhL" :
{ "id" : "-Lgsjx1c-E6OU3t1SbhL",
"owner_one" : "x#gmail.com",
"owner_three" : "y#gmail.com",
"owner_two" : "z#gmail.com",
"projectId" : "-LgrtyuTjd2QpNIhUeEi",
"taskDate" : "2019 / 6 / 5",
"taskDescription" : "dddd",
"taskName" : "ddddd",
"taskstatus" : "Closed",
"userId" : "SolD4tqjUJd1Xru3mRliwtoik2A3"
}
}
}
}
I have tried the following code to get the email from Users and the owners emails from tasks, but I get stuck at comparing the email with email1, email2 and email3.
myTasks = findViewById(R.id.myTasks);
myTasks.setLayoutManager(new LinearLayoutManager(this));
tasks = new ArrayList<>();
UsersRef = FirebaseDatabase.getInstance().getReference().child("Users");
UsersRef.child(currentUserID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
String email = user.getEmail();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
reference = FirebaseDatabase.getInstance().getReference().child("tasks");
reference.addChildEventListener(new ChildEventListener(){
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
//set code to retrieve data and replace layout
for(DataSnapshot dataSnapshot1: dataSnapshot.getChildren()){
Task p;
p = dataSnapshot1.getValue(Task.class);
String email1 = p.getOwner_one();
String email2 = p.getOwner_two();
String email3 = p.getOwner_three();
tasks.add(p);
}
taskAdapter = new TaskAdapter(MyTasks.this, tasks);
myTasks.setAdapter(taskAdapter);
taskAdapter.notifyDataSetChanged();
}
There is no need to create a database call in order to be able to get the email address of the user, you can simply get the email address directly from the FirebaseUser object like this:
String email = FirebaseAuth.getInstance().getCurrentUser().getEmail();
According to your database schema, please note that there is no way you can query your database when you have two nodes with dynamically (pushed) names. There are also no wildcards in Firebase. To solve this, you should reduce the number of children by adding the project's id as a property of your task object. Now, to get all task that corresponde to a specific email address, please use the following query:
reference = FirebaseDatabase.getInstance().getReference().child("tasks");
Query firstQuery = reference.orderByChild("owner_one").equalsTo("x#gmail.com");
This works only with one property. Unfortunately, you cannot query in Firebase realtime database using multiple "WHERE" clauses (in SQL terms). What you can do is to query the database three times, once for every owner property. The second and the third query should look like this:
Query secondQuery = reference.orderByChild("owner_two").equalsTo("x#gmail.com");
Query thirdQuery = reference.orderByChild("owner_three").equalsTo("x#gmail.com");
Please note, that in order to get the desired result, you should use nested queries that might look like this:
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
if(dataSnapshot.exists()) {
//Perform the second query and then the third query
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
};
firstQuery.addChildEventListener(childEventListener);
However, this can be more easily done in Cloud Firestore, where you can add a property of type array and use whereArrayContains() method like this:
tasksRef.whereArrayContains("owners", "x#gmail.com");

Android Firebase keeps on getting old values

I'm using Firebase in my Android Project. I have a ValueEventListener and it keeps on getting the old value even the node is already modified by other devices. It seems like it is disregarding the updates made by other devices.
Here's my code:
ValueEventListener listener = destinationDatabaseReference.child(somestring)
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue()==null)
{
}
else
{
//here I keep on getting the old value
string value = (long) dataSnapshot.child("someString").getValue();
}
}
use the query like this:-
Query query = mDatabase.getReference().child("posts").orderByChild("date").limitToLast(1);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if(dataSnapshot!=null){
String notific = String.valueOf(dataSnapshot.getValue());
Log.d("key",dataSnapshot.getKey());
Log.d("title",String.valueOf(dataSnapshot.child("title").getValue()));
Log.d("content",String.valueOf(dataSnapshot.child("content").getValue()));
}
}
Thanks guys for all the help. I tried your suggestions but it did not work for me :(
However, I found this link and I would like to share just in case someone needs it.
For this kind of issue, you have to use Transaction Operations. Please see related useful links below:
Updating firebase data in several devices by Transaction Operations
Similar Question with Working Answer
Try to use a final reference like this:
URL url = new URL(yourURL);
final Firebase root = new Firebase(url.toString());
root.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue()==null)
{
}
else
{
//here I keep on getting the old value
string value = (long) dataSnapshot.child("someString").getValue();
}
}

Google firebase check if child exists

In my app, I need to check if a given element of my database on firebase has a child with a given name. I hoped it could be done by using something along the lines of:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
if (rootRef.childExists("name")) {
//run some code
}
I searched but I couldn't find anything useful.
Edit 2; worth putting on top: I think it is worth mentioning that this is actually downloading all data at this snapshot just to check whether any data exists. You should be mindful here. If the reference is huge (e.g. actually the root reference and not a specific child/property) then you should either find a deeper node you can use to check for existence or design your data structure differently so an efficient check is possible.
A database reference is effectively the URL for that data. You want to actually get data to see whether a child exists. This is why the method you seem to be looking for is on DataSnapshot.
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("name")) {
// run some code
}
}
});
Now, this design pattern feels a bit strange. You're reading the whole database just to see whether "name" exists. You can make this a bit more efficient by listening to rootRef.child("name") and then just checking whether snapshot.exists().
If you're trying to do validation here, and not control flow, you should consider putting this code in your rules.json.
edit: I originally used the wrong function name (childExists instead of hasChild)
Don't do like this
NEVER
It will take all your data and bring to device
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("name")) {
// run some code
}
}
});
Check it by this way.
It will return the value of the child if exists, otherwise -> null
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.child("childName")
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
void onDataChange(DataSnapshot snapshot) {
if (snapshot.getValue() == null) {
// The child doesn't exist
}
}
});
A complete solution. No need to download all the data. Just check if the child exist like this:
// Assuming the names are in the name node. Remove "name/" if not
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference("name/" + name);
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
// Exist! Do whatever.
} else {
// Don't exist! Do something.
}
}
#Override
public void onCancelled(DatabaseError error) {
// Failed, how to handle?
}
});
Try using .childexists in combination with .equalTo("Your specific name")
UsersRef = FirebaseDatabase.getInstance().getReference();
Users.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("childName")) {
// it exists!
}else{
// does not exist
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Use snapshot.exists() to check if the referenced database entry contains a child , irrespective of the value of the child.

Categories