如何使用whereArrayContains实现Firestore两个数组的元素逐个匹配
Firestore数组多元素包含匹配解决方案
问题原因
Firestore原生的whereArrayContains仅支持判断数组是否包含单个元素,whereArrayContainsAny仅支持判断数组是否包含给定列表中的任意一个元素,都不满足「包含给定列表全部元素」的查询需求,所以直接传入列表作为whereArrayContains的参数不会生效。
可行解决方案
方案1:服务端粗筛+客户端精筛(无需改数据结构,通用首选)
先从你要匹配的数组里挑1个出现频率低的元素,用whereArrayContains先过滤掉绝大多数不匹配的文档,拿到查询结果后在客户端做二次校验,判断文档的ingredients是否包含你自定义数组的所有元素。
修改后代码示例:
List<String> ingreList; String[] ingredientLists; String[] test = {"sugar","b"}; private void checkTesting() { // 先拿目标数组的第一个元素做粗筛,可替换为出现频率更低的元素提升效率 postRef.whereArrayContains("ingredients", test[0]) .get() .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() { @Override public void onSuccess(QuerySnapshot queryDocumentSnapshots) { postLists3.clear(); List<String> targetList = Arrays.asList(test); for(QueryDocumentSnapshot documentSnapshot: queryDocumentSnapshots){ // 取出文档里的ingredients数组 List<String> docIngredients = (List<String>) documentSnapshot.get("ingredients"); // 二次校验:是否包含目标数组的所有元素 if (docIngredients != null && docIngredients.containsAll(targetList)) { Post post = documentSnapshot.toObject(Post.class); postLists3.add(post); } } postAdapter.notifyDataSetChanged(); progressBar.setVisibility(View.GONE); } }); }
- 优点:不需要修改现有数据结构,适配任意数量的匹配元素
- 缺点:如果首次筛选命中的文档量很大,会有额外的带宽消耗
方案2:修改数据结构为Map类型(服务端直接过滤,性能最优)
提前修改Firestore的文档结构,新增ingredients_map字段,把每个食材作为key,value固定为true,示例结构:
{ "ingredients": ["sugar", "b", "c"], "ingredients_map": { "sugar": true, "b": true, "c": true } }
查询的时候链式调用whereEqualTo匹配所有你要的元素即可,修改后代码示例:
private void checkTesting() { Query query = postRef; // 循环添加所有匹配条件 for (String ingredient : test) { query = query.whereEqualTo("ingredients_map." + ingredient, true); } query.get() .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() { @Override public void onSuccess(QuerySnapshot queryDocumentSnapshots) { postLists3.clear(); for(QueryDocumentSnapshot documentSnapshot: queryDocumentSnapshots){ Post post = documentSnapshot.toObject(Post.class); postLists3.add(post); } postAdapter.notifyDataSetChanged(); progressBar.setVisibility(View.GONE); } }); }
- 优点:完全服务端过滤,查询效率最高
- 限制:Firestore单条查询最多支持10个等值条件,如果你要匹配的元素超过10个不能使用该方案
特殊场景:完全匹配数组所有元素
如果你的需求是文档的ingredients和你自定义的数组完全相等(元素、顺序完全一致),直接用whereEqualTo传入List类型参数即可,原生支持:
postRef.whereEqualTo("ingredients", Arrays.asList(test))
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

