9

I have a query that looks like this:

Query first = ref.orderBy("name", Query.Direction.ASCENDING).limit(10);

This is how I display the data to my RecyclerView.

firestoreRecyclerOptions = new FirestoreRecyclerOptions.Builder<ModelClass>().setQuery(query, ModelClass.class).build();
myFirestoreRecyclerAdapter = new MyFirestoreRecyclerAdapter(firestoreRecyclerOptions);
recyclerView.setAdapter(myFirestoreRecyclerAdapter);

I'm trying to use pagination as specified in here and I cannot solve it.

first.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
    @Override
    public void onSuccess(QuerySnapshot documentSnapshots) {
        DocumentSnapshot lastVisible = documentSnapshots.getDocuments().get(documentSnapshots.size() - 1);
        Query second = ref.orderBy("name", Query.Direction.ASCENDING).startAfter(lastVisible).limit(10);
        //Create a firestoreRecyclerOptions and setting the adapter
    }
});

Is there a way to paginate queries by combining query cursors using FirestoreRecyclerAdapter? Is this even possible?

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Joan P.
  • 2,368
  • 6
  • 30
  • 63
  • https://stackoverflow.com/questions/43289731/pagination-in-endless-recycler-view-with-firebase –  May 29 '18 at 20:31
  • @Eminem Thanks for the comment. Is this also working for Cloud Firestore? In that example, I cannot see the use of `FirebaseRecyclerAdapter`, which is mostly the same. – Joan P. May 29 '18 at 20:32
  • https://stackoverflow.com/questions/46795445/how-to-load-batches-of-data-in-a-recycler-view-using-firestore –  May 29 '18 at 20:36
  • @Eminem I studied the last post and isn't helping me. Do you have any other idea? Thanks! – Joan P. May 30 '18 at 07:15
  • 2
    @Eminem Can you please take a closer look to my question? Thanks – Joan P. Jun 01 '18 at 09:18
  • `FirestoreRecyclerAdapter` is quite limited. Your own customized recyclerview adapter is always better when use `Firebase Realtime Database` or `Firestore`. If you only need some kind of "Chatting" app without pagination of old chat history, in that case `FirestoreRecyclerAdapter` is applicable. – wonsuc Jun 02 '18 at 20:41
  • @wonsuc No, I want to create pagination because my query returns to many result that cannot be displayed at once. So you say that there is no way I can achieve this? – Joan P. Jun 04 '18 at 10:53

2 Answers2

7

As @FrankvanPuffelen already answered in an earlier question of yours, you cannot achieve that because in your case, you should pass 2 different queries (first and second) to a single adapter, which is not possible with FirestoreRecyclerAdapter. You can either use the first query or the second with a single instance of your adapter.

A solution would be to create two different lists, containing the results from each query and combine them. Then you can pass the resulting list to another adapter, let's say an ArrayAdapter and then display the results into a ListView or even better in a RecyclerView. The problem in this case is that you will not be able to use the real-time features that the FirestoreRecyclerAdapter class provides but this approach will solve your problem.

Edit:

According to your request from the comment section, I'll give you an example on how to paginate a query on button click in the easiest way, using a ListView and an ArrayAdapter. You can achieve the same thing also using a RecyclerView when scrolling down. But to keep things simple, let's assume we have a ListView and a Button and we want to load more items to the list on every button click. For that, let's define first the views :

ListView listView = findViewById(R.id.list_view);
Button button = findViewById(R.id.button);

Let's assume we have a database structure that looks like this:

Firestore-root
   |
   --- products (collection)
         |
         --- productId (document)
                |
                --- productName: "Product Name"

And a model class that looks like this:

public class ProductModel {
    private String productName;

    public ProductModel() {}

    public ProductModel(String productName) {this.productName = productName;}

    public String getProductName() {return productName;}

    @Override
    public String toString() { return productName; }
}

Now, let's define a query with the limit set to 3.

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference productsRef = rootRef.collection("products");
Query firstQuery = productsRef.orderBy("productName", Query.Direction.ASCENDING).limit(3);

This means that on every button click, we'll load 3 more items. And now, here is the code that does the magic:

firstQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            List<ProductModel> list = new ArrayList<>();
            for (DocumentSnapshot document : task.getResult()) {
                ProductModel productModel = document.toObject(ProductModel.class);
                list.add(productModel);
            }
            ArrayAdapter<ProductModel> arrayAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, list);
            listView.setAdapter(arrayAdapter);
            lastVisible = task.getResult().getDocuments().get(task.getResult().size() - 1);

            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Query nextQuery = productsRef.orderBy("productName", Query.Direction.ASCENDING).startAfter(lastVisible).limit(3);
                    nextQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                        @Override
                        public void onComplete(@NonNull Task<QuerySnapshot> t) {
                            if (t.isSuccessful()) {
                                for (DocumentSnapshot d : t.getResult()) {
                                    ProductModel productModel = d.toObject(ProductModel.class);
                                    list.add(productModel);
                                }
                                arrayAdapter.notifyDataSetChanged();
                                lastVisible = t.getResult().getDocuments().get(t.getResult().size() - 1);
                            }
                        }
                    });
                }
            });
        }
    }
});

In which lastVisible is a DocumentSnapshot object that represents the last visibile item from the query. In this case, every third one and it is declared as gloabl variable:

private DocumentSnapshot lastVisible;

Edit2: Here you have also the solution on how you can get the data from your Firestore database and display it in smaller chunks in a RecyclerView when user scrolls.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • You are saying that I can't made it any way using `FirestoreRecyclerAdapter`? – Joan P. May 30 '18 at 07:16
  • No, in the way you are trying to. You should consider using the other approach. – Alex Mamo May 30 '18 at 07:27
  • 2
    This is basically another question but ok, for a better understandin I will write you a few lines of code. – Alex Mamo Jun 06 '18 at 07:32
  • Please see my updated answer and tell me if it works. – Alex Mamo Jun 06 '18 at 08:51
  • Sorry, I forgot to mention it. Yes it is declared as a global variable. Please see again my updated answer. – Alex Mamo Jun 06 '18 at 09:17
  • Thank you for your time and effort Alex. According also to wonsuc's comment I understand that I cannot achieve this usig `FirestoreRecyclerAdapter`. I wish I could find a way. Your code works perfectly. Thanks again! – Joan P. Jun 06 '18 at 10:49
  • Unfortunately Firebase-UI's adapters can only use a single query. Happy to hear that it worked. Cheers! – Alex Mamo Jun 06 '18 at 10:52
  • Its not good. Sometimes its showing duplicate item or sometimes loading repeated set of items. Not recommended. – Pooja Oct 23 '19 at 20:39
  • @Pooja "Sometimes it's showing duplicate item" it is very vague and doesn't help anyone. Without seeing your code it's hard to say what it is not working the way you expect, so please post another fresh question using its own [MCVE](https://stackoverflow.com/help/mcve), so me and other Firebase developers can help you. – Alex Mamo Oct 23 '19 at 20:53
  • @AlexMamo my code is same as your posted answer but its not scrolling smoothly. Moreover its showing duplicate items and sometimes showing whole same list (i.e. number of items). I tried a lot with actual data. Although I am using this code for app but its showing duplicate. – Pooja Oct 24 '19 at 05:41
  • @Pooja Since I cannot see your code, I can't believe you when you say that it's the same. There is no way in which you can use this **exact** code and produce duplicate data so it's very obvious that you are doing something wrong. Beside that, it's it's not correct to say that a code is not working without proving it. I only can say that I tested the code when I posted the answer and I tested now and it works 100% without any duplicates or errors. – Alex Mamo Oct 24 '19 at 08:48
  • @AlexMamo no. I didn't. I checked that its working perfectly for small sized data. But its repeating when having more than 4k documents. I don't know why but its repeating sometime also there's some performance issue. I am not professional developer, but I faced this situation. I appreciate your work. Thanks. – Pooja Oct 29 '19 at 14:13
  • @Pooja You're very welcome. So if you say "is repeating sometime", it should be a pattern. Add a new qustion so we can take a look. – Alex Mamo Oct 29 '19 at 14:20
  • Still don't work with FirebaseRecycler (Firestore-UI)? – luke cross May 18 '20 at 13:25
  • @lukecross Post a new question, so we can take a look at it. – Alex Mamo May 18 '20 at 13:32
  • It's the same issue from this comments, i need know only if FirebaseRecyclerAdapter not is cool to use for Paginating, the answer down looks fine too and look that works FirebaseRecycler Package. – luke cross May 18 '20 at 13:56
  • @RavishRajput "Doesn't work", doesn't help anyone in any way. Without seeing your code it's hard to say what it is not working the way you expect, so please post a new question using its own [MCVE](https://stackoverflow.com/help/minimal-reproducible-example), so I and other Firebase developers can help you. – Alex Mamo Sep 28 '20 at 20:28
1

A possible approach is save the push keys of each item in a separate node.

Then fetch all keys from that node to an array list or something ..

Then based on your pagination number fetch the keys from this array List and use orderByKey Query and startAt and LimitToFirst queries combined to make the pagination algorithm

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jinson Paul
  • 481
  • 6
  • 17
  • Hi Paul and thanks for your answer. I'm not using push keys, I'm using the new Cloud Firestore database. There are no push keys in here. Your idea should work when using Firebase Realtime database but is there any way in which I can implement this using `FirestoreRecyclerAdapter`? – Joan P. Jun 05 '18 at 08:47
  • I don't know if this is correct change the variable second to simply first then on any button press or something simply execute the first.get().addOnSuccessListener line,, may this way it might get work – Jinson Paul Jun 05 '18 at 09:00
  • Please provide me a concrete example. I didn't understood you. – Joan P. Jun 05 '18 at 09:15
  • Please add the source code which your are currently implemented,, the question seems incomplete – Jinson Paul Jun 05 '18 at 09:17
  • I have added the code from the beginning. It's a straightforward example. – Joan P. Jun 05 '18 at 09:47