Flutter项目中Firebase多条件where查询索引过多如何解决?
解决方案
方案1:建单组全量联合索引(推荐,无需改业务代码)
Firestore 对全是 isEqualTo 条件的查询有索引兼容规则:只要你把所有可能用到的相等过滤字段全部放在联合索引的前半部分,排序字段放在最后,就能覆盖所有过滤条件的组合场景,不需要为每个组合单独建索引。
比如你的场景用到3个相等过滤字段directionId、flag、officeId,排序字段为createdAt降序,只需要建1个联合索引:
directionId升序、flag升序、officeId升序、createdAt降序
不管你查询时传入1个、2个、3个过滤条件,还是不传任何过滤条件,这个索引都能正常匹配。你原来的第一版代码不需要任何修改,直接用这个索引即可。
方案2:冗余标签数组适配动态过滤(适合过滤字段多的场景)
如果你的过滤字段会不断新增,不想每次加字段都重建索引,可以给文档冗余一个filterTags数组字段,把所有过滤条件的键值对存到数组里,查询时用arrayContainsAll匹配。
步骤1:写入文档时生成标签
// 写入Transac文档前生成过滤标签 List<String> buildFilterTags(String? directionId, bool? flag, String? officeId) { final tags = <String>[]; if (directionId != null) tags.add('directionId:$directionId'); if (flag != null) tags.add('flag:$flag'); if (officeId != null) tags.add('officeId:$officeId'); return tags; }
步骤2:修改查询逻辑
Future<List<Transac>> getTrans(TransFilter filter, Transac? lastTrans) async { const limit = 10; var result = _collection.orderBy(filter.orderBy, descending: true).limit(limit); final queryTags = <String>[]; if (filter.directionId != null) queryTags.add('directionId:${filter.directionId}'); if (filter.flag != null) queryTags.add('flag:${filter.flag}'); if (filter.officeId != null) queryTags.add('officeId:${filter.officeId}'); if (queryTags.isNotEmpty) { result = result.where('filterTags', arrayContainsAll: queryTags); } if (lastTrans != null) { result = result.startAfter([lastTrans.createdAt.millisecondsSinceEpoch]); } return await result.get().then( (value) => value.docs.map((e) => Transac.fromSnapshot(e)).toList() ); }
索引要求
只需要建1个联合索引即可覆盖所有过滤组合:
filterTags数组、[你的排序字段]降序
注意事项
- 仅适用于相等判断的过滤场景,不支持范围查询
arrayContainsAll最多同时匹配10个标签,即最多同时传10个过滤条件
方案3:客户端二次过滤(适合数据量小的场景)
如果你的单页数据量不大、分页要求不高,可以先按排序规则拉取数据,在客户端过滤不符合条件的条目,仅需要排序字段的单字段索引即可,缺点是分页时可能出现单页返回数据少于limit的情况。
内容的提问来源于stack exchange,提问作者pckim
相关产品推荐
相关产品推荐

