Firestore中的分页:在不知道该页面中第一个文档的情况下跳转到特定页面。

huangapple go评论62阅读模式
英文:

Paginaion in firestore: Jump to a specific page without knowing the first document in that page

问题

我有一个包含成千上万个文档的Firestore 集合,我需要实现一个分页系统,具体要求如下:

  1. 一次只渲染20个文档。
  2. 文档应该按照一个名为 created_at 的时间戳字段进行排序。
  3. 用户应该能够直接跳转到指定页,而不需要逐页翻过去。例如,可以直接打开第9页。

以下是你的代码:

import { getDocs, query, collection, orderBy, getFirestore, startAt, limit } from 'firebase/firestore';

const pageSize = 20;

async function fetchPage(page: number){
    const db = getFirestore();
    const offset = page * pageSize;
    const q = query(
        collection(db, 'order'),
        orderBy('created_at'),
        limit(pageSize),
        startAt(offset));
    return await getDocs(q);
}

fetchPage(9).then(snapshot => {
    console.log('加载的文档: ', snapshot.docs);
});

上面的代码总是加载集合中的前20个文档,不管提供的页码是多少,而且无论你传递给 startAt 方法的整数值是什么,都不会影响结果。

英文:

I have a Firestore collection with thousands of documents and I need to implement a pagination system where:

  1. Only 20 documents will be rendered at a time
  2. Documents will be sorted by let's say, a created_at timestamp field.
  3. One more very important requirement is that the user should be able to jump to a specific page directly without having to go through all the previous pages first. For example, open the 9th page directly.

Here's my attempt:

import { getDocs, query, collection, orderBy, getFirestore, startAt, limit } from 'firebase/firestore';

const pageSize = 20;

async function fetchPage(page: number){
    const db = getFirestore();
    const offset = page * pageSize;
    const q = query(
        collection(db, 'order'),
        orderBy('created_at'),
        limit(pageSize),
        startAt(offset));
    return await getDocs(q);
}

fetchPage(9).then(snapshot => {
    console.log('Documents loaded: ', snapshot.docs);
});

The above code always loads the first 20 documents in the collection regardless of the page number and regardless of whatever integer value I provide to the startAt method, it just has no impact on the results.

答案1

得分: 1

你编写的代码不会达到你的意图。相反,它将返回第一个 pageSize 文档,这些文档满足 created_at > offset 的条件。Firestore 客户端中没有偏移量。

但是,根据你的需求,你可以采取以下方式进行一些修改:

  1. 在每个文档的一个字段中添加文档编号模除 20 的结果,比如 page
  2. 在一个单独的中央文档中保留文档的总数。你需要在每次创建 1 中的文档时递增这个值。

然后,取决于你对 "第 n 页" 的理解:

  • 第 n 页是从一开始创建的 20 个文档集合 => 那么很容易,只需在查询中添加 where('page','==',n)
  • 第 n 页是最新的 20 个文档集合(例如,第 1 页是最后的 20 个文档),那么你需要进行一些数学计算,但通过检索至多 2 个连续的 page(因此 40 个文档),你始终可以获取到你想要的 20 个文档。

注意:

  1. 这个解决方案要求你首先读取当前的总文档数,然后创建新文档并递增总数 - 这一切都在一个事务中以避免并发问题。这将限制你可以创建文档的速度
  2. 因此,每次操作都会多消耗 1 次读取和 2 次写入。
  3. 如果可以删除文档,这个解决方案将无法正常工作。
英文:

The code you write will not do what you intend. Instead it will return the first pageSize documents with created_at > offset. There is no offset in Firestore client.

But, depending on your needs, you could for instance hack it this way:

  1. Add the document number modulo 20 in a field of each document, say page
  2. Keep the total number of documents in a separate central document. You will need to increment it every time a document in 1 is created.

Then it depends what you mean by page nth:

  • the nth set of 20 docs created from the start => then easy, just add where('page','==',n) to your query.
  • the nth set of 20 latest documents (eg page 1 = 20 last documents) then you will have to to some math but by retrieveing documents for at most 2 consecutive page (so 40 docs) you are always sure to get the 20 you want.

Note that

  1. this solution requires that you first read the current total document number and then create the new document and increment the total number - all this in a transaction to avoid concurrent issues. This will limit the rate at which you can create documents.
  2. Consequently it will cost you 1 more read and 2 more write each time
  3. This solutions does not work if you can delete documents

huangapple
  • 本文由 发表于 2023年2月27日 16:27:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75578217.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定