如何在Kotlin中高效查询含动态嵌套Map时间戳的Firestore文档?
Firestore嵌套动态键结构的当日travel_date查询方案
文档结构说明
现有Firestore文档结构如下:
fieldOne = "value1" fieldTwo = "value2" fieldThree = { flights = { [动态flight_number] = { travel_date = TimeStamp } } }
其中flight_number是无法预先知晓的动态键(比如示例中的LH760),每个文档下可能存在多个不同的flight_number键。
需求
用Kotlin编写高效查询,找出所有travel_date为当日的文档。
问题排查(之前的测试尝试)
你之前用.whereEqualTo("created", LocalDateTime.now())无结果,原因是LocalDateTime不带时区信息,而Firestore的Timestamp是带时区的时间戳,必须将LocalDateTime转换为带时区的Instant后再转成Timestamp才能匹配,示例代码如下:
val now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant() val nowTimestamp = Timestamp.fromDate(Date.from(now)) // 正确的created字段查询示例 firestore.collection("your-collection") .whereEqualTo("created", nowTimestamp)
针对travel_date的查询方案
由于原结构中flight_number是动态键,Firestore不支持直接查询动态映射下的所有子节点字段,这里提供两种可行方案:
方案1:修改数据结构(推荐,符合Firestore查询最优设计)
将flights从动态映射改为数组,每个元素包含flight_number和travel_date字段,调整后的结构示例(Kotlin数据类):
data class Flight( val flight_number: String, val travel_date: Timestamp ) data class YourDocument( val fieldOne: String, val fieldTwo: String, val fieldThree: String, val flights: List<Flight> )
查询代码
首先计算当日的时间范围(从当日00:00到次日00:00),再进行范围查询:
// 计算当日开始和结束时间 val todayStart = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant() val todayEnd = todayStart.plus(1, ChronoUnit.DAYS) val startTimestamp = Timestamp.fromDate(Date.from(todayStart)) val endTimestamp = Timestamp.fromDate(Date.from(todayEnd)) // 执行查询 firestore.collection("your-collection") .whereGreaterThanOrEqualTo("flights.travel_date", startTimestamp) .whereLessThanOrEqualTo("flights.travel_date", endTimestamp) .get() .addOnSuccessListener { snapshot -> // 处理符合条件的文档 for (doc in snapshot) { println("Document ID: ${doc.id}, Data: ${doc.data}") } } .addOnFailureListener { e -> println("查询失败: ${e.message}") }
该方案利用Firestore的数组字段查询能力,直接在服务端完成筛选,效率最高。
方案2:客户端过滤(不推荐,仅适用于文档量极小的场景)
如果无法修改数据结构,只能先拉取所有文档,再在客户端过滤符合条件的内容:
// 计算当日时间范围 val todayStart = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant() val todayEnd = todayStart.plus(1, ChronoUnit.DAYS) firestore.collection("your-collection") .get() .addOnSuccessListener { snapshot -> // 客户端过滤文档 val validDocs = snapshot.filter { doc -> // 解析flights动态映射 val flightsMap = doc.get("fieldThree.flights") as? Map<String, Map<String, Timestamp>> flightsMap?.values?.any { flightData -> val travelInstant = flightData["travel_date"]?.toDate()?.toInstant() // 判断travel_date是否在当日范围内 travelInstant != null && travelInstant.isAfter(todayStart) && travelInstant.isBefore(todayEnd) } ?: false } // 处理过滤后的结果 validDocs.forEach { doc -> println("符合条件的文档ID: ${doc.id}") } } .addOnFailureListener { e -> println("查询失败: ${e.message}") }
注意:此方法会下载所有文档到本地,数据量大时会消耗大量带宽和客户端资源,仅作为临时替代方案。
内容的提问来源于stack exchange,提问作者K K
相关产品推荐
相关产品推荐

