I have the following database structure:
I need to delete a child (both key and value), for example, the first one, by knowing his value: xzFD1RahhZYr05nZljIW9BRzvSq1
So I have
String itemToDelete = "xzFD1RahhZYr05nZljIW9BRzvSq1";
FirebaseDatabase database = FirebaseDatabase.getInstance();
databaseReference deleteRef=database.getReference("richieste").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
deleteRef.orderByValue().equalTo(itemToDelete);
deleteRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//What to do with .removeValue(); ?
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
}
I don't know exactly what to do with dataSnapshot to remove the child
Thank you for the answer
Bonus question: what should I do with onCancelled event?
To get the DatabaseReference, you can call DataSnapshot.getReF(), and then you can remove the value by calling .removeValue().
But based on your case, you should iterate the DataSnapshot or use ChildEventListener to make sure you are removing the child and not the whole data under richieste/<user_id>.
To iterate and remove the value:
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) {
childSnapshot.getRef().removeValue();
}
There's another error in your code, you should store the reference of this line deleteRef.orderByValue().equalTo(itemToDelete); to another DatabaseReference before attaching the event listener or you can directly attach the listener with this trick deleteRef.orderByValue().equalTo(itemToDelete).addListenerForSingleValueEvent(...);
For the bonus question, the onCancelled will be called if there's an error reading the database such as network or permission problem.
Hope this helps :)
Willik's answer covers the important issues. The code below implements them all.
Update for Comments:
Query is a class in the Firebase SDK. You should be able to import it:
import com.google.firebase.database.Query;
If dataSnapshot.exists() is false, it means there is no data in the database at /richieste/$uid. Check that the UID of the signed-in user is what you expect and there is data for that UID in your database.
String itemToDelete = "xzFD1RahhZYr05nZljIW9BRzvSq1";
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference deleteRef = database.getReference("richieste")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
// orderByValue() and equalTo() return a query; can't use the original reference
Query deleteQuery = deleteRef.orderByValue().equalTo(itemToDelete);
deleteQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// If the value to delete is not unique, there may be multiple children
for (DataSnapshot child : dataSnapshot.getChildren()) {
// TODO Should also check the result of removeValue()
// using a CompletionListener or the returned Task
// in case you don't have write access
child.getRef().removeValue();
Log.d(TAG, "onDataChange: Removed " + child.getKey());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Often get here because of a protection violation;
// i.e. the security rules are not set to grant read access.
// One option is to log the failure
Log.e(TAG, "onCancelled: FAILED " + databaseError.getMessage());
// Or you can also throw the exception
throw databaseError.toException();
}
});
Related
I am trying to retrieve a list that is on a child that starts with something. Below is a sample of data in my Firebase realtime database:
In the image, I want to retrieve all data that starts with the keyword "jsonmat".
I am using thee code below but it always return null:
DatabaseReference db = FirebaseDatabase.getInstance().getReference()
.child("Events");
db.startAt("jsonmat").addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("events", dataSnapshot.toString());
for (DataSnapshot data : dataSnapshot.getChildren()) {
// here the user will have the specified email only
}
}
#Override
public void onCancelled(DatabaseError databaseError){
Log.i("MyApp", "getUser:onCancelled", databaseError.toException());
}
});
What you're trying to do isn't possible. You can't order/filter of a nested key, only on direct child keys (-M...) and on nested values (active: true).
Typically you'll want to create a new top-level node, where you store the keys you're searching for, and then the push keys for each matching nested node:
"category_things": {
"jsonmat_jsonmat": {
"-M62....uYgB": true,
"-M62....2-eO": true
}
}
Also see:
Firebase Query Double Nested
My original, but wrong answer is below...
If you use startAt without specifying an orderBy... clause, the data will be ordered by priority. This priority is a left-over from before Firebase supported ordering on specific properties, so mostly it means that you must call an orderBy... method before filtering.
So:
DatabaseReference db = FirebaseDatabase.getInstance().getReference().child("Events");
db.orderByKey().startAt("jsonmat").addListenerForSingleValueEvent(new ValueEventListener() {
...
What you can do for your case, is to loop 2 times over the children of the node Events:
//the reference to the node Events
DatabaseReference db = FirebaseDatabase.getInstance().getReference().child("Events");
db.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//loop 1
for (DataSnapshot data : dataSnapshot.getChildren()) {
//loop2
for (DataSnapshot dataTwo : data.getChildren()) {
//get the key
String key = dataTwo.getKey();
if(key.startsWith("jsonmat")){
//we got a matching key so extract the data and maybe put them in a list
boolean active = dataTwo.child("active").getValue(Boolean.class);
int bet = dataTwo.child("bet").getValue(Integer.class);
String challenger = dataTwo.child("challenger").getValue(String.class);
String competitor = dataTwo.child("competitor").getValue(String.class);
String game = dataTwo.child("game").getValue(String.class);
............
............
............
}else{
//we didn't get a match
}
}//end loop2
}//end loop1
}
#Override
public void onCancelled(DatabaseError databaseError){
Log.i("MyApp", "getUser:onCancelled", databaseError.toException());
}
});
I am storing user details 'firstname' and 'lastname' in UserNode. But when i want to retrieve that details then no data is being retrieved. I tried almost all solutions on the internet but nothing solved my problem. Here is my code for retrieving data of the current user:
FirebaseUser userr = FirebaseAuth.getInstance().getCurrentUser();
if (userr != null) {
String name = userr.getDisplayName();
Log.e("value", name);
}
but it says "println needs a message"
I also tried with this but nothing happened:
DatabaseReference DataRef;
DataRef = FirebaseDatabase.getInstance().getReference().child("UserNode");
DataRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String acctname = (String)dataSnapshot.child("firstname").getValue();
Log.e("name", acctname);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
]1
Please help me I am stuck with it
You're reading a collection of user with a ValueEventListener. As the [Firebase documentation for reading lists with a value event](Listen for value events) explains:
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 [child node], the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result.
So in your code:
DatabaseReference DataRef;
DataRef = FirebaseDatabase.getInstance().getReference().child("UserNode");
DataRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
String acctname = (String)childSnapshot.child("firstname").getValue();
Log.i("name", acctname);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
Using FirebaseUser:
FirebaseUser implements UserInfo and in UserInfo's getDisplayName() documentation says
Returns the user's display name, if available.
So, it is possible that FirebaseUser.getDisplayName() return null when display name is not set. In that case Log.e() receives null as message and therefore prints println needs a message
Using your own structure:
Instead of using type conversion use getValue(Class<T>) like so:
String acctname = dataSnapshot.child("firstname").getValue(String.class);
Please, read how to retrieve data from firebase. I think you have a problem because you don't have Class Model.
Your steps:
Create model UserModel with firstname and lastname field
Use listener (example from docs):
// Attach a listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Post post = dataSnapshot.getValue(Post.class);
System.out.println(post);
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
See other answers: How to retrieve data from one single userID Firebase Android and retrieving data from firebase android
I've been trying to retrieve an element from my Firebase database using its key. I have a class User and users are present in database.
I want to retrieve an object user using its key with this method :
public User getConnectedUserByUId(final String uid){
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = database.child("users");
final List<User> connectedUser= new ArrayList<User>();
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot item: dataSnapshot.getChildren()) {
if (item.getKey()==uid)
{
User user= dataSnapshot.getValue(User.class);
connectedUser.add(user);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return connectedUser.get(0);
}
but it returns an empty list every time.
The issue is here:
if (item.getKey()==uid)
since you are comparing 2 String in java you have to use the method
string.equals(Object other) not the == operator.
Moreover, since you know the key of the data in Firebase you can use it to get the reference without cycling all children.
Something like:
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = database.child("users").child(uid);
Here you try to check a very specific ID only on changed data. Instead, try using a Firebase Query with filterByKey and not using your own function to achieve that. Here's sample code that I would use to try to replace your function:
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = database.child("users");
Query connectedUser = ref.equalTo(uid);
connectedUser.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
// TODO: handle the post here
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
As specified in the Firebase documentation here: https://firebase.google.com/docs/database/android/lists-of-data#filtering_data
in the line : User user= dataSnapshot.getValue(User.class);
you have to put : User user= item.getValue(User.class);
and you have to check the id after you get the user:
if (user.getKey()==uid){
connectedUser.add(user);
}
There are 2 mistakes and a minor issue:
you are using == to compare two String objects. In java, this is true only if they are the same reference. Use equals instead.
addValueEventListener only adds a listener that gets invoked once after you add it and then every time something changes in the value you are listening to: this is an asynchronous behaviour. You are trying to get data synchronously instead. Please read something about this.
you are fetching useless data: you only need an object but you are fetching tons of them. Please consider to use the closest reference you can to the data you are fetching.
So, in conclusion, here's some code. I'd like to point out right now that forcing synchronous acquisition of naturaly asynchronous data is a bad practice. Nevertheless, here's a solution:
public User getConnectedUserByUId(final String uid){
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = database.child("users").child(uid);
Semaphore sem = new Semaphore(0);
User[] array = new User[1];
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot item: dataSnapshot.getChildren()) {
if (item.getKey()==uid)
{
User user= dataSnapshot.getValue(User.class);
array[0] = user;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
try
{
sem.tryAcquire(10, TimeUnit.SECONDS);
}
catch (Exception ignored)
{
}
return array[0];
}
EDIT: I've just seen that this post is very old. I'm not sure how I ended up here.
I want to retrieve the messages data under a specific key. But I don't how to get the key. Please help, I'm new to firebase.
In my case right now, I want to get the key encircled below.
I have tried this code below but this returns "chat-mates" not the key.
final DatabaseReference ref =FirebaseDatabase.getInstance().getReference().child("chat").child("single-chat").child("converstation").child("chat-mates");
ref.orderByChild("receiverName").equalTo("Liza Soberano").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()){
String key = child.getKey();
Log.e("Key", key);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I have tried this code below but this returns "chat-mates" not the key.
You must be using DataSnapshot method to access the JSON tree.
The DataSnapshot element has a method called getKey(). That returns the key of an object.
Official Doc: DataSnapShot getKey() method
Example Code:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot objSnapshot: snapshot.getChildren()) {
Object obj = objSnapshot.getKey();
}
}
#Override
public void onCancelled(DatabaseError firebaseError) {
Log.e("Read failed", firebaseError.getMessage());
}
});
In your case, first get to the child node "conversation" and then apply the above method getKey().
You're building your path wrong and likely end up iterating a different part of the tree, one level above child-mates. In that case it would be correct that child-mates is a child key.
The problem is in the last child() call when you create the ref:
final DatabaseReference ref =FirebaseDatabase.getInstance().getReference()
.child("chat")
.child("single-chat")
.child("converstation")
.child("chat-mates");
There is no child chat-mates under converstation, so this ref won't be correct.
You probably want to do this:
final DatabaseReference ref =FirebaseDatabase.getInstance().getReference()
.child("chat")
.child("single-chat")
.child("converstation");
ref.orderByChild("chat-mates/receiverName")
.equalTo("Liza Soberano")
.addListenerForSingleValueEvent(new ValueEventListener() {
This will filter on the chat-mates/receiverName child of each chat.
Note that you're going against one of Firebase's recommendations with this data structure. Firebase recommends against nesting data types in the way you do here.
A more denormalized data model would be:
chat-mates
$chatRoomId
receiverName
senderName
chat-messages
$chatRoomId
$messageId
This way you can get the mates/participants in a chat, without accessing (or even needing to have access to) the messages themselves.
Description
My Firebase NoSQL Database should look somewhat like this:
A user Java model should get pushed whenever a new user signs in.
Problem
How to check if the user with the email address already has an account? Since the email address is at: Root > PushID > email; and push ID is automatically generated, I am not sure on how to iterate on all push ID's and check if any of them has its email key set to the specified value.
I have read the documentation but unable to figure out how to solve this problem.
Learning from Shubhank's answer
Firebase Methods which involve a child can be applied to any of its children irrespective of its level in the hierarchy i.e. not limited to immediate children.
In this problem, the child to be searched was 2 levels below the common parent. The first level child can be ignored and query methods can be directly applied on the target child.
You should first make a model class for the user
class User {
public String email;
public String name;
}
Then get the users from the db and map them into User objects
Simple loop through all results query
FirebaseDatabase database = FirebaseDatabase.getInstance();
// i don't know the end point since you have not specified it in the image
database.getReference("myUsersEndPoint").addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
User user = data.getValue(User.class);
if (myField.getText().toString().eqaulsIgnoreCase(user.email)) {
// exist
}
}
}
#Override
public void onCancelled(DatabaseError databaseError)
Log.w("MyApp", "getUser:onCancelled", databaseError.toException());
}
});
Searching only for specific Values
In this example, we use the orderByChild and equalTo method to limit result to specific child value
FirebaseDatabase database = FirebaseDatabase.getInstance();
// i don't know the end point since you have not specified it in the image
database.getReference("myUsersEndPoint").orderByChild("email").equalTo("emailToSearchhere").addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
// here the user will have the specified email only
}
}
#Override
public void onCancelled(DatabaseError databaseError)
Log.w("MyApp", "getUser:onCancelled", databaseError.toException());
}
});