A part of my current Firebase Database looks like this:
I have only expanded one to show you the general structure of the JSON tree.
Within the "medium" child, I have 3 more sub-children of "a bit of both", "nature" and "urban". I already know how to extract the mappings under each trail (trailA, trailB, etc) in Android but I am not sure how to loop through the 3 sub-children mentioned above. Is it a for loop for DatabaseReferences or something else? My current code works but it is very inefficient, particularly when the data grows even bigger.
I am really new to Firebase and am only beginning to understand the structuring so please forgive me if this question is relatively basic. Any help (and further explanations) would be deeply appreciated.
My current code for extracting data from the sub-children is in the form of hard-code:
List<DatabaseReference> dbList = new ArrayList<>();
DatabaseReference refB = firebaseDatabase.child("medium").child("a bit of both");
DatabaseReference refC = firebaseDatabase.child("medium").child("nature");
DatabaseReference refC = firebaseDatabase.child("medium").child("urban");
dbList.add(refB);
dbList.add(refC);
for (DatabaseReference x : dbList) {
x.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
TrailInformation ti = ds.getValue(TrailInformation.class);
trailInformationCallback.onCallBack(ti);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Related
CABARAN is an unknown Uid. it is not a text. Right now I have the uid, and I want to get value for the tajukPenuh.
This is the code and I still can't get the value.
FirebaseDatabase.getInstance().getReference().child("karangan").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Karangan karangan = child.child(karanganID).getValue(Karangan.class);
if (karangan != null) {
String tajukPenuh = karangan.getTajukPenuh();
holder.getTextViewKaranganID().setText("Karangan Tajuk: " + tajukPenuh);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
You could in theory do a query like this:
FirebaseDatabase.getInstance().getReference().child("karangan")
.orderByChild("-LYgFIl4Xiv_Ls51Slvh/uid").equalTo("-LYgFIl4Xiv_Ls51Slvh")
.addListenerForSingleValueEvent(new ValueEventListener() {
But the problem is that you'd need a lot of indexes in your rules, which may be technically possible, but is unfeasible for most real usage.
Your current data structure makes it easy to find all the child nodes for CABARAN, but it does not make it easy to find CABARAN for a given child node. To allow that use-case to run efficiently, you should expand your data structure with a so-called reverse index that maps back to CABARAN from the value that you know. So something like:
"myIndex": {
"-LYgFIl4Xiv_Ls51Slvh": "CABARAN",
"-LzfFIl4Xasas51Slads": "CABARAN",
"-Lasddas981398asdh1h": "CASITWO"
}
This is an additional data structure, that you will have to keep up to date when you're writing the rest of the data. But with this structure, it now becomes very easy to determine that -LYgFIl4Xiv_Ls51Slvh maps to CABARAN.
For more on this, see my answer here: Firebase query if child of child contains a value
How do I display the RequestsID on a recyclerview only when stallID = 1?
How should my DatabaseReference be?
update:
Your database structure would not support orderByChild() from top node. So you would have to set your database reference upto the node food and then you can use orderByChild() query to find the Childs with certain stallID.
This, in code would look something like this:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Requests").child("1542..").child("foods");
ref.orderByChild("stallID").equalTo(stallIDYouWant).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()){
// do here what you wanted
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
// ToDo: don't ignore this, do something for errors
}
)};
This would in fact not help you much because you'd have to set this for every unique id in your database structure.
So the better thing to do here would be make your database structure different. To know more how to make your structure good for this operation, refer this answer.
In my Android app I am using this code to retrieve posts from firebase database:
ArrayList<Post> posts = new ArrayList<>();
dbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dsp : dataSnapshot){
String author = dsp.child("author").getValue().toString();
String text = dsp.child("author").getValue().toString();
posts.add(new Post(author,text));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But this returns all the posts and if there would be many of them I think it wouldn't be good idea to keep them all in memory. Is there a way to get only certain number of node's children? For example at the beginning I would like to get 10 first ones, then 10 more and so on
You're looking for limitToFirst() (or limitToLast()).
ArrayList<Post> posts = new ArrayList<>();
Query query = dbRef.orderByKey().limitToFirst(10);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dsp : dataSnapshot){
String author = dsp.child("author").getValue().toString();
String text = dsp.child("author").getValue().toString();
posts.add(new Post(author,text));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
See the Firebase documentation on queries for an explanation of all its capabilities.
You need to use your DataSnapShot to retrieve more specific information.
There is a Firebase guide for Android that explain how to utilize: Firebase Android Guide
If you would prefer to see on Stack OverFlow a small example this answer here seems to be doing somewhat of what you would like to achieve: Firebase Get Children
I'm new to Firebase and NoSQL. I have an Android Demo, with a City Autocomplete Text Field in which I want to populate the cities I have from my Firebase DB, while typing.
{ "cities":{
"Guayaquil":true,
"Gualaceo":true,
"Quito":true,
"Quevedo":true,
"Cuenca":true,
"Loja":true,
"Ibarra":true,
"Manta":true
}
}
This is what I have so far.
How can I retrieve from the DB cities that start with a letter (input from keyboard)? If I start typing "G", I want to receive "Guayaquil" and "Gualaceo".
If I use orderByValue always returns an empty snapshot.
If I use orderByKey return the whole list.
Query citiesQuery = databaseRef.child("cities").startAt(input).orderByValue();
citiesQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> cities = new ArrayList<String>();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
cities.add(postSnapshot.getValue().toString());
}
Note: If you can recommend a better data structure, you're welcome.
#NicholasChen has identified the problem. But here's the way you'd implement using the 3.x SDK:
DatabaseReference cities = databaseRef.child("cities")
Query citiesQuery = cities.orderByKey().startAt(input).endAt(input+"\uf8ff");
citiesQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> cities = new ArrayList<String>();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
cities.add(postSnapshot.getValue().toString());
}
By starting at the user input and ending at the last string that starts with the user input, you get all matching items
For relatively short lists of items Ryan's approach will also work fine. But the above Firebase query will filter server-side.
Update
I just ran this code:
DatabaseReference databaseRef = FirebaseDatabase.getInstance().getReference("39714936");
String input = "G";
DatabaseReference cities = databaseRef.child("cities");
Query citiesQuery = cities.orderByKey().startAt(input).endAt(input + "\uf8ff");
citiesQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> cities = new ArrayList<String>();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
cities.add(postSnapshot.getValue().toString());
}
System.out.println(cities);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
And it printed:
true
true
So clearly matches two cities.
Feel free to test against my database: https://stackoverflow.firebaseio.com/39714936
Try something like this to iterate over the children in the cities snapshot and add all the cities to an ArrayList of Strings.
ArrayList<String> cityList = new ArrayList<>();
databaseRef.child("cities").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
cityList.clear();
for (DataSnapshot data : dataSnapshot.getChildren()){
cityList.add(data.getKey);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "getUser:onCancelled", databaseError.toException());
// ...
}
});
Editing this paragraph for clarity:
This will get all your cities read into the program memory so you can use that data to display the cities to the user. If the city list changes, so will the data the user sees. If the user is not online, this will not work. This puts a real time, online only listener on the database.
The logic in my mind is something like:
Set a value listener on the text box.
When user types, make a view display all the items in the array list
that start with the same substring that was typed.
Handle arrayIndex errors of course.
Hopefully this will get you on the right track. I am sure there are other ways you could implement it but this is what I would personally do. If you need help with the code to display the correct cities, start a chat with me and I can brainstorm with you.
Of course OrderByValue returns nothing because that's the booleans you have.
you can use the startAt and endAt methods to do so. (The below is Firebase 2.0's Code)
var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
ref.orderByKey().startAt("b").endAt("b\uf8ff").on("child_added", function(snapshot) {
console.log(snapshot.key());
});
You can explore more on the Firebase 3 documentation site here.
What Ryan did was right. However, you have to implement startAt on the dataSnapshot to make sure that your "live" search works.
This is my JSON Tree in the Firebase Realtime database. I just want to iterate and read through all of the items under questions. I think I am doing something silly wrong.
Under the "My App" - I have a "questions" branch and saving a bunch of data under it. Below is the tree and my code. In the code I point out the part that is crashing. Circled in red below are what I am trying to read.
public void readfromFireDB() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference().child("questions");
// Read from the database
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
**THIS IS THE ERROR, ques comes back as NULL**
Question ques = dataSnapshot.getValue(Question.class);
String key = dataSnapshot.getKey();
}
#Override
public void onCancelled(DatabaseError error) {
}
});
}
This Issue: ques in onDataChanged() comes back as a null reference. May be I am not accessing the children under questions properly?
By using a ValueEventListener you're accessing the list of questions, not an individual question.
To access each individual question, you'll need to loop over the snapshot:
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot questionSnapshot: dataSnapshot.getChildren()) {
Question ques = questionSnapshot.getValue(Question.class);
String key = questionSnapshot.getKey();
}
}