Firebase:无需父设备ID查询子集合最旧文档并最小化读取次数的方案
实现方案
最优实现思路(仅产生1次读取)
利用Firestore的集合组查询能力,可实现最低读取成本的查询:
- 提前给所有
callbacks子集合的文档冗余存储你需要用来筛选的设备唯一字段(比如设备序列号、设备MAC等,根据你的实际业务字段调整),写入callback数据时直接附带该字段即可,不需要额外修改其他逻辑 - 给
callbacks集合组创建查询索引:索引字段为【你用来筛选的设备唯一字段名 + timestamp】,查询范围选择集合组
代码示例
admin .firestore() .collectionGroup("callbacks") // 集合组查询,匹配所有名为callbacks的子集合 .where("你要筛选的设备唯一字段名", "==", "目标筛选值") // 替换成实际的筛选字段和值 .orderBy("timestamp", "asc") // 按时间戳升序,最旧文档排在首位 .limit(1) .get() .then((data) => { let callbacks = []; data.forEach((doc) => { callbacks.push(doc.data()); }); return res.json(callbacks); }) .catch((err) => console.error(err));
备选方案(不修改数据结构,仅产生2次读取)
如果你不想调整现有callbacks文档的字段结构,可以用固定2次读取的方案,不会随callback数量增加读取成本:
- 第一次查询符合筛选条件的设备文档,因为字段是唯一的,仅返回1条结果,消耗1次读取
- 用拿到的设备文档ID直接查询对应子集合的最早文档,消耗1次读取
代码示例
const db = admin.firestore(); // 第一步查询符合条件的设备 db.collection("devices") .where("你要筛选的设备唯一字段名", "==", "目标筛选值") .limit(1) .get() .then((deviceSnapshot) => { if (deviceSnapshot.empty) { return res.json([]); } const deviceDoc = deviceSnapshot.docs[0]; // 第二步查询对应设备下的最早callback return deviceDoc.ref.collection("callbacks") .orderBy("timestamp", "asc") .limit(1) .get(); }) .then((callbackSnapshot) => { if (!callbackSnapshot) return; let callbacks = []; callbackSnapshot.forEach(doc => callbacks.push(doc.data())); return res.json(callbacks); }) .catch((err) => console.error(err));
读取成本对比
- 原有实现:1次(查设备)+ N次(设备下所有callback文档数),成本随callback数量线性增长
- 集合组方案:仅1次读取,固定成本
- 备选方案:仅2次读取,固定成本
内容的提问来源于stack exchange,提问作者Brave
相关产品推荐
相关产品推荐

