Firestore使用arrayContains查询字典数组如何忽略非必要键匹配
核心原因
Firestore 的 arrayContains 操作对数组内的对象类型元素执行全字段精确匹配:只有数组内的对象和你传入的查询对象的字段数量、字段名、对应字段值完全一致时,才会命中结果。
你之前的查询能正常返回,是因为当时数组内存储的邀请对象仅包含otherUserId、inviteStatus两个字段,和查询入参完全吻合;后续给邀请对象新增isInvoiceAccess字段后,数组元素比查询入参多了一个键,自然无法匹配。你在查询条件里补上isInvoiceAccess: true就能查到对应值的记录,也正是符合这个匹配规则。
可行解决方案
方案1:调整数据结构(官方推荐,长期维护成本最低)
不要依赖数组内的完整对象做查询匹配,单独抽离查询维度生成专属查询字段:
- 新增一个专门用于查询的数组字段,比如命名为
inviteQueryTags,数组元素为拼接字符串,格式为{userID}_{inviteStatus},每次新增邀请、修改邀请状态时同步更新该字段 - 查询时直接使用
whereField(FirebaseKeys.ProjectDetails.inviteQueryTags, arrayContains: "\(userID)_\(FirebaseKeys.ProjectDetails.pending)")即可
这种方式下,无论你后续给邀请对象新增多少个业务字段(比如isInvoiceAccess、其他权限标记、时间戳等),都不会影响现有查询逻辑。
方案2:不改动现有数据的临时方案
如果暂时无法迁移历史数据,可以通过多查询合并结果的方式实现:
因为isInvoiceAccess只有true/false两个取值,分别写两个查询覆盖两种情况,最后在本地合并去重结果即可,代码示例:
// 匹配isInvoiceAccess为true的待邀请记录 let pendingQueryWithAccess = FirestoreReferenceManager.rootProjects.whereField( FirebaseKeys.ProjectDetails.inviteData, arrayContains: [ FirebaseKeys.ProjectDetails.otherUserId: userID, FirebaseKeys.ProjectDetails.inviteStatus: FirebaseKeys.ProjectDetails.pending, FirebaseKeys.ProjectDetails.invoiceAccess: true ] ) // 匹配isInvoiceAccess为false的待邀请记录 let pendingQueryWithoutAccess = FirestoreReferenceManager.rootProjects.whereField( FirebaseKeys.ProjectDetails.inviteData, arrayContains: [ FirebaseKeys.ProjectDetails.otherUserId: userID, FirebaseKeys.ProjectDetails.inviteStatus: FirebaseKeys.ProjectDetails.pending, FirebaseKeys.ProjectDetails.invoiceAccess: false ] )
注意:该方案仅适用于新增可选字段取值范围极小的场景,如果后续邀请对象还要新增其他可选字段,需要叠加的查询数量会指数上升,且Firestore单次查询有数组条件数量限制,长期不推荐使用。
内容的提问来源于stack exchange,提问作者Dharmesh Kheni
相关产品推荐
相关产品推荐

