I am building recipes app. I have page in my app that retrieves all the recipes from the database on Firebase into ListView. Each Recipe has these variables:
public String key;
public String uid;
public String title;
public String type;
public String ingredients;
public String instructions;
public int likes;
Here's the function that retrieves all the data :
//Retrieve Data from database
public void retriveData() {
database.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
recipes = new ArrayList<Recipe>();
for(DataSnapshot data : dataSnapshot.getChildren()) {
Recipe r = data.getValue(Recipe.class);
recipes.add(r);
}
allRecipeAdapter = new AllRecipeAdapter(AllRecipeActivity.this,0,0,recipes);
lv.setAdapter(allRecipeAdapter);
}
}
}
Now i want to create another screen that also has a ListView and i would like to sort the Recipes by nubmer of likes and then enter the top 10 recipes into the ListView.
I searched on Google i found the function OrderByValue but i can't figure out how to use it i realized how to works but i can't implement this to my project.
Gaƫtan Maisse and KLHauser gave you solutions that work in pure client-side code. Here's an alternative that uses Firebase Database queries, which means the ordering and filtering happen on the server:
// assuming that database refers to the list of all recipe
Query topRecipes = database.orderByChild("likes").limitToLast(10);
topRecipes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
recipes = new ArrayList<Recipe>();
for(DataSnapshot data : dataSnapshot.getChildren()) {
Recipe r = data.getValue(Recipe.class);
// add the recipe to the front of the list
recipes.add(0, r);
}
allRecipeAdapter = new AllRecipeAdapter(AllRecipeActivity.this,0,0,recipes);
lv.setAdapter(allRecipeAdapter);
}
}
Since Firebase queries always sort items in ascending order, the query takes the last 10 items - those are the ones with the highest like count. To reverse those items, the code inserts each item to the from of the list, instead of appending them to the end.
You can sort your list using a Comparator and then get a sublist of 10 first elements before displaying it in the ListView:
Comparator<Recipe> likesOrderComparator = new Comparator<Recipe>() {
public int compare(Recipe recipe1, Recipe recipe2) {
return recipe1.likes < recipe2.likes ? -1 : recipe1.likes == recipe2.likes ? 0 : 1;
}
};
Collections.sort(recipes, likesOrderComparator);
List<Recipe> topTenRecipes = recipes.subList(0, 9);
// Now display topTenRecipes in a ListView
Java7:
Collections.sort(list, new Comparator<Recipe>() {
#Override
public int compare(Recipe r1, Recipe r2) {
if (r1.getLikes() > r2.getLikes())
return 1;
if (r1.getLikes() < r2.getLikes()
return -1;
return 0;
}
});
Java8 (using lambda):
recipes.stream()
.sorted((r1, r2) -> Integer.compare(r1.getLikes(),
r2.getLikes()))
Related
I have a database something like this. How I want to compare the value for all users to get most value.
restaurant
-userUid
-stateUid
-restaurantUid
-price = 9
-restaurantUid2
-price = 10
-stateUid2
-restaurantUid3
-price = 2
As you can see the database there, stateUid price is 19 while stateUid2 price is only 2
So, stateUid has the most price. How to compare them and display the most one. Thank you
EDIT:
I have done something like this, and it's error at return. And the value is not working.
exports.calculateTotal = functions.database.ref('/restaurant/{userUid}/{stateUid}/{restaurantUid}')
.onWrite((change, context) => {
// Only edit data when it is first created.
if (change.before.exists()) {
return null;
}
// Exit when the data is deleted.
if (!change.after.exists()) {
return null;
}
//Get id
const restaurantUid = context.params.restaurantUid;
let totalValue = 0;
change.after.forEach(function (item) {
totalValue += item.child('total').val();
});
console.log(totalValue);
return functions.database.ref('/priceTotal/' + restaurantUid).child('total').set(totalValue);
});
Firebase queries work on a flat list of nodes. A query can contain only a single unknown key, the key of the direct child nodes under the location where you query. In your data structure there are multiple levels of unknown keys, which means that you can't query for the highest price across all of them.
What you can do in your current data structure is query across one state for the restaurant with the highest price. That'd look something like:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("restaurant");
DatabaseReference stateRef = ref.child("userUid").child("stateId");
stateRef.orderByChild("price").limitToLast(1).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot: dataSnapshot.getChildren()) {
Log.i(TAG, snapshot.getKey()+": "+snapshot.child("price").getValue(Long.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
But you can't search across all states for a user, or even all users. If you want to allow that, you'll have to store all prices in a flat list, like:
restaurant_prices: {
"restaurantid": {
price: 9,
state: "statid",
user: "userid"
}
}
Also see:
Firebase Query Double Nested
Firebase query if child of child contains a value
int totalPrice = 0;
int greaterPrice = 0;
int temp = 0;
DatabaseRefernce restRef = FirebaseDatabase.getInstance().getReference().child("restaurant").child(userUid);
restRef.addValueEventListener(new ValueEventListener(){
onDataChange(Datasnapshot snapshot) {
for(Datasnapshot snap : snapshot) {
String key = snap.getKey();
//This will return you the keys of stateUid
restRef.child(key).addValueEventListener(new ValueEventListener(){
onDataChanged(DatSnapshot datasnapshot) {
//this for loop will iterate through restaurants of that specific state
for(DataSnapshot snap2 : datasnapshot){
totalPrice += (int) snap2..child("price").getValue();
}
//When this loop ends you will get the total price of all restaurants from that state
}
});
//as u see above I mentioned greater price and temp variable
using simple logic of finding greatest number out of two number save the value of greatest integer to the variable every time you loop through state
}
}
}
);
Use nested for loops to iterate from database like above and calculate your prices
Else what you can do is when you are uploading the data of restos - while uploading prices just make an extra node for total price of city and add price of resto every time you upload new resto
Hi I am trying to populate recycler view by getting data from two different nodes in firebase. However I am getting two separate listings... one of each node. I want to combine them and show as a single item. My original question is posted here if more files are required to be seen : Populating Recycler View From Nested Queries in Firebase
My Fragment is as follows
// Firebase
fbDatabaseRootNode = FirebaseDatabase.getInstance().getReference();
fbDatabaseRefGroupList = fbDatabaseRootNode.child("groups_list").child(current_user_id);
fbDatabaseRefGroups = fbDatabaseRootNode.child("groups");
fbDatabaseRefGroupList.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// Array to Get Group List
lGroupsList = new ArrayList<>();
if (dataSnapshot.exists()) {
// Clear Array to Get Group List
lGroupsList.clear();
for (DataSnapshot glSnapshot : dataSnapshot.getChildren()) {
// Use The Model To Format Array List and Pass It Into It
GroupsListModel g = glSnapshot.getValue(GroupsListModel.class);
// Array to Get Group List
lGroupsList.add(g);
// Get The Group ID To Get Data From Other Nodes
String groupID = String.valueOf(glSnapshot.getKey());
fbDatabaseRefGroups.child(groupID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// Array to Get Group List
lGroupsList2 = new ArrayList<>();
if (dataSnapshot.exists()) {
// Clear Array to Get Group List
lGroupsList2.clear();
// String groupName = (String) dataSnapshot.child("group_name").getValue();
// String groupTagLine = (String) dataSnapshot.child("group_tagline").getValue();
// String groupMemberCount = (String) dataSnapshot.child("group_member_count").getValue();
// Use The Model To Format Array List and Pass It Into It
GroupsListModel g = dataSnapshot.getValue(GroupsListModel.class);
// Array to Get Group List
lGroupsList2.add(g);
// Joining The Arrays To Get One Array
lGroupsList.addAll(lGroupsList2);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
aGroupList = new GroupsListAdapter(getContext(), lGroupsList);
rvGroupList.setAdapter(aGroupList);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
However the problem seems to be that instead of joining the two nodes to show the item as one, I am getting two items in the list (first one containing data from lGroupsList and second one from lGroupsList2). They do not seem be joining.
// Joining The Arrays To Get One Array
lGroupsList.addAll(lGroupsList2);
I'm working with firebase, I can get data from firebase with this code
String value1 = "Jack"
DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("users");
Query personsQuery = data.orderByChild("desc").equalTo(value1);
one value works well, how can I get data with array value like this
String value1[] = {"Larry", "Moe", "Curly"};
Edit:
this code lists all names
mPeopleRVAdapter = new FirebaseRecyclerAdapter<Productget, NewsViewHolder>(personsOptions) {
#Override
protected void onBindViewHolder(Product.NewsViewHolder holder, final int position, final Productget model) {
holder.setTitle(model.getTitle());
}
#Override
public Product.NewsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.product_row, parent, false);
return new Product.NewsViewHolder(view);
}
};
mPeopleRV.setAdapter(mPeopleRVAdapter);
Can I filter this code for Larry, Moe and Curly?
You can try to get your data with a snapshot and store it into a map like this
Map<String, String> map = (Map<String, String>) dataSnapshot.getValue();
check this answer
Firebase Realtime Database won't be able to do that out of the box, you may need to structure your data differently in other to get desired results, see https://firebase.google.com/docs/database/android/structure-data#best_practices_for_data_structure
For instance you could do composite keys (if you are trying to filter on different childs/fields at once). Also you could try to loop though those different conditions ("Larry", "Moe", "Curly"), download the entire parent node and filter your self; depends on what you are trying to achieve.
If you are ok to Fetch all data (all users), then you could filter by name using a HashMap for example:
First Create and populate the HashMap:
HashMap<Stirng, User> hashmap = new HashMap<>();
ValueEventListener userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
hashmap.put(user.getName(), user);
}
};
mPostReference.addValueEventListener(userListener);
Then on your adapter you could filter on your data:
//String value1[] = {"Larry", "Moe", "Curly"};
ArrayList<User> fullNameList = new ArrayList<>(); //filtered users array list
for (int i = 0; i<value1.length; i++){
if(hashmap.containsKey(value1[i])){
fullNameList.add(hashmap.get(value1[i]));
}
}
Firestore on the other hand does support more complex queries:
https://firebase.google.com/docs/firestore/query-data/queries#compound_queries
I want to add values to array in Firestore document and get them back as list or array.
This is my code , and it just replace the array value at 0 index , and i made a list and added the value it return boolean .
FirebaseFirestore db = FirebaseFirestore.getInstance();
final DocumentReference doc = db.collection("RoomInformation").document(model.getRoomAdmin());
db.collection("RoomInformation").document(model.getRoomAdmin()).update(freshUser).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
doc.update("onlineUsers" , Arrays.asList("user" , facebookUserName)).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
new OurToast().myToast(view.getContext() , facebookUserName + " Join The Room");
}
});
}
}
As seen in the offical documentation regarding arrays:
Although Cloud Firestore can store arrays, it does not support querying array members or updating single array elements.
Taking the following code snippet as an example:
public class ArrayPost {
String title;
String[] categories;
public ArrayPost(String title, String[] categories) {
this.title = title;
this.categories = categories;
}
}
ArrayPost myArrayPost = new ArrayPost("My great post", new String[]{
"technology", "opinion", "cats"
});
We see that using the data structure above, there is no way to perform this query.
One more thing to note, although Cloud Firestore can store arrays, this is an anti-pattern. One of the many reasons Firebase recommends against using arrays is that it makes the security rules impossible to write. I recomand you consider an alternative data structure, where each category is the key in a map and all values are true. Please take a look at this example:
public class MapPost {
String title;
Map<String,Boolean> categories;
public MapPost(String title, Map<String,Boolean> categories) {
this.title = title;
this.categories = categories;
}
}
Map<String, Boolean> categories = new HashMap<>();
categories.put("technology", true);
categories.put("opinion", true);
categories.put("cats", true);
MapPost myMapPost = new MapPost("My great post", categories);
Edit 13 Aug 2018:
According to the updated documentation regarding array membership, now it is possible to filter data based on array values using whereArrayContains() method. A simple example would be:
CollectionReference citiesRef = db.collection("cities");
citiesRef.whereArrayContains("regions", "west_coast");
This query returns every city document where the regions field is an array that contains west_coast. If the array has multiple instances of the value you query on, the document is included in the results only once.
I have data structure as:
https://law-apps-44h221543638e.firebaseio.com/apps to promote/0/ >> name:"First App", package:"fra"
https://law-apps-44h221543638e.firebaseio.com/apps to promote/1/ >> name:"Second App", package:"sca"
https://law-apps-44h221543638e.firebaseio.com/apps to promote/2/ >> name:"Third App", package:"tha"
and I query it using
Firebase myFirebaseReference = new Firebase("https://law-apps-44h221543638e.firebaseio.com/apps to promote");
Query queryRef = myFirebaseReference.orderByChild("name");
queryRef.addListenerForSingleValueEvent(new ValueEventListener() { // override methods })
But it returns the data in the same order i.e sorted by the first child (1,2,3, etc.)
How should I query it so it sorts the data by the "name" tag of each child?
Ok so I found the answer to this question and I am writing it so others may benefit from it. I was using an older technique wherein I was finding from datasnapshot my apps using their parents' numbers and due to this, I was delibrately undoing the ordering.
Now I have used dataSnapshot.getChildren().iterator() and it is now working correctly. Here's the code:
Query queryRef = myFirebaseReference.orderByChild("name");
queryRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String name;
String my_package;
long lengthOfForLoop = dataSnapshot.getChildrenCount();
Iterator<DataSnapshot> child = dataSnapshot.getChildren().iterator();
for (int i = 0; i < lengthOfForLoop; i++) {
DataSnapshot next = child.next();
name = next.child("name").getValue(String.class);
my_package = next.child("package").getValue(String.class);
// do something with this data.
}
}
});
}
thanks, Usman!
I used the same code with the children iterable collection in a for loop.
This code was worked for me (in Kotlin):
accountsReference.child(accountId).child("actions").orderByChild("actionPosition").addListenerForSingleValueEvent( object : ValueEventListener {
override fun onDataChange(var1: DataSnapshot) {
if (var1.children != null) {
for (actionsEntries in var1.children) {
...
}
}