簡體   English   中英

為什么 firebase 查詢中的 startAfter 不起作用?

[英]Why does startAfter in this firebase query isn't working?

我有下一個 function 在 JavaScript 應用程序中使用 firebase。 查詢第一次運行良好,因為 lastDoc 未定義,因此它獲取數據庫中的前 7 個文檔,但下一次有 lastDoc 時,查詢繼續返回相同的前 7 個文檔。 lastDoc 變量被更新並且確實獲得了下一個文檔的值。


const getPosts = async ({loadMore=false, lastDoc}: { loadMore: boolean, lastDoc?: any}) => {
    let col = collection(db, "posts");

    let q = query(col,
            orderBy("updateDate", "desc"),
            limit(7),
            startAfter(lastDoc));
    
    const querySnapshot = await getDocs(q);

    let newLastDoc = querySnapshot.docs[querySnapshot.size-1];

    let posts = querySnapshot.docs.map(doc => {
        if(doc.data().inactive == false || !doc.data().inactive) return {...doc.data(), id: doc.id}
        else return null
    }).filter((post: any) => post !== null);

    return {posts, lastDoc: newLastDoc};
}

第一次 lastDoc 未定義,返回文檔 1、2、3、4、5、6 和 7。第二次 lastDoc 是:

{
  _firestore: {...},
  _userDataWriter: {...},
  _key: {...},
  _document: {..., data: {DATA_FROM_THE_7th_DOC}},
  _converter: null
}

,並不斷返回文檔 1、2、3、4、5、6 和 7

為什么它不起作用?

在 JavaScript SDK 的舊版本中, startAfter被定義為:

Query<T>#startAfter(...fieldValues: any[]): Query<T>;
Query<T>#startAfter(snapshot: DocumentSnapshot<any>): Query<T>;

在現代的JavaScript SDK中, startAfter定義為:

function startAfter(...fieldValues: unknown[]): QueryStartAtConstraint;
function startAfter(snapshot: DocumentSnapshot<any>): QueryStartAtConstraint;

這意味着您的代碼應該按預期運行。 但是,根據評論中提供的項目代碼,這是針對 Express API,而不是客戶端 JavaScript。

您的查詢未按預期工作,因為lastDoc根本不是真正的DocumentSnapshot object ,而是由res.json()生成的 JavaScript 版本。 我們不應該將損壞的DocumentSnapshot object 返回給客戶端,而應該只發回我們需要作為startAfter arguments 傳遞的數據。在我們的例子中,我們將只發回lastDocData中的updateDate參數:

return {
    posts,
    lastDocData: {
        updateDate: newLastDoc.get("updateDate")
    }
};

然后當數據返回到 API 時,我們將它傳遞給startAfter

startAfter(lastDocData.updateDate)) // Assumes lastDocData is { updateDate: number }

在查看methods/getPosts.ts時,我注意到類似於以下內容的行:

let q = !loadMore
    ? category && category !== "TODOS"
        ? query(
            col,
            where("school", "==", school?.code),
            where("category", "==", category),
            where("inactive", "==", false),
            orderBy("updateDate", "desc"),
            limit(7)
        )
        : query(
            col,
            where("school", "==", school.code),
            where("inactive", "==", false),
            orderBy("updateDate", "desc"),
            limit(7)
        )
    : category && category === "TODOS" // note: condition does not match above
        ? query(
            col,
            where("school", "==", school?.code),
            where("inactive", "==", false),
            orderBy("updateDate", "desc"),
            limit(7),
            startAfter(lastDocData.updateDate))
        )
        : query(
            col,
            where("school", "==", school.code),
            where("category", "==", category),
            where("inactive", "==", false),
            orderBy("updateDate", "desc"),
            limit(7),
            startAfter(lastDocData.updateDate)
        );

因為query采用可變數字 arguments,所以您可以使用擴展運算符 ( ... ) 選擇性地包括一些 arguments。作為一個簡化示例:

let includeC = false,
    result = [];

result.push(
  'a',
  'b',
  ...(includeC ? ['c'] : []),
  'd'
);

console.log(result); // => ['a', 'b', 'd']

includeC = true;
result = [];

result.push(
  'a',
  'b',
  ...(includeC ? ['c'] : []),
  'd'
);

console.log(result); // => ['a', 'b', 'c', 'd']

這允許您改用以下查詢構建器:

const q = query(
    colRef,
    ...(school?.code ? [where("school", "==", school.code)] : []), // include school code filter, only if provided
    ...(category && category !== "TODOS" ? [where("category", "==", category)] : []), // include category filter, only if provided and not "TODOS"
    where("inactive", "==", false),
    orderBy("updateDate", "desc"),
    limit(7),
    ...(lastDocData?.updateDate ? [startAfter(lastDocData.updateDate)] : []) // Assumes lastDocData is a { updateDate: number }
);

把這一切放在一起給出:

// methods/getPosts.ts
import { School } from '../../types';
import app from '../../firebaseconfig';
import { getFirestore, collection, query, where, getDocs, limit, orderBy, startAfter } from 'firebase/firestore/lite';

const db = getFirestore(app);

const getPosts = ({category, school, dataSavingMode=false, lastDocData}: {category: string, school: School, dataSavingMode: boolean, lastDocData?: { updateDate: number }}) => {
    const colRef = collection(db, dataSavingMode ? "dataSavingPosts" "posts");

    const q = query(
        colRef,
        ...(school?.code ? [where("school", "==", school.code)] : []),
        ...(category && category !== "TODOS" ? [where("category", "==", category)] : []),
        where("inactive", "==", false),
        orderBy("updateDate", "desc"),
        limit(7),
        ...(lastDocData?.updateDate ? [startAfter(lastDocData.updateDate)] : []),
    );

    const querySnap = await getDocs(q),
        postDocsArr = querySnap.docs,
        lastDoc = postDocsArr[postDocsArr.length - 1],
        noMorePosts = postDocsArr.length < 7;
        
    const postDataArr = postDocsArr
        .filter(docSnap => !docSnap.get("inactive")) // shouldn't be needed with above filter
        .map(docSnap => ({ ...docSnap.data(), id: docSnap.id }));

    return {
        posts: postDataArr,
        noMorePosts,
        lastDocData: { updateDate: lastDoc.get("updateDate") }
    }
}

export default getPosts;
// routes/getPosts.ts
import express from 'express';
import { School } from '../../types';

const router = express.Router();

// Methods
import getPosts from '../methods/getPosts';

router.post('/', async (req, res) => {
    const reqBody: {
        school: School,
        category: string,
        lastDocData?: any,
        dataSavingMode?: boolean
    } = req.body;

    const {posts, lastDocData, noMorePosts} = await getPosts({
        category: reqBody.category,
        school: reqBody.school,
        dataSavingMode: reqBody.dataSavingMode,
        lastDocData: reqBody.lastDocData
    });

    res.json({posts, lastDocData, noMorePosts});
})

export default router;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM