Firebase sorting data based on child not working - android

I'm trying to develop a simple application to learn firebase database.
Here is my realtime database schema
mydirectory
-contact_list
-LOyDhepB_IZlM6lZtko
mobileNumber: "9385746982"
name: "Jay"
-LOyDhetiPHalLhXrOaU
mobileNumber: "8000478912"
name: "Ravi"
-LOyDhetiPHalLhXrOaV
mobileNumber: "123456789"
name: "ABC"
-LOyDheubruATyyBp8dG
mobileNumber: "023456879"
name: "XYZ"
I want to get the list of data ordered by name
So I'm using this snippet to load the data in my android application
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("contact_list").orderByChild("name").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, Object> objectMap = (HashMap<String, Object>) dataSnapshot.getValue();
Master.personList.clear();
for (Object obj : objectMap.values()) {
if (obj instanceof Map) {
Map<String, Object> mapObj = (Map<String, Object>) obj;
String name = (String) mapObj.get("name");
String mobileNumber = (String) mapObj.get("mobileNumber");
Person person = new Person(name, mobileNumber);
Master.personList.add(person);
populateListView();
}
}
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w("ANDs", "Failed to read value.", error.toException());
}
});
But it does not sort data based on the alphabetical order of name
key. Am I doing anything wrong or missing something?
P.S.
I've also indexing rules (ref), but still result is same! It doesn't sort data alphabetical wise.
{
"rules": {
"mydirectory": {
"contact_list": {
"$user_id": {
".indexOn": ["name"]
}
}
},
".read": true,
".write": true
}
}
I've tried many things from other posts like this topic but nothing works! Please don't make it duplicate instead please help me to solve it!

A HashMap can only contain a key and a value. And the keys in a Map are by definition not ordered. So when you call dataSnapshot.getValue(), you're throwing away all ordering information.
To maintain the order, you'll need to loop over dataSnapshot.getChildren():
public void onDataChange(DataSnapshot dataSnapshot) {
Master.personList.clear();
for (DataSnapshot contactSnapshot: dataSnapshot.getChildren()) {
String name = contactSnapshot.child("name").getValue(String.class);
String mobileNumber = contactSnapshot.child("mobileNumber").getValue(String.class);
Person person = new Person(name, mobileNumber);
Master.personList.add(person);
}
populateListView();
}
You'll also notice that I use child(...) to get a child snapshot for a specific property and get the String value from it. Your approach for that would work too, but I find that I get better error messages when I stick to a snapshot longer (instead of converting to a Map).

Related

How to add additional child node inside a unique key in firebase realtime android?

I want to include surname inside that unique key together with full name and student id. I am quite stuck and this is probably my last resort. I don't know what to do in the coding. Your answer will definitely a big help. Thank you so much!
{
"Students" : {
"-Ma9R58DvoFMkLqukMmk" : {
"fullname" : "Juan Capistrano Dela Cruz",
"studId" : "18-ln-0001"
},
"-Ma9TttvUqShhYjD-q_5" : {
"fullname" : "Maria Acosta Gomez",
"studId" : "18-ln-0002"
},
"-Ma9Ub__33IsWEFDbitu" : {
"fullname" : "David Orosco Villar",
"studId" : "18-ln-0003"
},
"-Ma9ZI_FvEgutnSvsSrU" : {
"fullname" : "Ara Quizon Diaz",
"studId" : "18-ln-0006"
},
"-MaBPU-fsEInFUGWft_n" : {
"surName" : "Diaz"
}
}
}
DatabaseReference databaseReference =
FirebaseDatabase.getInstance().getReference();
String userId = databaseReference.child("Students").push().getKey();
String surname = snameInput.getText().toString();
HashMap hashMap = new HashMap();
hashMap.put("surName", surname);
databaseReference.child("Students").child(userId).setValue(hashMap).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(PersonalData.this, "Surname updated", Toast.LENGTH_SHORT).show();
}
});
If you know the student ID of the node you want to update, then you can take a two-step process:
Use a query to find the nodes with that student ID.
Update that node/those nodes.
In code that'd be:
Query query = databaseReference.child("Students").orderByChild("studId").equalTo("18-ln-0002");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot resultSnapshot) {
for (DataSnapshot studentSnapshot: resultSnapshot.getChildren()) {
studentSnapshot.getRef().child("surname").setValue("Cortez");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
In general though, if your student IDs are already unique, then it makes more sense to use those as the keys for the nodes under Students. So in that case you'd add a student with:
HashMap hashMap = new HashMap();
hashMap.put("fullname", "Maria Acosta Gomez");
databaseReference.child("Students").child("18-ln-0002").setValue(hashMap);
And updating them would then be as simple as:
databaseReference.child("Students").child("18-ln-0002").child("surname").setValue("Cortez");
Or if you want to use the hashmap:
HashMap hashMap = new HashMap();
hashMap.put("surName", snameInput.getText().toString());
databaseReference.child("Students").child("18-ln-0002").updateChildren(hashMap);

Get Value from Firebase Database

I need to get the string value from the node passcode in my Firebase database to compare with a user input, but unfortunately I am not able to get the value. This is the link to my firebase database in the below image.
This is my codes below:
final DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference("pin_code");
mDatabase.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
String rface = (String) dataSnapshot.child("pincode").getValue();
if (rface.equals(userPassword) && !rface.equals("")){
Intent intent = new Intent(PinActivity.this, ProfileActivity.class);
startActivity(intent);
}
else {
if (rface.equals("") || rface.equals(null)){
// Creating new user node, which returns the unique key value
// new user node would be /users/$userid/
String userId = mDatabase.push().getKey();
// creating user object
Pin pin = new Pin(authUserId, userPassword);
mDatabase.child(userId).setValue(pin);
Intent intent = new Intent(PinActivity.this, ProfileActivity.class);
startActivity(intent);
}
else {
Toast.makeText(PinActivity.this,"Invalid PIN code", Toast.LENGTH_SHORT).show();
return;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This is the json code
{
"pin_code" : {
"id" : "TQYTo1NHNnhPJnOxhe1Vok3U6ic2",
"pincode" : "12345"
}
}
This FirebaseDatabase.getInstance().getReference("pin_code") does not refer to the node you're looking for. Most likely you know the id property, in which case you can get the node with:
DatabaseReference collection = FirebaseDatabase.getInstance().getReference("p...");
Query query = collection.orderByChild("id").equalTo("TQT...ic2");
query.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
String rface = (String) child.child("pincode").getValue();
if (rface.equals(userPassword) && !rface.equals("")){
The changes I made:
On the first line we get the collection: the node under which you want to run a query. You struck out the name of that node in the screenshot, but it's the second line you marked.
In the second line we create a query on the id property of each child node under the collection.
In the onDataChange we added a loop. This is needed because a query against the Firebase Database will potentially have multiple results. So the dataSnapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result. We loop over dataSnapshot.getChildren() to handle those multiple results.
If there can ever only be one node with the same id, you should consider changing your data structure to use the id as the key of the node. So:
pin_codes
uid1: "pincode1"
uid2: "pincode2"
Your code then becomes significantly simpler, because you don't need to query for the user anymore. You can just directly read from the path:
DatabaseReference user = FirebaseDatabase.getInstance().getReference("pin_codes").child("TQT...ic2");
user.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
String rface = (String) dataSnapshot.getValue();
if (rface.equals(userPassword) && !rface.equals("")){
Try change this:
String rface = (String) dataSnapshot.child("pincode").getValue();
To this:
String rface = (String) dataSnapshot.child("pincode").getValue(String.class);
Use the following::
Object some = dataSnapshot.getValue();
String value = some.toString();

Ordering of data with Firebase Android

I'm running into a weird issues in which a Firebase query using orderByChild() doesn't actually order the data. Below is a snapshot of the data that I'm trying to order: (total is off for the sake of this example)
This is the code that I've used so far:
Query query = locationComment.orderByKey();
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() == null) {
return;
}
data.clear();
adapter.notifyDataSetChanged();
String userId;
String time;
String comment;
Map<String, String> commentMap = (Map) dataSnapshot.getValue();
for (Map.Entry<String, String> entry : commentMap.entrySet()) {
if(!((entry.getKey()).contains("total"))) {
String[] keyString = (entry.getKey()).split(",");
time = keyString[0];
userId = keyString[1];
comment = entry.getValue();
Date resultdate = new Date(Integer.parseInt(time));
data.add(new Comment(comment,
DateFormat.getDateTimeInstance().format(resultdate), userId));
adapter.notifyItemInserted(data.size());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I'm getting each and every key-value pair (barring total) except that it's not in order as expected (ordered by key). The Firebase docs say that the keys are ordered lexicographically if they can't be parsed into a 32 bit integer.
Either way though the order should be as shown in the image but the data that I get back while looping through the map is not in this order.
I would really appreciate it if someone could point me in the right direction. Thanks!
When you execute a query you get three things:
they keys
the values
the relative order between these
When you convert the result to a Map it can only hold two of these, so the order is lost. To prevent this, you should use the DataSnapshot.getChildren() method:
Query query = locationComment.orderByKey();
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() == null) {
return;
}
data.clear();
for (DataSnapshot child : dataSnapshot.getChildren()) {
...
}
adapter.notifyDataSetChanged();
}
Also see this example in the Firebase documentation on working with lists: https://firebase.google.com/docs/database/android/lists-of-data#listen_for_value_events
Looks like your data is badly structured.
Firebase has it's internal way of keying, and Firebase only can order those keys, with additional criteria if wanted. But since your key, is a custom key, firebase can't and won't order this branch.
The solution is to restructure your data in a meaningful way, like this
+ comments
+ total: 13
+ data:
+ 12uoasjihdou3
+ time: xx
+ userID: xx
+ comment: xx
+ 123tjiueoi134
+ 1piahf9hasheui
+ 6890324890oiuwer
Always use push() to generate new keys, do not create custom keys for lists, thats a total no go
FirebaseDatabase.getReference("comments").child("data").push({yourdata});
if you want to order your data, this is how you do that
FirebaseDatabase.getReference("comments").child("data").orderByChild("time");

Querying in Firebase with equalTo condition not working

I have database structure like this in Firebase
I want to search a search on this structure based on key number and get the parent key in return. Meaning if i search for 8860124421 then i should get -KTEtSR7chN8te1WaW-W in return .
I am doing it like this in android -
final String number = "8860124421";
DatabaseReference global_user_entry_ref = ApplicationContext.getGlobalDataBaseReference()
.child("User-Entry-2").getRef(); //Reference to User-Entry-2
Query query = global_user_entry_ref.orderByChild("number").equalTo(number);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
for(DataSnapshot friend: dataSnapshot.getChildren()){
String firebase_id = (String) friend.getKey();
Log.d("ContactSync","handle number "+firebase_id+" "+number+" "+friend);
}
Log.d("ContactSync","handle number outer "+dataSnapshot);
//user exist
}
else {
//user_does_not_exist
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("ContactSync","handle number oncancel "+databaseError.getMessage());
}
});
But I am not getting proper result , dataSanpshot in onDataChange looks like this -
DataSnapshot { key = User-Entry-2, value = null }
but i want to get dataSnapShot with parent of number key.
Please help , Thanks in advance
As #Frank van Puffelen stated in comments , the problem was that i was comparing a number from code with a string in the database , which does not match , Therefore the solution is to change
final String number = "8860124421";
to
final long number = 8860124421;

Read through Firebase snapshot - Android

I am having some issues with the Android version of my app. I completed the iOS version and am working on the Android data part. After I get the the snapshot in Swift, I can simply say something like this:
myList.addObject((snapshot.value["num"] as? String)!)
Then I have a nice list of of the numbers I need.
So far with Android it isn't that simple. Here is my how my data is structured and what I am looking for.
staffNUM
--staff
--12345677
-- num:112234
--2345689
-- num:090909
--44445677
-- num:999234
--6665689
-- num:888673
I can't change the way the data is created since the app for iOS is already being used.
Here is the question:
I have a reference to my database, which works.
mDatabase = FirebaseDatabase.getInstance().getReference();
Here is how I am calling the database after:
mDatabase.child("staffNUM/staff").addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
Log.d("Snap", "" + dataSnapshot.getValue());
StaffNum staffNum = dataSnapshot.getValue(StaffNum.class);
} else {
// something else happens
}
My results in the snapshot are:
D/Snap: {staff={12345677={num=112234},{2345689 ={num= 090909},{2345689 ={num= 090909}, {44445677={num= 999234}, {6665689 ={num= 888673}}
What is the best way to parse that data? I just need a list like I did in Swift to use in the app.
Edit:
Here is the class I added
#IgnoreExtraProperties
public class StaffNum {
public String num
public StaffNUM() {}
public StaffNUM(String num) {
this.num = num;
}
#Exclude
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("num", num);
return results;
}
}
If I call Log.d("num: ","" + staffNUM.num);
I still get
D/num:: null
Create a Staff POJO with the fields that you need from your Firebase structure
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot d : dataSnapshot.getChildren()) {
Staff staff = d.getValue(Staff.class);
Log.d("Snap", "num: " + staff.getNum());
}
}

Categories

Resources