Prodigy
Prodigy

Reputation: 2124

Firebase database pagination in ascending order

I'm having a good problem trying to paginate datas from firebase database for weeks now all to no avail. I tried all the example from android documentation to SO not still working. Below is a screenshot of my data node

enter image description here

Below is my code, I'm using recycleview for continous scrolling.

commentDatabaseReference = FirebaseDatabase.getInstance().getReference().child(CHANNEL_COMMENT).child(postId);
commentDatabaseReference.orderByKey().limitToLast(TOTAL_ITEM_EACH_LOAD).addListenerForSingleValueEvent(commentValueEventListener);

ValueEventListener commentValueEventListener = new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if(!dataSnapshot.hasChildren()) {
                currentPage--;
            }

            int counter = 0;
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                commentId = snapshot.getKey();
                Comment comment = snapshot.getValue(Comment.class);
                commentList.add(comment);

                adapter.notifyDataSetChanged();
            }
            //adapter.notifyDataSetChanged();
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    };

Recycleview continous scrolling listener

recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(linearLayoutManager) {

            @Override
            public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
                Log.e("more", "onload");
                loadMoreData();
            }
        }); 

private void loadData() {
        Log.e("loading", ""+ commentId);
        commentDatabaseReference.orderByChild("timestamp").startAt(commentId).limitToLast(TOTAL_ITEM_EACH_LOAD)
                .addListenerForSingleValueEvent(commentValueEventListener);
    }

    private void loadMoreData(){
        //if ((currentPage * TOTAL_ITEM_EACH_LOAD) <= commentCount) {
            currentPage++;
            loadData();
        //}
    }

Conclusion: For instance if I have 20 data with offset of 10 and I use orderby with limitToLast I get the last 10 data starting at the top i.e I get data from 11 - 20 instead of 20 - 11. Also noticed sometimes I get unending data when I combine with startAt

Upvotes: 2

Views: 3344

Answers (2)

Prodigy
Prodigy

Reputation: 2124

Finally solved this thanks to faruk hints:

The code below methods load data from firebase to the recycleview

private void loadData() {
        Query query;
        if (commentId == null) {
            query = commentDatabaseReference.orderByChild("timestamp").limitToLast(TOTAL_ITEM_EACH_LOAD);
        } else {
            query = commentDatabaseReference.orderByChild("timestamp").startAt(inverseTimestamp).limitToFirst(TOTAL_ITEM_EACH_LOAD);
        }
        query.addListenerForSingleValueEvent(commentValueEventListener);
    }

    private void loadMoreData(){
        //commentCount: is the total comments stored in firebase database
        if ((currentPage * TOTAL_ITEM_EACH_LOAD) <= commentCount) {
            currentPage++;
            loadData();
        }
    }

On the recycleview scroll listener i called loadMoreData method to fetch more data from firebase

recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(linearLayoutManager) {

            @Override
            public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
                //Load more data
                loadMoreData();
            }
        });

Firebase database listener to listen to data fetched

ValueEventListener commentValueEventListener = new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if(!dataSnapshot.hasChildren()) {
                currentPage--;
            }

            int counter = 0;
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                commentId = snapshot.getKey();//Index key for the node
                Comment comment = snapshot.getValue(Comment.class);

                ++counter;

                //Inverse timestamp
                inverseTimestamp = comment.getTimestamp();

                //store nth - 1 data into the arraylist to avoid duplicates
                if (counter < TOTAL_ITEM_EACH_LOAD) {
                    commentList.add(comment);

                    adapter.notifyDataSetChanged();
                }
            }
            //adapter.notifyDataSetChanged();
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    };

Upvotes: 3

Faruk
Faruk

Reputation: 5841

Use limitToFirst and add a field (timestamp inverse) that contains timestamp * -1 for example call it timestampInv

And then you can orderByChild("timestampInv") and startAt :

commentDatabaseReference
  .orderByChild("timestampInv")
  .startAt(lastTimeInv)
  .limitToFirst(TOTAL_ITEM_EACH_LOAD)
  .addListenerForSingleValueEvent(commentValueEventListener);

If you want to create the timestamp inverse automatically, I suggest using cloud function with onCreate trigger :

exports.addCommentTimestampInv = functions.database.ref('/comment/{postId}/{commentId}')
.onCreate(event => {
  // Grab the current value of what was written to the Realtime Database.
  const commentData = event.data.val();

  var timeInv = commentData.timestamp * -1;

  return event.data.ref.child('timestampInv').set(timeInv);

});

Hope it helps :)

Upvotes: 1

Related Questions