firebase check if child exists - java

So I have my backend using firebase. What I'm aiming to do is to add user matches to a user id. However, when the user initially signs up, they have no matches. What I'm trying to do is to check whether or not the "match" child exists within a user child, if not a list child is created and the first match is stored. However, if it already exists, the match is simply added. Here's my code:
public void setMatch(final String match){
final Firebase ref = new Firebase("FIREBASEURL");
final Firebase userRef = ref.child("Flights").child(userName);
userRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("does the child exist? " + dataSnapshot.child("matches").exists());
if(!dataSnapshot.child("matches").exists()){
ArrayList<String> matches = new ArrayList<String>();
matches.add(match);
Firebase matchesRef = userRef.child("matches");
matchesRef.setValue(matches);
userRef.removeEventListener(this);
}else if(dataSnapshot.child("matches").exists()){
Map<String, Object> matches = new HashMap<>();
matches.put("matches", match);
userRef.child("matches").push().setValue(matches);
userRef.removeEventListener(this);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
Currently, the value is being added twice (the else if is called twice if the field already exists/its called if it doesn't). I'm not sure what I'm doing wrong.

This sounds pretty overcomplicated. In the Firebase Database, it's often best to separate read and write operations as much as possible. And while push ids are a great way to store data in a chronological way; if items have a natural key, it is often better to store them under that key.
For example if your String match is really a String matchId, you can ensure that each match is present at most once by using matchId as the key.
userRef.child("matches").child(matchId).setValue(true);
This operation is idempotent: it will give the same result no matter how often you run it.
You'll note that I don't check of matches already exists: the Firebase Database automatically creates all nodes that are needed to store the value and it automatically removes all nodes that have no values under them.

It looks like you create the field if it doesn't exist in the if block, and then test to see if that field (which was just created) exists, and it now does, so it adds it again. The removeEventListener call will remove the listener, but will not stop the current code from completing.
Try:
if(!dataSnapshot.child("matches").exists()){
ArrayList<String> matches = new ArrayList<String>();
matches.add(match);
Firebase matchesRef = userRef.child("matches");
matchesRef.setValue(matches);
userRef.removeEventListener(this);
return;
}else if(dataSnapshot.child("matches").exists()){
Map<String, Object> matches = new HashMap<>();
matches.put("matches", match);
userRef.child("matches").push().setValue(matches);
userRef.removeEventListener(this);
}
Adding the return statement should quite the current call, and still disable the Listener as you intended.

Related

is there any way to fetch records from firebase realtime database whose attribute has a value in my predefined list?

Basically what I am trying to do is I have a database with the name users having an attribute username. I have some usernames in one list and I want to show details of these users only whose username is present in the list. How can I write a query to fetch details of those users only whose username is found in this list? And note that there is no lexicographical ordering so i can't use startAt() and endAt() functions as well.
code snippet:
=> myList contains usernames. This code doesn't yield accurate results.
Any help would be really appreciated! Thank you!
FirebaseRecyclerOptions<MainModel> options =
new FirebaseRecyclerOptions.Builder<MainModel>()
.setQuery(FirebaseDatabase.getInstance().getReference().child("users").orderByChild("username")
.startAt(myList.get(0)).endAt(myList.get(myList.size()-1)),MainModel.class).build();
As already mentioned in the comment, the Firebase-UI library doesn't help in your case, because it doesn't allow you to pass multiple queries to the FirebaseRecyclerOptions object. So you need to perform a separate query and use the combined result.
When you are calling .get() on a Firebase Realtime Database query object, you are getting back a Task object. So the key to solving this problem is to use whenAllSuccess(Collection> tasks). In your case, it should look like this:
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
DatabaseReference usersRef = db.child("users");
Query query = usersRef.orderByChild("username");
List<Task<DataSnapshot>> tasks = new ArrayList<>();
for (String username : myList) {
tasks.add(query.equalTo(username).get());
}
Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//Do what you need to do with your list.
for (Object object : list) {
MainModel mm = ((DataSnapshot) object).getValue(MainModel.class);
if(mm != null) {
Log.d("TAG", mm.getUsername());
}
}
}
});
Assuming that you have in your MainModel class a getter called getUsername(), the result in your logcat will be all the usernames of all returned children.

How can I write Arraylist data to Firebase Database in Java?

I have an android app with firebase as the backend. I have an activity where the use populates a list for a recycler view which they want to save to the database.
Below is a screenshot of the Firebase Realtime Database structure of my database:
The values of children in the Values node is what I'm using to create the id for new data that is added.
I have an arraylist of sales objects populated by the user which is to be saved in a new Sales node and would like to use the value in Values/Sales to populate the ids for each item in the arraylist.
Below is my code for saving that data. However in the database, only the last item in the arraylist is saved.
ArrayList<Inventory> salesArrayList = new ArrayList<>();
------------------- Code for populating data into the arraylist ----------------------
for (int i = 0; i < salesArrayList.size(); i++){
int finalI = i;
databaseReference.child("Values").child("Sales").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String newCount = String.valueOf(Integer.parseInt(String.valueOf(snapshot.getValue()))+1); // Gets the original value saved and adds 1 to it to be used as the id in the 'Sales' node
DatabaseReference countReference = databaseReference.child("Sales").child(newCount);
countReference.child("date").setValue(currentDate);
countReference.child("name").setValue(salesArrayList.get(finalI).getName());
countReference.child("quantity").setValue(salesArrayList.get(finalI).getQuantity());
countReference.child("unit_price").setValue(salesArrayList.get(finalI).getValue()/salesArrayList.get(finalI).getQuantity());
countReference.child("value").setValue(salesArrayList.get(finalI).getValue());
databaseReference.child("Values").child("Sales").setValue(Integer.parseInt(newCount)); // Updates the number of sales
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
If there are 7 items in the arraylist, I'd like a way to have all them saved in the Sales similar to the Purchases node rather than only the last item on the arraylist.
This doesn't work (and I'm surprised it even compiles):
databaseReference.child("Values").child("Sales")(Integer.parseInt(newCount)); // Updates the number of sales
To write back the incremented number of sales:
snapshot.getReference().setValue(snapshot.getValue(Long.class)+1);
Note that in general using such sequential numeric keys in Firebase is an anti-pattern. I highly recommend using Firebase's native push() keys, which are also always incrementing but provide much stronger guarantees outside of that. To read more about that, check out: Best Practices: Arrays in Firebase.
I can't comment, but I can answer, but I don't know if my answer will be correct.
If you are only updating the last item on the array it would seem to me that you are at each iteration overwriting the same object and finally saving only that object to the database.
Is there an append method? Or does the documentation say anything about this?
This for me seems the most probable cause.
Maybe you each time need a new instance of the 'databaseReference'-object.

Is there any way to make my app wait for data to be retrieved from Firebase before continuing the code?

My app has to display a list of names on ListView. Those names are stored within Cloud Firestore in the following manner:
Collection: users - Documents: Organized by user UID - Field: name (I must note that there are other fields for each user too, however i need to retrieve the name field specifically)
To accomplish this, I have a first list that retrieves all documents or user UIDs. That first list is then used within a for loop to retrieve the name of each user in the users collection.
However, due to Firebase retrieving data asynchronously, some names are usually missing and they end up being displayed in a disorganized manner (not consistent with the order in which uids were passed from the first list).
If anyone could give me any insight on how to make Firebase wait for data to be retrieved before continuing with the for loop it would be greatly appreciated!
Below is some of my code to give you a better idea of what I am doing.
This first part of the code, which successfully retrieves all documents (uids) and puts them on a list
subTopicsDatabase.collection("schoolTopics").document(docKey).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot document = task.getResult();
if (document.exists()) {
List<String> list = new ArrayList<>();
Map<String, Object> map = document.getData();
if (map != null) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
list.add(entry.getValue().toString());
}
}
}});
The second part of the code, which doesnt work due to Firebase's asynchronous behavior.
for (int i = 0; i<list.size(); i++) {
String uid = list.get(i);
Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();
subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
String stName = documentSnapshot.getString("name");
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, testList);
adapter.notifyDataSetChanged();
sListView2.setAdapter(adapter);
}
}
});
}
You need to store the elements and in last of the for loop, you have to show the names list.
As you said you are getting a list of All UID's now you want their names on a list. I had updated your code to work.
// Create a Hashmap Object which has Key as UID and Name as Key
HashMap<String,String> hashMap = new HashMap<>();
for (int i = 0; i<list.size(); i++) {
final String uid = list.get(i);
Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();
subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
//Store Your UID and Name in Hashmap
String stName = documentSnapshot.getString("name");
hashMap.put(uid,stName);
}
//Check if it is last index of array then show the names list
if(i==list.size()-1){
showListInAdapter(hashMap);
}
}
});
}
private void showListInAdapter(HashMap<String,String> hashMap) {
//now convert your hashmap into a list of name and get Your Names List and show in Adapter
ArrayList<String> listOfNames = new ArrayList<>(hashMap.keySet());
//Set list to Adapter
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, listOfNames);
sListView2.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
Your assertion that it doesn't work because of Firebase's asynchronous behavior is incorrect. The reason that your view is not displaying the way you want it to, is because you are updating your adapter every single time you receive a document back from Firebase.
In psuedocode, this is what should happen:
// Create function with completion block - i.e. fetchTopicNames
//
// Create array to hold fetched String values - i.e. topicNames
// For loop to request each document
// add String value to `topicNames`
// if current iteration is last iteration, finish forLoop and return topicNames
//
In another method, call your newly created method, update your adapter with your full list of topicNames. You can also then perform other operations on your Array like filtering and sorting. There is probably a more efficient way as well, I'm just giving you the most basic way to accomplish your task.
you can simulate fetching user synchronous by making recursion (function which call it self until index becomes bigger then size of list of uids).
So first thing you want to define adapter and List of strings (which represent user names). When you do that, you can call recursion, which will populate your List and notifyDataSetChanged. Here is the example
// Define empty list of user names, which you will populate later with recursion
List<String> userNames = new ArrayList<String>();
// Connect adapter with empty list
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, userNames);
// Set adapter to ListView
sListView2.setAdapter(adapter);
// Call recursion with list of uids and starting index of 0
getUserSync(list, 0);
private void getUserSync(List<String> list, int i) {
if (i < 0 || i > list.length - 1) {
// If index i is out of bounds for list, we break the recursion
return;
}
String uid = list.get(i);
Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();
subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
// When we load document, we fetch name and add it to the list which is connected to adapter
// After that, we call adapter.notifyDataSetChanged which will update ui
// When all that is done, we call getUserSync, to fetch user name for next uid
if (documentSnapshot.exists()) {
String stName = documentSnapshot.getString("name");
if (stName != null) {
userNames.add(stName);
adapter.notifyDataSetChanged();
}
}
getUserSync(list, i++);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// If enything goes wrong, we break the recursion
return;
}
});
}
If you have any troubles, feel free to comment..
I recommend that you follow the Guide to App Architecture and use a LiveData observer to keep the ListView updated. You can follow this tutorial and insert your Firebase access in the Repository class.
Changing your code to fit the MVVM pattern may require a bit of work but it will also make your app run better and simplify some development later.

How to remove specific entries from Firebase in Android Studio?

I have the following structure in Firebase:
where the entries contain an image and a timestamp. The name of each entry is randomly generated and the timestamp is an int larger than 0.
What I want:
To delete the entries with a timestamp smaller than a specific value.
My current progress:
I should be able to retrieve the list of sorted entries with the following code:
Firebase myFirebaseRef = new Firebase("https://someURL.firebaseio.com/");
Firebase listOfObjects = myFirebaseRef.orderByChild("timestamp").getRef();
What's next:
I want to iterate through and/or somehow only remove specific entries in the listOfObjects. Possibly with some if-statements?
First I believe you could use endAt() to filter just the entries you need
Firebase listOfObjects = myFirebaseRef.orderByChild("timestamp").endAt(myLimit).getRef();
Then something like this should work
listOfObjects.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot querySnapshot) {
queurySnapshot.getRef().removeValue()
}
}

How to check if a Set contains an object which has one member variable equal to some value

I have a java Set of Result objects. My Result class definition looks like this:
private String url;
private String title;
private Set<String> keywords;
I have stored my information in a database table called Keywords which looks like this
Keywords = [id, url, title, keyword, date-time]
As you can see there isn't a one-to-one mapping between an object and a row in the database. I am using SQL (MySQL DB) to extract the values and have a suitable ResultSet object.
How do I check whether the Set already contains a Result with a given URL.
If the set already contains a Result object with the current URL I simply want to add the extra keyword to the Set of keywords, otherwise I create a new Result object for adding to the Set of Result objects.
When you iterate over the JDBC resultSet (to create your own set of Results) why don't you put them into a Map? To create the Map after the fact:
Map<String, List<Result>> map = new HashMap<String, List<Result>>();
for (Result r : resultSet) {
if (map.containsKey(r.url)) {
map.get(r.url).add(r);
} else {
List<Result> list = new ArrayList<Result>();
list.add(r);
map.put(r.url, list);
}
}
Then just use map.containsKey(url) to check.
Normalization is your friend
http://en.wikipedia.org/wiki/Database_normalization
If it's possible, I suggest changing your database design to eliminate this problem. Your current design requries storing the id, url, title and date-time once per key word, which could waste quite a bit of space if you have lots of key words
I would suggest having two tables. Assuming that the id field is guarenteed to be unique, the first table would store the id, url, title and date-time and would only have one row per id. The second table would store the id and a key word. You would insert multiple rows into this table as required.
Is that possible / does that make sense?
You can use a Map with the URLs as the keys:
Map<String, Result> map = new HashMap<String, Result>();
for (Result r : results) {
if (map.containsKey(r.url)) {
map.get(r.url).keywords.addAll(r.keywords);
} else {
map.put(r.url, r);
}
}
I think that you need to make an override on equals() method of your Result class. In that method you will put your logic that will check what you are looking for.
N.B. You also need to know that overrideng the equals() method, you need to override also hashCode() method.
For more on "overriding equals() and hashCode() methods" topic you can look at the this another question.

Categories