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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 13:25:18