I am trying to update a value in my Firebase database. This is the structure of the database:
I need to update the value of status to "accepted" if the r_id and s_id have a specific value. The problem here is that the key for each friend_data child is generated using "push" like this
db.child("friend_data").push().setValue(friend);
I have tried this question here and this one here but both don't meet my requirements. How do I go about solving this one?
Edit: I understand now that I am supposed to use a Query here. This is what I tried:
final DatabaseReference db = FirebaseDatabase.getInstance().getReference("friend_data");
Query query = db.orderByChild("r_id").equalTo(f_id).orderByChild("s_id").equalTo(id);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
db.child(child.getKey()).child("status").setValue("accepted");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This does not work either because this error is raised
java.lang.IllegalArgumentException: You can't combine multiple orderBy calls!.
So how do I combine two Queries?
I have finally got this to work after days of trying to figure it out.
I added a Query with orderByChild method to filter out a big chunk of data at the server side itself (thanks to this answer for the idea.) The remaining data which I got as child of data type DataSnapshot had all the necessary information that I needed. Here is the query
Query query = db.orderByChild("r_id").equalTo(f_id);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
String child_s_id = (String) child.child("s_id").getValue();
String child_status = (String) child.child("status").getValue();
if (child_s_id.equals(id)) {
Log.e("Got value", f_id + " - x -" + id + " " + child_status);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But again I could not meet my requirements, because I actually needed to modify one of the value in the child and I could not use any methods like setValue to change the value of child.
Again, it took me a long while (what an idiot) to figure out that I had to convert the child (of data type DataSnapShot) to a reference for setValue to work. And it worked
child.getRef().child("status").setValue("accepted");
Here is the completed code. Hope it helps someone along the way
Query query = db.orderByChild("r_id").equalTo(f_id);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
String child_s_id = (String) child.child("s_id").getValue();
String child_status = (String) child.child("status").getValue();
if (child_s_id.equals(id))
child.getRef().child("status").setValue("accepted");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
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());
}
});
How To get all event and check index =-L61MIZORBalbOLJBBSk
Then get value("owned"):
I have try this one but not working
string eventkey =-L61MIZORBalbOLJBBSk
final String eventkey = ds.child("key").getValue(String.class);
Query query1 = FirebaseDatabase.getInstance().getReference()
.child("users")
.orderByChild("userid")
.equalTo(userid);
query1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot ds) {
Log.e("eventvalue", "" + ds.child("events").getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("userserror", "" + databaseError);
}
});
Sorry for my bad English.
When you execute a query against the Firebase Database, there will potentially be 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.
So the code in your onDataChange will need to handle those multiple children by looping over DataSnapshot.getChildren():
public void onDataChange(DataSnapshot ds) {
for (DataSnapshot child: ds.getChildren()) {
Log.e("eventvalue", "" + child.child("events").getValue());
}
}
Alternate data structure
But also consider if your data structure is the best for your use-case right now. You have a list of users, and each user already has a unique identified called userid. Instead of storing each user under a push ID, I'd recommend storing each user under their userid.
In code you probably now store the users with something like:
DatabaseReference ref = FirebaseDatbase.getInstance().getReference("users");
ref.push().setValue(user);
To get the structure I recommend, instead store them with:
ref.child(user.getUserid()).setValue(user);
Now each user is stored directly under their userid. That means that if you know a userid, you can look the user up without needing a query. Your code becomes:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference()
.child("users")
.child(userid);
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot ds) {
Log.e("eventvalue", "" + ds.child("events").getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("userserror", "" + databaseError);
}
});
Node the lack of a loop in onDataChange and (more importantly) the lack of orderByChild().equalTo() in the query. This is just a direct look up, which:
Is easier to code.
Automatically means that each user can be stored only once, since the keys are automatically unique.
Scales much better, since you don't need a query.
The following line of code:
ds.child("events").getValue()
Returns a Map. So all you need to do is to iterate over the map. Please see the following code:
Query query1 = FirebaseDatabase.getInstance().getReference()
.child("users")
.orderByChild("userid")
.equalTo(userid);
query1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot ds) {
Map<String, Object> map = (Map<String, Object>) ds.child("events").getValue();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if(entry.getKey().equals(eventkey() {
Log.e("TAG", entry.getKey());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("userserror", "" + databaseError);
}
});
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 would like to ask for help in a problem that I want to search for e-mail, according to the database, but it fails to process the received data. I want to receive something of value that the email is already in the database or not.
How do I process the result? Here's the code which try:
DatabaseReference reg = FirebaseReferenciak.reg;
reg.orderByChild("email").equalTo("email", etEmail.getText().toString()).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, String> map = dataSnapshot.getValue(Map.class);
String value = map.get("name");
//Here is how do I check?
//String value = dataSnapshot.getValue(String.class);
dialog(value);
}
#Override
public void onCancelled(DatabaseError databaseError) {
dialog("error");
}
}
);
Thanks for the help!!!
When you execute a query against the Firebase Database, there will potentially be 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 that reason you'll need to loop over the children of the DataSnapshot that you receive in onDataChange():
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
String value = childSnapshot.child("name").getValue(String.class);
dialog(value);
}
}