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.
Related
Here is my Database architecture where Jobs is the main child and under that there will be users child, and an user can post multiple time. now i want to get all the posts done by all the users at once. is it possible to do that?
https://i.stack.imgur.com/DT3MO.jpg
setQuery(FirebaseDatabase.getInstance().getReference().child("Jobs")
Used that query to get all of the posts from all of the users but didnt work. any better solution for this problem?
setQuery(FirebaseDatabase.getInstance().getReference().child("Jobs").child("Uid");
this one works but cant use this one because i want to set free for all users to read data.
You can do the following:
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Jobs");
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()){
String key = ds.getKey();
for (DataSnapshot childSnapshot: ds.getChildren()) {
// get the child attribute under the random key
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
Since you have a reference to node Jobs then in the first iteration you can get the random keys (highlighted in your image), and in the second iteration you can get the attributes inside those keys.
Hi I am very new to Firebase and Java and I am looking for a hopefully easy answer. This is my database:
In my code the user inputs a name and number from separate text fields. I use the name to generate a child in "Client." What I want to do is take the "Name" and "Number" values and output them into text on my application. Problem is I am having a hard time figuring out how to reference the children of Client to output the data. Alex, David, Will are just placeholders for really any value the user inputs so how do I reference a child I don't know the name of?
First retrieve your Client datasnapshot
//Get datasnapshot at your "users" root node
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Client");
ref.addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Get map of users in datasnapshot
collectInfo((Map<String,Object>) dataSnapshot.getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
//handle databaseError
}
});
Then loop through clients, accessing their map
private void collectInfo(Map<String,Object> clients) {
//iterate through each client
for (Map.Entry<String, Object> entry : clients.entrySet()){
//Get client map
Map singleClient = (Map) entry.getValue();
// Do whatever you want to do with single clients
}
}
Here's a simple way:
DatabaseReference clientsRef = FirebaseDatabase.getInstance().getReference().child("Client");
clientsRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot clientSnapshot: dataSnapshot.getChildren()) {
System.out.println(clientSnapshot.getKey()); // "Alex", "David", "Will"
System.out.println(clientSnapshot.child("Name").getValue(String.class)); // "Alex", "David", "Will"
System.out.println(clientSnapshot.child("Number").getValue(String.class)); // "12345", "98765", "12345"
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
If you are just getting started with Android development and Firebase, I recommend taking this codelab first. It will save you much time (and questions) going forward.
my data look like this
and I simply want to add an object at index 3. How could I add it there. Is there any way to add an object without iteration or I have to iterate and getChildCount and then append new child("3") and it's data to it.
TransGenderBO transGenderBO = new TransGenderBO();
transGenderBO.setName("pushName");
transGenderBO.setAge(13);
mRef.child("").setValue(transGenderBO);
there is no method in mRef for getting child count and appending new item at 3 position..
Edit after using Frank code but still not working
Query last = mRef.orderByKey().limitToLast(1);
last.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int lastIndex = 0;
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
lastIndex = Integer.parseInt(childSnapshot.getKey());
}
TransGenderBO transGenderBO = new TransGenderBO();
transGenderBO.setName("pushName");
transGenderBO.setAge(13);
mRef.child(""+(lastIndex+1)).setValue(transGenderBO);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(mContext,databaseError.getMessage(),Toast.LENGTH_LONG).show();
}
});
There is a good reason that the Firebase documentation and blog recommend against using arrays in the database: they don't work very well for multi-user applications where users can be offline.
To add the next element to your array here, you'll have to download at the very least the last element of the array to know the index of the next element:
Query last = root.orderByKey().limitToLast(1);
last.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int lastIndex;
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
lastIndex = Integer.parseInt(childSnapshot.getKey());
}
root.child(""+(lastIndex+1)).setValue(true);
}
But this has an inherent race-condition. When multiple users are adding elements to the array at the same time, they may end up writing to the same index.
To prevent this you can use a Firebase transaction. With this you get the current value from a location and in exchange return the new value you want at that location. This ensures that no data is overwritten between users, but means that you have to download the entire array.
And neither of these scenarios works when a user is not connected to the network.
Firebase instead recommends using so-called push IDs, which:
Generate a always-increasing key that is guaranteed to be unique.
Do not require reading any data - they are generated client-side and are statistically guaranteed to be unique.
Also work when a user is offline.
The only disadvantage is that they're not as easily readable as array indexes.
Get your data like this
private ArrayList<TransGenderBO> transGenderBO;
FirebaseDatabase.getInstance().getReference().child("Main")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
transGenderBO = (ArrayList<TransGenderBO>) dataSnapshot.getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
and set your value like this
TransGenderBO transGender = new TransGenderBO();
transGender.setName("pushName");
transGender.setAge(13);
FirebaseDatabase.getInstance().getReference().child("Main").child(String.valueOf(transGenderBO.size())).setValue(transGender);
or U can set this way too
TransGenderBO transGender = new TransGenderBO();
transGender.setName("pushName");
transGender.setAge(13);
TransGenderBO.add(transGender);
FirebaseDatabase.getInstance().getReference().child("Main")
.setValue(transGenderBO);
In my solution I save data from Windows form application. There is no problem. I can list them in my android app. On this point I am trying to get key value to update the data, but always I get null.
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myReference = database.getReference();
Query myTopPostsQuery = myReference.child("DURUSLAR").orderByChild("kayitid").equalTo("1298843637");
myTopPostsQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren())
{
//Childadi =postSnapshot.getKey().toString();
String anahtar=postSnapshot.getKey().toString();
Toast.makeText(getApplicationContext(),anahtar,Toast.LENGTH_LONG);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Can you please help me?
When you execute a query at a location in the Firebase Database, it will search for the property you order/filter on in each child under that location. Since you query in /DURUSLAR, Firebase looks for /DURUSLAR/{something}/kayitid. Such a property does not exist, since you only have /DURUSLAR/{something}/{pushid}/kayitid.
To fix this problem you have two options:
query at a lower level
query to the property at its (fixed) path
The first option is to create the query at a lower level in the tree:
Query myTopPostsQuery = myReference.child("DURUSLAR/K6").orderByChild("kayitid").equalTo("1298843637");
Now the query is looking for /DURUSLAR/{pushid}/kayitid and it will work.
The second option is to query for the known path of the property:
Query myTopPostsQuery = myReference.child("DURUSLAR").orderByChild("K6/-KknsAR4_KFAeZ1HVXPW/kayitid").equalTo("1298843637");
It seems unlikely you want this here, but the approach may be helpful in other situations. Often when you need this approach with push IDs in the path, you'll want to look at http://stackoverflow.com/questions/40656589/firebase-query-if-child-of-child-contains-a-value.
The reason you are getting null is because you are doing orderByChild on the wrong level of data . According to your data u need to have a child K4-> which has a unique_id_push_id -> kayitid.Therefore you need to traverse the K4 in order to get 1298843637 for key kayitid.You can follow this tutorial to understand the retrieving of data from firebase .
Query myTopPostsQuery = myReference.child("DURUSLAR").child("k6").orderByChild("kayitid").equalTo("1298843637");
myTopPostsQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren())
{
//Childadi =postSnapshot.getKey().toString();
String anahtar=postSnapshot.getKey().toString();
Toast.makeText(getApplicationContext(),anahtar,Toast.LENGTH_LONG);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I have this firebase database
and i need to get all phone numbers of users , which listener shall i use to get all childes?
Every user is added as an object with user-ID as a name of that object, I need to retrieve this objects without knowing that user-ID
I searched the documentation , it's related to DataSnapshot but i couldn't get a DataSnapshot without a listener ! is it correct to seek a DataSnapshout or shall i use something else
First retrieve your users datasnapshot.
//Get datasnapshot at your "users" root node
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("users");
ref.addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Get map of users in datasnapshot
collectPhoneNumbers((Map<String,Object>) dataSnapshot.getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
//handle databaseError
}
});
Then loop through users, accessing their map and collecting the phone field.
private void collectPhoneNumbers(Map<String,Object> users) {
ArrayList<Long> phoneNumbers = new ArrayList<>();
//iterate through each user, ignoring their UID
for (Map.Entry<String, Object> entry : users.entrySet()){
//Get user map
Map singleUser = (Map) entry.getValue();
//Get phone field and append to list
phoneNumbers.add((Long) singleUser.get("phone"));
}
System.out.println(phoneNumbers.toString());
}
This listener will only retrieve the datasnapshot when explicitly called. Consider storing a list of numbers under an "allPhoneNumbers" node in addition to your users node. This will make your datasnapshots lighter and easier to process when collecting all numbers. If you have say hundreds of users, the "users" datasnapshot will be way too big and the "allPhoneNumbers" list will be far more efficient.
The above code was tested on your sample database and does work. However, your phone field may need to be casted to String or int depending on your user's phone instance field's type.
DatabaseReference ref1= FirebaseDatabase.getInstance().getReference();
DatabaseReference ref2,ref3,ref4;
ref2 = ref1.child("User");
ref2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Userlist = new ArrayList<String>();
// Result will be holded Here
for (DataSnapshot dsp : dataSnapshot.getChildren()) {
Userlist.add(String.valueOf(dsp.geValue())); //add result into array list
}
/* userlist will store all values of users, then point to every userlist item
and get mobile numbers*/