Firestore查询复合索引动态值替代方案及投票应用场景问题咨询
解决方案
方案1:调整数据结构(最优,适配所有规模场景)
核心问题出在你用动态字段名(选民UID作为Voters的子字段)做查询条件,Firestore复合索引要求字段名是静态固定的,所以不可能给每个用户单独建索引。调整结构如下:
- 给每个候选人文档新增
unvotedVoterUids数组类型字段,初始化时存入所有有投票权限的选民UID - 当某选民给该候选人投票后,直接将该选民的UID从
unvotedVoterUids数组中移除
调整后的查询代码如下:
FirebaseUser user = mAuth.getCurrentUser(); String voterID = user.getUid(); // 索引仅需建1次:unvotedVoterUids 升序、posOrder 升序、candidateFullName 升序 Query query = notebookRef.orderBy("posOrder").orderBy("candidateFullName") .whereArrayContains("unvotedVoterUids", voterID); FirestoreRecyclerOptions<VoterCandidateItem> options = new FirestoreRecyclerOptions.Builder<VoterCandidateItem>() .setQuery(query, VoterCandidateItem.class) .build(); // 后续适配器、列表逻辑不变
这个方案的复合索引是全局通用的,所有选民查询都复用同一个索引,完全符合业务要求。如果你的选民规模超过单数组存储上限(1MB,约存数万个UID),可以拆分多字段存储或者用下面的方案。
方案2:客户端过滤(适配候选人数量<1000的轻量场景)
如果你的候选人总数不多,可以跳过动态查询条件,先拉取全量排序后的候选人列表,再在适配器层过滤掉当前用户已经投过的候选人,完全不需要额外建复合索引:
FirebaseUser user = mAuth.getCurrentUser(); String voterID = user.getUid(); // 去掉动态where条件,索引只需要静态的 posOrder、candidateFullName 复合索引 Query query = notebookRef.orderBy("posOrder").orderBy("candidateFullName"); // 在适配器的onBindViewHolder方法中增加过滤逻辑,已经投过的项直接隐藏View,或者提前过滤数据集
这个方案改造成本极低,适合小体量投票场景。
方案3:选民专属列表子集合(适配海量选民+海量候选人场景)
如果选民和候选人规模都非常大,可以通过云函数触发,每个选民在自己的用户文档下建立availableCandidates子集合,存储该用户还没投过的候选人数据,投票后云函数自动删除对应子集合文档,查询时直接查该子集合即可,完全不需要动态条件。
内容的提问来源于stack exchange,提问作者HeroreH29
相关产品推荐
相关产品推荐

