Home > front end >  How to retireve a document index ranked by field value?
How to retireve a document index ranked by field value?

Time:05-31

In my Android quiz application, I have implemented a Leaderboard with Firebase Firestore and Pagination v3 Android Jetpack Library, each page contains 2 elements to reduce reading costs. I query the documents by Descending value of a field called "score". It works for people who are at the top of the leaderboards. But when users are for example 1000th I can't display it without query 1000 documents and so it will cost 1000 reads. So there is a way to get the user rank without querying all the documents?

EDIT:

@HiltViewModel
class LeaderboardScreenViewModel @Inject constructor(

) : ViewModel() {

    private val db = Firebase.firestore
    
    val loadingState = MutableStateFlow(LoadingState.IDLE)

    val flow = Pager(PagingConfig(pageSize = 2)) {
        FirestorePagingSource(
            queryBestScores = db.collection("users_public_details").orderBy("score", Query.Direction.DESCENDING)
                .limit(6L)
        )
    }.flow
        .cachedIn(viewModelScope)

}


class FirestorePagingSource(
    private val queryBestScores: Query
) : PagingSource<QuerySnapshot, DocumentSnapshot>() {

    override fun getRefreshKey(state: PagingState<QuerySnapshot, DocumentSnapshot>): QuerySnapshot? =
        null

    override suspend fun load(params: LoadParams<QuerySnapshot>): LoadResult<QuerySnapshot, DocumentSnapshot> {
        return try {
            val currentPage = params.key ?: queryBestScores.get().await()
            val lastVisibleScore = currentPage.documents[currentPage.size() - 1]
            val nextPage = queryBestScores.startAfter(lastVisibleScore).get().await()
            LoadResult.Page(
                data = currentPage.documents,
                prevKey = null,
                nextKey = nextPage
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }


}

CodePudding user response:

Is there a way to filter documents by a field value and get the 1000th of them without querying the previous 999 documents?

That's actually not possible. When it comes to Firestore, you can only request pages of data of a particular size using the Query#limit(long limit) method. That being said, you can start from the beginning of a query, and get the following pages of the same size, one by one. This operation should continue with other similar operations until you reach the page that contains the 1000th document.

There is no way you can jump directly to that document. You always have to read all the prior pages. So you have to start from the first page, then go forward through the pages using the query cursors, by specifying which document was the last one in the previous query.

Besides that, since the collections in Firestore don't maintain a document count, you won't be able to know how many pages of data there are ahead of time unless you create and maintain your own count of documents.

If you want to implement a modern way of handling pagination, you should consider implementing that so-called "infinite scroll". Facebook does that, and Instagram does that. There are many examples out there.

I have also written an article called:

Where I have explained how you can count the documents in a Firestore collection.

  • Related