如何根据集合中的文档位置进行分页? (偏移分页)

Posted

技术标签:

【中文标题】如何根据集合中的文档位置进行分页? (偏移分页)【英文标题】:How to do pagination based on the document position within a collection? (offset pagination) 【发布时间】:2021-03-26 16:13:42 【问题描述】:

我正在尝试进行分页,用户可以在 UI 中看到每个按钮的页码。我在这个项目中使用 Firestore 和 Buefy。

我的问题是 Firestore 针对这种情况返回了错误的查询。有时(取决于用户单击的页面)它可以工作,但有时不能(它返回与前一页按钮相同的数据)。 这真的很混乱,我不明白发生了什么。我给你看代码:

Vue组件:(注意onPageChange方法)

<template>
    <div>
        <b-table
            :data="displayData"
            :columns="table.columns"
            hoverable
            scrollable
            :loading="isLoading"
            paginated
            backend-pagination
            :total="table.total"
            :per-page="table.perPage"
            @page-change="onPageChange">

        </b-table>
    </div>
</template>

<script>
import  fetchBarriosWithLimit, getTotalDocumentBarrios, nextBarrios  from '../../../../firebase/firestore/Barrios/index.js'
import moment from 'moment'
const BARRIOS_PER_PAGE = 5
    export default 
        data() 
            return 
                table: 
                    data: [],
                    columns: [
                        
                            field: 'name',
                            label: 'Nombre'
                        ,
                        
                            field: 'dateAddedFormatted',
                            label: 'Fecha añadido'
                        ,
                        
                            field: 'totalStreets',
                            label: 'Total de calles'
                        
                    ],
                    perPage: BARRIOS_PER_PAGE,
                    total: 0
                ,
                isLoading: false,
                lastPageChange: 1
            
        ,
        methods: 
            onPageChange(pageNumber) 
                // This is important. this method gets fired each time a user clicks a new page. I page number that the user clicks.
                this.isLoading = true
                if(pageNumber === 1) 
                    console.log('show first 5...')
                    return;
                
                const totalPages = Math.ceil(this.table.total / this.table.perPage)
                if(pageNumber === totalPages) 
                    console.log('show last 5...')
                    return;
                

                /* Here a calculate the next starting point */
                const startAfter = (pageNumber - 1) * this.table.perPage
                nextBarrios(this.table.perPage, startAfter)
                    .then((querySnap) => 
                        this.table.data = []
                        this.buildBarrios(querySnap)
                        console.log('Start after: ', startAfter)
                    )
                    .catch((err) => 
                        console.err(err)
                    )
                    .finally(() => 
                        this.isLoading = false
                    )
                
            ,
            buildBarrios(querySnap) 
                querySnap.docs.forEach((docSnap) => 
                        this.table.data.push(
                        id: docSnap.id,
                        ...docSnap.data(),
                        docSnapshot: docSnap
                    )
                );
            
        ,
        computed: 
            displayData() 
                let data = []
                this.table.data.map((barrioBuieldedObj) => 
                    barrioBuieldedObj.dateAddedFormatted = moment(Number(barrioBuieldedObj.dateAdded)).format("DD/MM/YYYY")
                    barrioBuieldedObj.totalStreets ? true : barrioBuieldedObj.totalStreets = 0;
                    data.push(barrioBuieldedObj)
                );
                return data;
            
        ,
        mounted() 
            // obtener primer paginacion y total de documentos.
            this.isLoading = true
            getTotalDocumentBarrios()
                .then((docSnap) => 
                    if(!docSnap.exists || !docSnap.data().totalBarrios) 
                        // mostrar mensaje que no hay barrios...
                        console.log('No hay barrios agregados...')
                        this.table.total = 0
                        return;
                    
                    const totalBarrios = docSnap.data().totalBarrios
                    this.table.total = totalBarrios
                    if(totalBarrios <= BARRIOS_PER_PAGE) 
                        return fetchBarriosWithLimit(totalBarrios)
                     else 
                        return fetchBarriosWithLimit(BARRIOS_PER_PAGE)
                    
                    
                )
                .then((querySnap) => 
                    if(querySnap.empty) 
                        // ningun doc. mostrar mensaje q no hay barrios agregados...
                        return;
                    
                    this.buildBarrios(querySnap)
                )
                .catch((err) => 
                    console.error(err)
                )
                .finally(() => 
                    this.isLoading = false
                )
        
    
</script>

<style lang="scss" scoped>

</style>

nextBarrios 函数:

function nextBarrios(limitNum, startAtNum) 

    const query = db.collection('Barrios')
                    .orderBy('dateAdded')
                    .startAfter(startAtNum)
                    .limit(limitNum)
    return query.get()

db 是调用 firebase.firestore() 的结果对象。我可以告诉查询从某个数字开始,其中数字是集合中文档的索引位置吗?如果没有,我该如何解决这个问题?

谢谢!

【问题讨论】:

【参考方案1】:

Firestore 不支持基于偏移或索引的分页。如果不实际阅读所有文档,也无法判断整个查询将返回多少文档。因此,不幸的是,Firestore 无法实现您想要做的事情。

您似乎也误解了分页 API 的实际工作方式。 startAfter 不采用索引 - 它采用前一页中最后一个文档的 DocumentSnapshot,或者您用于对查询进行排序的有序字段的值,同样,您在前一页中看到的最后一个值.您基本上将使用 API 来告诉它根据您在最后一页中找到的结果在下一页结果中从哪里开始。这就是文档说您正在使用“查询光标”时的意思。

【讨论】:

我明白了。所以我们只能向前和向后分页。我忘了提到我在一个单独的文档中保留了所有 Barrios 的聚合。我可以通过使用 Firebase 函数来处理上述分页吗?或者这是我们可以使用 Firestore 进行分页的唯一方法? 正如我所说,Firestore 不支持基于偏移的分页。在哪里运行查询并不重要。除非您将所有文档都保存在内存中,否则您将不知道有多少页,或者页面从哪里开始或结束。

以上是关于如何根据集合中的文档位置进行分页? (偏移分页)的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis分页对比MybatisPlus分页

MySQL大数据量分页查询方法及其优化

SQL---分页查询

如何在 Laravel 5 中对合并的集合进行分页?

Sql Server 按偏移量分页行 - 没有'ORDER BY'

ThinkPHP5如何对数组进行分页