Firestore 日期范围过滤返回全部记录 如何仅返回指定区间数据
问题根因
- 字段格式/类型不匹配:你数据库的
data字段存储的是DD/MM/YYYY格式的字符串,而Firestore对字符串的比较是按逐位字符的字典序执行的,和实际日期的大小规则完全不符。比如日期01/09/2021(9月1日)实际晚于18/08/2021(8月18日),但字符串比较时"01/09/2021"的首字符是0,小于"18/08/2021"的首字符1,会被判定为更小的数值,导致范围过滤完全失效。如果你的dateRangePicker.start/end是Date对象、时间戳或者其他格式的字符串,和数据库存储的字符串类型不匹配,也会导致过滤逻辑不生效。 - 缺少复合索引:Firestore执行多个范围条件的查询时,需要提前创建对应的复合索引,如果没有创建索引,查询可能会报错或者直接返回全量数据。你可以打开浏览器控制台,查看是否有Firestore抛出的索引缺失提示。
修复方案
方案1(推荐):调整存储格式
修改数据库中data字段的存储方式,二选一:
- 存储为Firestore原生的
Timestamp类型,日期比较逻辑完全由Firestore负责,不会出现格式问题 - 存储为
YYYY-MM-DD格式的字符串,这种格式的字典序和日期大小顺序完全一致,可直接用于范围比较,需要同步把查询的起止日期也转为相同格式的字符串再传入where条件
修改后对应的查询代码调整(以Timestamp为例,dateRangePicker.start/end为Date对象即可):
async function toFind() { console.log("start", dateRangePicker.start); console.log("end", dateRangePicker.end); const importacao = db.collection("importacao"); const snapshot = await importacao .orderBy('data') .where("data", '>=', dateRangePicker.start) .where("data", '<=', dateRangePicker.end) .get() if (snapshot.empty) { toast.error('No matching documents.'); return; } // 后续处理逻辑 }
如果控制台有索引缺失提示,直接点击提示中的链接跳转至Firebase控制台,一键创建对应索引即可。
方案2(不推荐):不改存储结构,客户端过滤
如果暂时无法修改数据库存储格式,可以先查询全量数据后在客户端做日期转换过滤,数据量较大时性能会很差:
async function toFind() { const start = new Date(dateRangePicker.start).getTime(); const end = new Date(dateRangePicker.end).getTime(); const importacao = db.collection("importacao"); const snapshot = await importacao.get(); const result = []; snapshot.forEach(doc => { const dataStr = doc.data().data; // 把DD/MM/YYYY转成时间戳比较 const [day, month, year] = dataStr.split('/'); const dataTime = new Date(year, month - 1, day).getTime(); if (dataTime >= start && dataTime <= end) { result.push(doc.data()); } }) if (result.length === 0) { toast.error('No matching documents.'); return; } // 后续处理result }
内容的提问来源于stack exchange,提问作者Fabio Martins
相关产品推荐
相关产品推荐

