![](/img/trans.png)
[英]Firestore Pagination : StartAfter query with DocumentSnapshot not working
[英]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.