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?
Pagination is the process of dividing data into discrete pages. In Firestore, it is achieved by ordering a collection by a field, limiting it to a consistent page size, then offsetting the query.
We can filter data in one of three ways: by child key, by key, or by value. A query starts with one of these parameters, and then must be combined with one or more of the following parameters: startAt , endAt , limitToFirst , limitToLast , or equalTo .
The total data in each write operation should be less than 256 MB. Multi-path updates are subject to the same size limitation.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With