You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 08:12:06