Firebase database count number of child - android

I want to get first the number of children (if any). Then I want to add 1 to number of children, so the added children will be +1 from the existing.
So when I add new question, II want to add number 3. But I get it added as number 1 and it updates the existing number.
int count = 0;
public void addQuestion(View view) {
mDatabase = FirebaseDatabase.getInstance();
mRef = FirebaseDatabase.getInstance().getReference("Questions");
count = count + 1;
String nr = String.valueOf(count);
GetData getdata = new GetData(qs, A, B, C, correct);
mRef.child(nr).setValue(getdata);
Toast.makeText(AdminPanel.this, "Adding question...", Toast.LENGTH_SHORT).show();
}

You can use the push function and it will generate a unique key, so you don't have to worry about giving then unique numbers.
mRef.push();
you can also get the key that was generated if you need it,
https://firebase.google.com/docs/database/admin/save-data#getting-the-unique-key-generated-by-push
or just retrieve all the children (your questions) when needed.
I suggest you read the documentation, I found there all the information I needed.
https://firebase.google.com/docs/database/

Related

The getKey method in Firebase does not return the exact push id generated

edit = (EditText) findViewById(R.id.addCat);
addCate = edit.getText().toString();
Catcat add;
String x = myRef.child("sfasf").push().getKey();
add= new Catcat(addCate,x);
myRef.push().setValue(add);
When I use this code, the String x differs from the actual push id by 1 letter at the end. Is this intentional or am I using the method wrong?
You called push() twice, which leads to two different keys. There are a couple ways to push an object onto Firebase and get the key.
You can get the key first, then use the key to get and update the child.
String x = myRef.child("sfasf").push().getKey();
add = new Catcat(addCate,x);
myRef.child("sfasf").child(x).setValue(add);
Or you can first push the object then retrieve the key.
add = new Catcat(addCate,x);
DatabaseReference ref = myRef.child("sfasf").push(add)
String x = ref.getKey()

Looping out JSON Array using ArrayList

I am trying to learn retrofit and I have made successful attempts at posting data and now I am trying to retrieve JSON array which looks as follows:
{
"result": "success",
"message": "All Questions Have Been Selected",
"question": {
"all_question_ids": ["1","2","3"]
}
}
I am using the following getter
public ArrayList getAll_question_ids(){
return all_question_ids;
}
I am retrieving using Retrofit as follows
if (resp.getResult().equals(Constants.SUCCESS)) {
SharedPreferences.Editor editor = pref.edit();
Log.d("Question_IDs", "getAllQuestionID() = " + response.body().getQuestion().getAll_question_ids() );
editor.putString(Constants.All_QUESTION_IDS,((resp.getQuestion().getAll_question_ids().toString())));
editor.apply();
}
progress.setVisibility(View.INVISIBLE);
It is here that I am stuck, as I am retrieving the array ok but I am unsure how to loop out the Array which is now stored in Shared Preferences.
When I place a toast to show me how the IDs are coming across, my toast confirms the data as [1,2,3]
The goal is to add a dynamic button and the individual ID, i.e button 1, button 2 etc every-time the loop is iterated.
I have tried the following:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
for (int i =0; i < questionNumber.length(); i++) {
try {
/*Dynamically create new Button which includes the question name
*/
AppCompatButton btn_question = new AppCompatButton(getActivity());
/*LayoutParams (int width, int height,float weight)
As LayoutParams is defaulted in px, I have called a method called dpToPX to make sure
the dynamically added EditText is the same size on all devices.
*/
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(280), dpToPx(45), 1);
btn_question.setBackgroundColor(Color.parseColor("#3B5998"));
btn_question.setTextColor(Color.WHITE);
// btn_question.setText(String.valueOf(x));
btn_question.setText("Question "+ pref.getString(Constants.All_QUESTION_IDS,""));
btn_question.setGravity(Gravity.CENTER);
//generate unique ID for each new EditText dynamically created
View.generateViewId();
//Log.d("TEST VALUE", "Question1 generated ID = " + btn_question.generateViewId());
params.setMargins(0, dpToPx(10), 0, dpToPx(10));
btn_question.setPadding(0, 0, 0, 0);
btn_question.setLayoutParams(params);
allEds.add(btn_question);
mLayout.addView(btn_question);
} catch (Exception e) {
Log.d(TAG, "Failed to create new edit text");
}
}
However the above is adding the value as it appears in the array e.g [1,2,3] which is obviously not what I want.
I have added a photo in case my explanation isn't clear. I want a button with 1 number added to it each time the loop iterates but I am unable to figure this out.
I have looked through lots of resource but cannot find an answer that is relevant to my problem, although, if there is, I am not familiar enough to recognise a similar issue.
If someone can offer some assistance, I would appreciate it!
When you call editor.putString(Constants.All_QUESTION_IDS,((SOMETHING.toString())));, what is actually stored depends on the implementation of the toString method in the type of SOMETHING (in this case String[]). So avoid doing that. Instead, since you're already using Gson or Jackson (or others), store the question_idsas JSON:
final String jsonIds = gson.toJson (resp.getQuestion().getAll_question_ids());
editor.putString(Constants.All_QUESTION_IDS, jsonIds);
Your actual stored value no longer depends on the implementation of something that you don't control (String[].toString). It is a valid JSON array and regardless of what tool/library you use to read it back, it's valid.
Now, to read back the stored data:
final String storedJson = pref.getString(Constants.All_QUESTION_IDS, null);
if (null == storedJson) {
// TODO: No question ids found
}
final String[] ids = gson.fromJson (storedJson, String[].class);
for (int i = 0; i < ids.length; i++) {
// make your buttons
}
This is a problem of saving and then reading out a List of items (in this case, String instances).
You've chosen to save the list by calling editor.putString() with a value of getAll_question_ids().toString(). That toString() call is going to return a string representation of your list, or, in other words, a String instance with the value [1, 2, 3]. At this point, you no longer have a List proper, but a String that looks like a list.
This is all technically fine, but it means you have to take this into account when you're trying to read out that list.
You've written this to read the list back out:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
Once this line executes, questionNumber will be a String instance with the value [1, 2, 3]. Again, this is fine, but now we come to the key point: we have to convert this String back into a List.
If you know for sure that the values in this list won't have commas in them, you can do it easily:
Trim the braces off the string using substring()
Create a String[] using split()
Convert your array to a list using Arrays.asList() (you could even skip this step since iterating over an array is just as easy as iterating over a list)
Put that together and you get:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
questionNumber = questionNumber.substring(1, questionNumber.length() - 1);
String[] array = questionNumber.split(", ");
List list = Arrays.asList(array);
At this point, you can iterate over your array or list:
for (String value : list) {
...
btn_question.setText("Question " + value);
...
}

Firebase database Android - check if nickname exists if not create another one until we get a new nickname

I want to create a nickname to each user who login to my app for the first time. The nickname is the user's firstname and I add a number to it from 1 to 9 000 000. For example kevin Bond could get the nickname kevin123456.
I have all the nicknames stored in a node of my firebase database. Two users shouldn't have the same nickname, so I have to check wether the nickname already exists or not in my database. If it already exists, I want to create another nickname until I get a new nickname.
I currently:
- add a listener to the usernickname node
- in onDataChange I have a for loop to create a nickname, then I check if it already exists with dataSnapshot.hasChild(newNickname). If the nickname doesn't exist I do what I want with the nickname and break the for loop, if it exists I loop the for loop to try another nickname. Here is my code:
mFirebaseDatabase.getReference().child("usernickname").orderByKey().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (int i = 0; i < 9000000; i++) {
final Random random = new Random();
randomNumberId = random.nextInt(9000000);
String newNicknameId = mFirstnameId + String.valueOf(randomNumberId);
//if the nickname already exists we restart the for loop to get a new random number
if (dataSnapshot.hasChild(newNicknameId)) {
i++;
//else if the nickname doesn't exists we create it and stop the loop
} else if (!dataSnapshot.hasChild(newNicknameId)) {
//do what I want...
break;
}
}
}
});
My issue is that I currently have to download all the nicknames node or at least all the nicknames beginning with the user's firstname. It is an issue for me because it could cost me a lot in GB downloaded if I have a lot of users :D
I guess I could use datasnapshot.exists() to check if the nickname exists without having to download all the nicknames, but I don't see how to create another nickname if it already exists. I cannot have a for loop outside of the listener and break it inside of the listener when needed.
Do you see what is the best way to create a new nickname for my users?
Thank you,
Alex
A better approach for unique nicknames would be to append a time stamp to the name:
username + new Date().getTime();
This will add a unique long like 1493808526335 and you can be certain that two users are unlikely to register in the same millisecond with the same name.
You need to use another aproch to check if a user exists. You need to change this line:
dataSnapshot.hasChild(newNicknameId);
with
dataSnapshot.child("userName").getValue().equals(newNicknameId);
In which userName is the name of field in which you store the newNicknameId, assuming your key -> value pair looks like this: userName: "newNicknameId"
Hope it helps.
final Query query = mFirebaseDatabase.getReference().orderByChild("usernickname").equalTo("yourNewNickName");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//here means the value exist
//do whatever you want to do
} else {
//here means the value not exist
//do whatever you want to do
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
use this will solve your problem. :)

How to retrieve data from all pages?

I want to get some data from all users in Users table. I've found that I have to use Data paging. I've written the same code as described in Feature 47->https://backendless.com/feature-47-loading-data-objects-from-server-with-sorting/ (because I also
have to sort) , but then I've figured out that this code takes data only from first page. Then , I decided that I have to go to the next page and read it , until its size is not equal to zero. Below,you can see my wrong solution:
QueryOptions queryOptions = new QueryOptions();
List<String> list = new ArrayList<String>() ;
list.add("point DESC") ;
queryOptions.setSortBy(list);
BackendlessDataQuery backendlessDataQuery = new BackendlessDataQuery();
backendlessDataQuery.setQueryOptions(queryOptions);
Backendless.Data.of(BackendlessUser.class).find(backendlessDataQuery, new AsyncCallback<BackendlessCollection<BackendlessUser>>() {
#Override
public void handleResponse(BackendlessCollection<BackendlessUser> one) {
while(one.getCurrentPage().size()>0) {
Iterator<BackendlessUser> it = one.getCurrentPage().iterator();
while (it.hasNext()) {
//something here,not so important
}
one.nextPage(this);// here I want to get next page,
//but seems like it does not work, cause my loop became infinite
}
}
I think that I have to use nextPage method with AsyncCallback instead of one.nextPage(this) , but if I do so , the method couldn't keep up with the loop. So, how can I solve my problem?
I actually can't find the problem with your solution, but I solved this problem using:
int tot = response.getTotalObjects()
to get the total number of objects at the first response. Then use a loop until your list of objects has size = tot. In each loop you make a query setting the offset equals to the current size of the list.

Firebase - random query

I am making and application that show a first screen with a bunch of randomly picked data from a node in Firebase database.
To present user with different data every time is kind of important for my application
Is there anyway to achieve this in native Android, all snapshots are of same model
There is no direct way provided by firebase database but you can do this using Collections.shuffle()
What i did was,Take the snapshot and store it in an arraylist.
private ArrayList<Integer> array=new ArrayList<>();
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot imageSnapshot : dataSnapshot.getChildren()) {
MyClass myclass = imageSnapshot.getValue(MyClass.class);
array.add(myclass.someFunction());
}
}
Then call the shuffle method on this array list.
Collections.shuffle(array); // randomize the arraylist
Now you can do whatever you want with this randomized arraylist.
Don't think there is a way to randomly grab data from the Firebase database as all queries that you can construct end up being deterministic in some way, either based on the generated push ids (which in turn are based on time) or some other key ordered lexicographically. I think the best way would be to grab a list of data from a node and randomly choose the data client side.
There actually is a possibility to do that without Loading the whole list client side. First you have to generate a numeric id either as child id or as an extra node.
That how your database would look like:
notes:
-KXe8LJJzczEJs3YYwRe
numericid : 001
-KXeePWrWBXvpw4g9n0p
numericid : 002
or
notes:
001
002
to create the numbers as String you can use DecimalFormat
String newint = new DecimalFormat("000").format(oldint);
Now you can get the children count in your valueeventlistener an use Math.random() to get a random child, e.g. for the second Database Design
FirebaseDatabase().getInstance().getReference().child("notes").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Long childrencount = dataSnapshot.getChildrenCount();
if(childrencount.equals(0))return;
int random = getRandomInteger(safeLongToInt(childrencount), 1);
String selectedchild = new DecimalFormat("000").format(random);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
You also need to add these safeLongtoInt and getRandomInteger
public static int getRandomInteger(int maximum, int minimum){
return ((int) (Math.random()*(maximum - minimum))) + minimum;
}
public static int safeLongToInt(long l) {
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
throw new IllegalArgumentException
(l + " cannot be cast to int without changing its value.");
}
return (int) l;
}
selectedchild is your random child id.

Categories

Resources