Flutter查询Firebase Firestore随机文档逻辑问题及去重需求
解决Firestore随机获取食谱重复问题
问题说明
我在Firebase Firestore上有个食谱集合,用Firestore自动生成的文档ID。需要拉取3个随机文档展示给用户,但写的随机ID生成和查询逻辑有时只能拿到2个文档。查了下是因为数据库文档少,会重复拉取同一个文档,存入Hive Box时旧数据被覆盖,导致用户只能看到2个不同食谱。现在要加个判断:如果文档已在Box里,就重新拉取,求代码实现。
文档ID示例
VUHZzY0y3fPihgU5EJSh XaHZzY4y3fPidfU5EJSj ABCZzY0y3fKkjfU5EJDu etc.
原随机ID生成代码
String getRandomGeneratedId() { const int AUTO_ID_LENGTH = 20; const String AUTO_ID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const int maxRandom = AUTO_ID_ALPHABET.length; final Random randomGen = Random(); String id = ''; for (int i = 0; i < AUTO_ID_LENGTH; i++) { id = id + AUTO_ID_ALPHABET[randomGen.nextInt(maxRandom)]; print('RandomID is $id'); } return id; }
原查询逻辑代码
Future<List> getData() async { List<Recipe> dataList = []; // Retrieves 3 random data in a loop for (int i = 0; i < 3; i++) { // generate a random index based on the list length // and use it to retrieve the element String _randomIndex = getRandomGeneratedId(); print('RandomIndex is $_randomIndex'); final query = FirebaseFirestore.instance.collection('recipes') .where("id", isGreaterThanOrEqualTo: _randomIndex) .orderBy("id") .limit(1); QuerySnapshot response = await query.get(); if (response.docs == null || response.docs.length == 0) { final anotherQuery = FirebaseFirestore.instance.collection('recipes') .where('id', isLessThan: _randomIndex) .orderBy('id') .limit(1); response = await anotherQuery.get(); } dataList.addAll(response.docs.map((d)=> Recipe.fromJson(d.data() as Map<String, dynamic>))); final datamodel_transaction = RecipeModelData() ..calories = dataList[i].calories ..carbs = dataList[i].carbs ..title = dataList[i].title ..id = dataList[i].id ..price = dataList[i].price ..url = dataList[i].url // ..servings = dataList[i].servings ..protein = dataList[i].protein ..fat = dataList[i].fat ..ingredients = dataList[i].ingredients ..instructions = dataList[i].instructions; final _box = Boxes.getModelItems(); _box.put(dataList[i].id,datamodel_transaction); print('Values of data model are ${_box.values}'); } return dataList; }
修改后的查询逻辑代码(加入去重判断)
Future<List<Recipe>> getData() async { List<Recipe> dataList = []; final _box = Boxes.getModelItems(); // 循环直到攒够3个不重复的食谱 while (dataList.length < 3) { String _randomIndex = getRandomGeneratedId(); print('RandomIndex is $_randomIndex'); // 先查大于等于随机ID的文档 QuerySnapshot response = await FirebaseFirestore.instance .collection('recipes') .where("id", isGreaterThanOrEqualTo: _randomIndex) .orderBy("id") .limit(1) .get(); // 没查到就查小于随机ID的文档 if (response.docs.isEmpty) { response = await FirebaseFirestore.instance .collection('recipes') .where('id', isLessThan: _randomIndex) .orderBy('id') .limit(1) .get(); } // 确认查询到有效文档 if (response.docs.isNotEmpty) { Recipe currentRecipe = Recipe.fromJson( response.docs.first.data() as Map<String, dynamic>); String recipeId = currentRecipe.id; // 检查这个食谱是不是已经在内存列表或Hive Box里了 bool isInList = dataList.any((recipe) => recipe.id == recipeId); bool isInBox = _box.containsKey(recipeId); if (!isInList && !isInBox) { // 不重复就加入列表并存入Hive dataList.add(currentRecipe); final datamodel_transaction = RecipeModelData() ..calories = currentRecipe.calories ..carbs = currentRecipe.carbs ..title = currentRecipe.title ..id = currentRecipe.id ..price = currentRecipe.price ..url = currentRecipe.url ..protein = currentRecipe.protein ..fat = currentRecipe.fat ..ingredients = currentRecipe.ingredients ..instructions = currentRecipe.instructions; _box.put(recipeId, datamodel_transaction); print('成功添加食谱到Box: $recipeId'); } else { print('食谱$recipeId已经存在,重新拉取...'); } } else { print('没查到对应食谱,重新拉取...'); } } print('最终获取到${dataList.length}个不重复食谱'); return dataList; }
核心修改点
- 把固定次数的
for循环改成while循环,直到拿到3个不重复的食谱为止 - 每次获取到食谱后,先检查ID是否已在内存列表或Hive Box中
- 只有不重复的食谱才会被加入列表并写入Hive,重复的就跳过重新拉取
- 直接取查询结果的第一条文档,避免原代码中
addAll可能导致的索引异常 - 增加调试日志,方便排查重复拉取的情况
内容的提问来源于stack exchange,提问作者NisiCH
相关产品推荐
相关产品推荐

