Google Cloud Firestore如何仅获取集合/子集合的文档ID?
嘿,这个问题问到点子上了——Firestore在子集合查询的成本控制上确实有技巧,刚好有几个方案能帮你只获取questions子集合的文档ID,不用拉取整个文档内容,我结合你的React+Mobx+Firestore技术栈给你详细说说:
方案1:使用Firestore内置的
__name__字段+select()查询 Firestore每个文档都自带一个内置字段__name__,它对应文档的唯一ID。我们可以利用select()方法指定只返回这个字段,这样Firestore就只会传输文档ID,不会加载其他内容,完美解决成本问题。
在Mobx Store中的实现示例
import { getFirestore, collection, query, select, getDocs, onSnapshot } from "firebase/firestore"; import { makeAutoObservable } from "mobx"; class QuizStore { firestore = getFirestore(); questionIds = []; constructor() { makeAutoObservable(this); } // 一次性获取所有问题ID async fetchQuestionIds(quizId) { try { const questionsRef = collection(this.firestore, `Quiz/${quizId}/questions`); // 仅选择文档的内置ID字段__name__ const q = query(questionsRef, select('__name__')); const snapshot = await getDocs(q); this.questionIds = snapshot.docs.map(doc => doc.id); return this.questionIds; } catch (err) { console.error('获取问题ID失败:', err); throw err; } } // 实时监听问题ID的变化(可选) subscribeToQuestionIds(quizId) { const questionsRef = collection(this.firestore, `Quiz/${quizId}/questions`); const q = query(questionsRef, select('__name__')); return onSnapshot(q, (snapshot) => { this.questionIds = snapshot.docs.map(doc => doc.id); }); } } export default new QuizStore();
方案2:(可选)如果手动存储了ID字段,直接选择该字段
如果你已经在questions文档里手动添加了id字段(和文档ID一致),也可以直接用select('id')来查询,效果和方案1类似,但不如内置字段高效(毕竟少了一个手动维护的字段)。
在React组件中的使用示例
结合Mobx的observer包装组件,实现跳转逻辑:
import { observer } from 'mobx-react-lite'; import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import quizStore from '../stores/QuizStore'; const QuizQuestionList = ({ quizId }) => { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(true); useEffect(() => { // 一次性获取ID const fetchIds = async () => { await quizStore.fetchQuestionIds(quizId); setIsLoading(false); }; // 或者实时监听(按需选择) // const unsubscribe = quizStore.subscribeToQuestionIds(quizId); // setIsLoading(false); // return () => unsubscribe(); fetchIds(); }, [quizId]); if (isLoading) return <div>加载中...</div>; return ( <div className="question-list"> {quizStore.questionIds.map(questionId => ( <button key={questionId} onClick={() => navigate(`/quiz/${quizId}/question/${questionId}`)} className="question-btn" > 跳转至问题 {questionId} </button> ))} </div> ); }; export default observer(QuizQuestionList);
额外注意事项
- 成本说明:Firestore的查询成本是按读取的文档数量计算的,不管你读取多少字段。所以即使只取ID,读取100个文档还是算100次读取,但好处是数据传输量大幅减少,带宽成本更低,也不会加载不必要的内容。
- 实时监听清理:如果使用
onSnapshot实时监听,记得在组件卸载时调用返回的取消订阅函数,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Thiện Thành Thật
相关产品推荐
相关产品推荐

