Im making query and trying to make an order by rating field. This is paginated list so im making partially queries.
But my query is not sorted.
private void callRealOnlineUsersList(int page, String neededGender) {
DatabaseReference mReference = realtimeReference.child("OnlineUsers/" + neededGender);
Query query = mReference.limitToLast(page * TOTAL_ITEMS_TO_LOAD);
query.orderByChild("rating");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final List<OnlineUser> userList = new ArrayList<>();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
OnlineUser user = postSnapshot.getValue(OnlineUser.class);
userList.add(new OnlineUser(user.getUid(), user.getName(), user.getImage(), user.getGender(), user.getCountry(), user.getRating()));
}
EventBus.getDefault().post(new ListEvent(userList));
}
Logs are
D/testList: rating 2
D/testList: rating 1
D/testList: rating 25
D/testList: rating 3
D/testList: rating 1
D/testList: rating 4
D/testList: rating 1
D/testList: rating 10
D/testList: rating 2
D/testList: rating 1
D/testList: rating 25
Datasnapshot
So, you've combined 3 questions into 1:
Why isn't my code sorting at all?
Why can't I sort in descending order?
Given a query that works, how should I do pagination with Firebase RTDB on Android?
Good news, the later 2 of these have good existing answers on StackOverflow!
First, the way you're using Query here:
Query query = mReference.limitToLast(page * TOTAL_ITEMS_TO_LOAD);
query.orderByChild("rating");
isn't correct (and this is why you aren't getting sorted results at all).
Instead try this:
Query query = mReference
.limitToLast(page * TOTAL_ITEMS_TO_LOAD)
.orderByChild("rating");
If you just call orderByChild, it returns a query that does that, but in your code you are ignoring the result.
To help make it clearer what is happening, you could also do this and it would work (but your current code doesn't save the result of the orderByChild call:
Query query = mReference.limitToLast(page * TOTAL_ITEMS_TO_LOAD);
query = query.orderByChild("rating");
Second, you can't sort by descending order in Firebase RTDB, so you should look at that question for possible solutions.
Finally, your current code will always be returning larger and larger numbers of results as page increases -- you don't always get just TOTAL_ITEMS_TO_LOAD results. A more complete SO question about pagination is here.
Related
Im trying to make pagination with Realtime db. I am making query first time with limit 15 and second time based on last item uid i am making second query where suppose to startAt() with that uid. After first query I am receiving as it suppose to 15 items. Next time it suppose to send me back 9 items. But it sends me zero
The code of response
DatabaseReference mReference = realtimeReference.child("OnlineUsers/" + neededGender);
Query query;
if (uid.equals("")){
query = mReference.orderByChild("rating").limitToLast(TOTAL_ITEMS_TO_LOAD);
}else {
//Pagination query with last uid to start
query = mReference.orderByChild("rating").startAt(uid).limitToLast(TOTAL_ITEMS_TO_LOAD);
}
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final List<OnlineUser> userList = new ArrayList<>();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
OnlineUser user = postSnapshot.getValue(OnlineUser.class);
userList.add(new OnlineUser(user.getUid(), user.getName(), user.getImage(), user.getGender(), user.getCountry(), user.getRating()));
}
EventBus.getDefault().post(new ListEvent(userList));
}
Database structure
When paginating, you always need to pass the value of the property that you order on. In addition, if there are multiple nodes with the same value for that property, you should pass the key of of the first item to return.
So in your case that'd be:
mReference.orderByChild("rating").startAt(rating, key).limitToLast(TOTAL_ITEMS_TO_LOAD);
This assumes that you have the rating of the node in rating and its key in key.
My recyclerview gets data from json api and populate list of articles. I have now implemented a commenting system with the help of firebase realtime database. I want to show number of comments below each article image in recyclerview. I tried several methods to implement that but all of them are not very effective.
At first I implemented database query based on article unique id for each view but since recyclerview has over 100 articles so it was making over 100 instance calls to database and was causing huge bandwidth problem.
Then I made one query to get all comments count from database and saved them locally in SQLite database and inside recyclerview i query SQLite databse to get comments counts but inserting 100 rows with article id and comments count in SQLite is slow.
What do you guys recommend best method for such task where I will spend least amount of bandwidth and get comment counts also?
My db structure is like this.
get comments method
public void getComments() {
keyrf = FirebaseDatabase.getInstance().getReference().child("Keys");
keyrf.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
HashMap map = new HashMap();
for( DataSnapshot child : dataSnapshot.getChildren() ) {
String childKey = child.getKey();
String c = child.child("c").getValue().toString();
map.put(childKey, c);
addComments(MainActivity.this, childKey, c);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
insert comments method
public static void addComments(Context context, String childKey, String c) {
try {
SQLiteDatabase myDB = context.openOrCreateDatabase("MyDb", Context.MODE_PRIVATE, null);
myDB.execSQL("CREATE TABLE IF NOT EXISTS comments (articleId INTEGER not null unique, comment INTEGER not null)");
String sql = "REPLACE INTO comments (articleId, comment) VALUES (?, ?)";
SQLiteStatement statement = myDB.compileStatement(sql);
statement.bindString(1, childKey);
statement.bindString(2, c);
statement.execute();
} catch (Exception e) {
}
}
What do you guys recommend best method for such task where I will spend least amount of bandwidth and get comment counts also?
The workaround here is to keep a count property somewhere in the database and update that whenever you add/delete child nodes.
So you can consider using a new section that might look similar to this:
Fireabase-root
|
--- numberOfComments
|
--- commentId: 12
|
--- commentId: 10
|
--- commentId: 20
So everytime you add or delete a post, increase / decrease that count by one. And because the number of comments might be updated in an multi user environment, I recommend you to use FirebaseTransactions as explained my answer from this post.
This question already has answers here:
Query based on multiple where clauses in Firebase
(8 answers)
Closed 6 years ago.
This is My Firebase database structure
i want to get data like
category = "cat" and level = "1"
Here is my code
Query query = firebaseReference.orderByChild("category").equalTo("cat1").limitToFirst(20);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UTILS.Log.e(TAG, "List Of User Count " + dataSnapshot.getChildrenCount());
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
//FCreateUSer modelRegister = dataSnapshot1.getValue(FCreateUSer.class);
UTILS.Log.e(TAG, "User Email :" + dataSnapshot1.child("question").getValue());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
it's wrok but how to use two condition in firbase i try two orderByChild but it's give error java.lang.IllegalArgumentException: You can't combine multiple orderBy calls!
Take a look at this video (it's in JavaScript, but it exactly solves your problem): https://youtu.be/sKFLI5FOOHs?t=612
Basically, you need to structure your data for your queries. In OP example Question will have field like category_level: "cat_1". Then you can perform query equalTo("cat_1"). You can leave out both category and level fields if you need it for other queries (but you will need to handle duplication in that case).
If you know that your number of items is small, simplest solution is to just pull all category=cat and filter level=1 items.
/users:
- user1
- name: user1name
- /contacts
- user2
- user3
.....
- user2
- name: user2name
....
This is my current firebase database. When user1 is logged in I check which contacts he has and get their UIDs. Now that I have their UIDs I'm trying to read the names of his contacts. How can I get the names for each of his contacts? Do I have to read the entire /users directory or can I use a query that retrieves only the data that I want. (In this case: the name of user2 and user3)
You'd do a nested read. So retrieve /users/$uid/contacts, then loop over the keys in there and for each key load /users/$key.
Supposing that you already have the user selected, you can go about it in two ways:
1) Use .child(String user); in your database reference.
2) You may have declared a URL for your database. Add + "/" + String user; to your URL.
You can further use these ways to parse through contacts of that user.
Finally, to retrieve contacts, you have to loop through.
Here is an example code:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot msgSnapshot : snapshot.getChildren()) {
mDataset.add(msgSnapshot.getKey());
mAdapter.notifyDataSetChanged();
}
}
This snippet fetches data and adds it to your adapter (which can be used to populate list view, grid view etc)
I have recently started switching my app from Parse to Firebase. Everything is going great so far, but I have not been able to find a Firebase equivalent method to Parse's whereContainedIn(String key, Collection values).
This was a very useful method that allowed me to pass in an array of ids and it would return all of the rows that matched that id. So far with Firebase, I have it returning all all of the rows in the database then looping through them to see if that row's id is contained in my array of ids. The other way is to query for each id individually, which doesn't work well with Firebase's asynchronous behavior. This is what I am doing now for the first approach:
List<String> userIds = new ArrayList<>();
userIds.add("2");
userIds.add("32");
userIds.add("12545");
userIds.add("187");
DatabaseReference firebaseRef = FirebaseDatabase.getInstance().getReference();
Query queryRef = firebaseRef.child("videos");
queryRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Video> usersVideos = new ArrayList<Video>();
for (DataSnapshot videoSnapshot : dataSnapshot.getChildren()) {
Video video = videoSnapshot.getValue(Video.class);
if (userIds.contains(video.userId)) {
usersVideos.add(video);
}
}
// usersVideos is now populated with the videos for the users
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "CANCELLED");
}
});
Neither of these methods seem reasonable since my table will contain hundreds of thousands of records.
Any help would be greatly appreciated!
Make another reference that can be looked up by userId and have it return all videoIds that this user has. From there you can query the videos. That means each time you save you will need to save in two spots. Sort of a pain, but it is what Firebase recommends.
So your reference will look more like:
videos -
| - <videoId> -
| - <video stuffs>
| - userId
videosByUser -
| - <userId> -
| 0 - <videoIdA>
| 1 - <videoIdB>
| - <userId2> -
| 0 - <videoIdA>
can't you just perhaps do a for loop over your ids and inside the for loop write a valueventlistener that will be called for each iterated item`?