Flutter中如何基于品牌从Firebase产品集合获取唯一分类?
如何从Firebase中高效获取某品牌下有产品的唯一分类?
背景
我在Firebase中创建了3个集合:
- Brands:字段包括
BrandId(String)、Title(String) - Categories:字段包括
CategoryId(String)、Title(String) - Products:字段包括
ProductId(String)、Title(String)、CategoryId(String)、BrandId(String)、IsFeautured(bool)
示例数据如下:
产品集合(Products)
static List<ProductModel> products=[ //samsung brands ProductModel(productId: '01', title: 'Galaxy Book1', brandId: '1', categoryId: '2', isFeautured: true), ProductModel(productId: '02', title: 'Galaxy Book2', brandId: '1', categoryId: '2', isFeautured: true), ProductModel(productId: '03', title: 'S21 Ultra', brandId: '1', categoryId: '1', isFeautured: true), ProductModel(productId: '07', title: 'S3 Gear Watch', brandId: '1', categoryId: '3', isFeautured: true), ]
品牌集合(Brands)
static List<BrandModel> brands=[ BrandModel(brandId: '1', title: 'Samsung', isFeautured: true), BrandModel(brandId: '2', title: 'Apple', isFeautured: true), BrandModel(brandId: '3', title: 'Nokia', isFeautured: true), ];
分类集合(Categories)
static List<CategoryModel> cateogories=[ CategoryModel(categoryId: '1', title: 'Mobile', isFeautured: true), CategoryModel(categoryId: '2', title: 'Laptop', isFeautured: true), CategoryModel(categoryId: '3', title: 'Watch', isFeautured: true), CategoryModel(categoryId: '4', title: 'Sounds', isFeautured: true), ];
我已经实现了品牌列表页面,点击品牌后跳转到对应页面,需要展示该品牌下有产品的所有唯一分类(比如点击三星,要显示Mobile、Laptop、Watch)。我写了以下方法,但不知道怎么提取唯一分类,同时担心产品上千时的性能问题,请求完善代码:
Future<List<CategoryModel>> fetchAvailableCategoryOfBrand(String brandId) async{ List<CategoryModel> uniqueCategories = []; final _db=FirebaseFirestore.instance; final list=await _db.collection("Products").where('BrandId',isEqualTo: brandId).get(); //this is fetching given brand data..but how to fetch unique category of from it /// what code for fetching unique categories and what if products are in thousands... return uniqueCategories; }
解决方案
1. 基础实现:提取唯一分类
先从查询结果中提取去重的分类ID,再批量查询分类信息,满足基础功能需求:
Future<List<CategoryModel>> fetchAvailableCategoryOfBrand(String brandId) async{ final _db = FirebaseFirestore.instance; // 获取该品牌下所有产品的快照 final productsSnapshot = await _db.collection("Products") .where('BrandId', isEqualTo: brandId) .get(); // 提取并去重CategoryId final Set<String> uniqueCategoryIds = {}; for (var doc in productsSnapshot.docs) { final categoryId = doc.data()['CategoryId'] as String; uniqueCategoryIds.add(categoryId); } if (uniqueCategoryIds.isEmpty) { return []; } // 根据去重后的ID批量查询分类信息 final categoriesSnapshot = await _db.collection("Categories") .where('CategoryId', whereIn: uniqueCategoryIds.toList()) .get(); // 转换为CategoryModel列表返回 return categoriesSnapshot.docs.map((doc) { return CategoryModel( categoryId: doc.data()['CategoryId'], title: doc.data()['Title'], isFeautured: doc.data()['IsFeautured'] ?? false ); }).toList(); }
2. 性能优化:应对上千条产品的场景
当产品数量庞大时,下载全量产品文档会浪费资源,推荐以下两种优化方案:
方案一:使用Firestore的distinct和select减少数据传输
先在Firebase控制台为Products集合创建复合索引:BrandId(升序)+ CategoryId(升序),然后通过select只获取需要的字段、distinct去重,大幅减少数据传输量:
Future<List<CategoryModel>> fetchAvailableCategoryOfBrand(String brandId) async{ final _db = FirebaseFirestore.instance; // 仅获取去重后的CategoryId字段 final categoryIdsSnapshot = await _db.collection("Products") .where('BrandId', isEqualTo: brandId) .select(['CategoryId']) .distinct() .get(); final uniqueCategoryIds = categoryIdsSnapshot.docs .map((doc) => doc.data()['CategoryId'] as String) .toSet() .toList(); if (uniqueCategoryIds.isEmpty) { return []; } // 批量查询分类信息 final categoriesSnapshot = await _db.collection("Categories") .where('CategoryId', whereIn: uniqueCategoryIds) .get(); return categoriesSnapshot.docs.map((doc) { return CategoryModel( categoryId: doc.data()['CategoryId'], title: doc.data()['Title'], isFeautured: doc.data()['IsFeautured'] ?? false ); }).toList(); }
方案二:预存品牌-分类关联数据
如果数据更新频率低,可以在Brands集合中添加availableCategoryIds数组字段,每次增删产品时同步更新该数组(保持去重)。查询时直接从品牌文档获取分类ID,性能最优:
修改后的Brands文档结构示例:
{ "BrandId": "1", "Title": "Samsung", "availableCategoryIds": ["1", "2", "3"] }
对应的查询代码:
Future<List<CategoryModel>> fetchAvailableCategoryOfBrand(String brandId) async{ final _db = FirebaseFirestore.instance; // 获取品牌文档中的可用分类ID final brandDoc = await _db.collection("Brands").doc(brandId).get(); if (!brandDoc.exists) return []; final List<String>? categoryIds = brandDoc.data()?['availableCategoryIds']?.cast<String>(); if (categoryIds == null || categoryIds.isEmpty) return []; // 批量查询分类信息 final categoriesSnapshot = await _db.collection("Categories") .where('CategoryId', whereIn: categoryIds) .get(); return categoriesSnapshot.docs.map((doc) { return CategoryModel( categoryId: doc.data()['CategoryId'], title: doc.data()['Title'], isFeautured: doc.data()['IsFeautured'] ?? false ); }).toList(); }
内容的提问来源于stack exchange,提问作者user22362195
相关产品推荐
相关产品推荐

