It's showing something, but from the wrong user, not the signed-in user. In fact, it is the latest registred user. Any suggestions? Here is my code:
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getInstance().getReference("userInfos");
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
UserInformation userInformation = dataSnapshot.getValue(UserInformation.class);
vorname.setText(userInformation.vorname);
nachname.setText(userInformation.nachname);
alter.setText(userInformation.alter);
sprachen.setText(userInformation.sprachen);
System.out.println("Prev: " + s);
}
Structure of my database
Well, the type of listener you're using is the ChildAdded one that will ge triggered when you add a new user (that's why you're getting the last added user) To get the current user data user, use a value event listener and pass the logged user id:
String UID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference ref = database.getInstance().getReference("userInfos");
ref.child(UID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
UserInformation userInformation = dataSnapshot.getValue(UserInformation.class);
vorname.setText(userInformation.vorname);
nachname.setText(userInformation.nachname);
alter.setText(userInformation.alter);
sprachen.setText(userInformation.sprachen);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
When you are using the following line of code:
System.out.println("Prev: " + s);
The s variable holds a value of type String that is actually coming from the second argument of the overridden method onChildAdded(). According to the offical documentation, the second argument can be also written as:
onChildAdded(DataSnapshot snapshot, String previousChildName)
That's why you are printing a wrong name. So the second argument is actually the name of the previous child.
Related
I want to get a User or just a User UID via email or name. Tried to write the query, but I'm getting all users and then iterating through them and then getting the user but I think it's an expensive task. How can I get only one User/Uid from Realtime Database?
This is what I came up with (But don't think is the best way):
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("Users");
Query emailQuery = usersRef.orderByChild("email").equalTo(client.getEmail());
emailQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot child : snapshot.getChildren()) {
if (child.getValue(User.class).getEmail().equals(client.getEmail())){
User user = child.getValue(User.class);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
According to your last comment:
I have the corresponding details of each user (email, name), and I want to get the UID of one user (no matter which one)
To get a particular user from the database based on a particular email address, the query that you are already using:
Query emailQuery = usersRef.orderByChild("email").equalTo(client.getEmail());
Returns only the users who have the field email set to what client.getEmail() returns. For instance, if client.getEmail() returns tedo#gmail.com then you'll get a single result, according to your screenshot, which is the first one. That being said, the following if statement, doesn't make sense to be used:
if (child.getValue(User.class).getEmail().equals(client.getEmail())){
User user = child.getValue(User.class);
}
Since the key of the user node is represented by the UID, then you should get it like this:
Query emailQuery = usersRef.orderByChild("email").equalTo(client.getEmail());
emailQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot child : snapshot.getChildren()) {
User user = child.getValue(User.class);
String uid = child.getKey();
//Do what you need to do with UID.
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Log.d("TAG", error.getMessage()); //Never ignore potential errors!
}
});
That's the simplest, cheapest way to query the Realtime Database, in which you return only the elements that you are interested in and that satisfy a particular condition.
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");
This is the Firebase database structure of my project.
I want to fetch complaint division, describe, stat for the particular hostelname and roomno. How to fetch these data and display in android project?
To get all those values, please use the following lines of code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference complaintsRef = rootRef.child("Complaints");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String complaintDivision = ds.child("complaintdivision").getValue(String.class);
//Get the other properties in the same way
Log.d(TAG, complaintDivision);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
complaintsRef.addListenerForSingleValueEvent(valueEventListener);
The output in your logcat will be:
Plumber Related
This is a basic function in Firebase on Android. You can use ValueEventListeners with the database reference to do this.
The steps to achieve the desired results can be enlisted as below:
Create a Complaint model with the same fields as your database.
Get the right Firebase reference for your database node and add a ValueEventListener instance to listen for database changes.
Pass a DataSnapshot into the Complaint class and assign it to a Complaint object.
Do what you want with the Complaint object you have obtained.
Creating a Complaint class:
class Complaint {
// your fields should have the same name as database fields to prevent unnecessary complications
public String complaintdivision;
public String complaintid;
public String describe;
public String hostelname;
public String roomno;
public String stat;
public Complaint(){// required for Firebase
}
}
Getting the data from Firebase:
ArrayList<Complaint> myComplaintArrayList = new ArrayList<>();
FirebaseDatabase.getInstance().getReference().child("Complaints").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot complaint: dataSnapshot.getChildren()){
Complaint c = complaint.getValue(Complaint.class);
myComplaintArrayList.add(c);// you should have an ArrayList<Complaint> for this loop
}
// do what you want with the items you obtained
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
This is pretty much all of it. If you are still having problems, you should read a tutorial on Firebase.
So to fetch the data you would first declare a Firebase DatabaseReference object that points to your database:
private DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
You would then write a query using this object:
Query query = mDatabase.child("Complaints");
Now you attach a SingleValueEventListener to this query:
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Write a for-each loop to cycle through your node's children
for(DataSnapshot data: dataSnapshot.getChildren()){
//Create an instance of your model class to
//store the received data
//Make sure you have an empty constructor in your model class
Complaint complaint = data.getValue(Complaint.class);
//then simply call your getters on the complaint object
//to get what you need
complaint.getComplaintDivision();
complaint.getDescription();
//...
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
To fetch more specifically, you just modify the query.
For more information on queries, see this
I get the Alex Mamo's Solution in order to check whether a unique value exists in my database but the snapshot gets always null. The solution is here:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userNameRef = rootRef.child("Users").child("Nick123");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
userNameRef.addListenerForSingleValueEvent(eventListener);
And my database looks like this:
users
(auto-generated push token ID)
fullname
username
gender
...
I don't know whether it's the right method but I used push() method for adding the user object to the database and this method generates push token ID like above.
So, in order not to create duplicate users, what should I change with the above solution? Is there a better method to do this? Maybe like checking the firebase uid with some firebase auth methods?
UPDATE
My database screenshot:
As you see the string just below 'users' is not the same as user's uid.
This is not the best practice when it comes to save user details into the database. First, you should implement Firebase Authentication and then in order to check if a user exist in your database, you should use the following lines of code:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference uidRef = rootRef.child("users").child(uid);
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
}
}
#Override
public void onCancelled(DatabaseError error) {
Log.d("TAG",error.getMessage()); //Don't ignore potential errors!
}
};
uidRef.addListenerForSingleValueEvent(eventListener);
So the key for solving this problem is to call the child() method and pass the UID as an argument instead of calling the push() method that generates an random unique identifier.
If you have this database:
users
(auto-generated push token ID)
fullname
username
gender
Then to check if user exists you need to do the following:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userNameRef = rootRef.child("users");
Query queries=userNameRef.orderByChild("fullname").equalTo("Nick123");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
queries.addListenerForSingleValueEvent(eventListener);
First if you have users lowercase letter, then inside child add users, then you need to use a query to be able to check if the fullname is equal to Nick123.
Usually the database is structured like this:
users
userId
fullname: Nick123
The attributes are key:value, the key is the identifier and Nick123 is the value, so inside the child() method you need to write the identifier which is fullname and not Nick123.
Then since you are able to get the userId, you can do this:
FirebaseUser currentFirebaseUser =FirebaseAuth.getInstance().getCurrentUser();
String userId=currentFirebaseUser.getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userNameRef = rootRef.child("users").child(uid).child("fullname");
userNameRef.addValueEventListener(new ValueEventListener(){...}
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()