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

NestJS/Prisma中如何按字段提取分组数据的唯一值

解决NestJS+Prisma获取多字段唯一值并转换为指定格式的问题

问题分析

你当前用groupBy查询得到的是字段组合的唯一数组,但前端需要的是单个字段各自的唯一值集合,确实无法直接通过Prisma的groupBy返回目标格式,需要通过查询优化或数据转换实现,同时满足动态字段、关联字段的需求。


方案一:单独查询每个字段的Distinct值(推荐)

这种方式直接针对每个字段查询唯一值,数据库返回数据量小,性能更优,同时天然支持动态字段。

实现代码

async getFilterOptions(fields: (keyof Prisma.PeopleSelect)[]) {
  const filterOptions: Record<string, any[]> = {};

  for (const field of fields) {
    // 查询当前字段的所有唯一值,仅返回该字段
    const fieldValues = await this.prisma.people.findMany({
      distinct: [field],
      select: { [field]: true },
    });

    // 提取值并过滤空值(避免数据库中存在null/undefined的情况)
    filterOptions[field] = fieldValues
      .map(item => item[field])
      .filter(value => value !== null && value !== undefined);
  }

  return filterOptions;
}

调用示例

// 传入需要获取唯一值的字段数组,支持动态添加
const filterOptions = await this.getFilterOptions([
  'profileType',
  'preferredLanguage',
  'department',
  'country' // 新增字段直接加入数组即可
]);

关联字段处理(如国家关联表)

如果字段是关联表的属性(比如country关联到Country表),可以通过嵌套查询获取关联字段的唯一值:

async getFilterOptionsWithRelations() {
  const filterOptions: Record<string, any[]> = {};

  // 获取关联的国家名称唯一值
  const countryValues = await this.prisma.people.findMany({
    distinct: ['countryId'], // 按关联ID去重
    select: {
      country: { select: { name: true } }
    },
  });
  filterOptions['country'] = countryValues
    .map(item => item.country?.name)
    .filter(Boolean);

  // 其他普通字段查询...
  const profileTypes = await this.prisma.people.findMany({
    distinct: ['profileType'],
    select: { profileType: true },
  });
  filterOptions['profileType'] = profileTypes.map(item => item.profileType).filter(Boolean);

  return filterOptions;
}

方案二:转换GroupBy查询结果

如果坚持使用groupBy查询,可通过转换函数将组合结果转为目标格式,同样支持动态字段:

转换函数实现

function transformGroupByResult(
  groupedData: Record<string, any>[],
  targetFields: string[]
): Record<string, any[]> {
  // 用Set存储唯一值,自动去重
  const resultMap: Record<string, Set<any>> = {};

  // 初始化每个字段的Set容器
  targetFields.forEach(field => {
    resultMap[field] = new Set();
  });

  // 遍历分组数据,收集每个字段的唯一值
  groupedData.forEach(item => {
    targetFields.forEach(field => {
      const value = item[field];
      if (value !== null && value !== undefined) {
        resultMap[field].add(value);
      }
    });
  });

  // 将Set转为数组,得到目标格式
  return Object.fromEntries(
    Object.entries(resultMap).map(([key, set]) => [key, Array.from(set)])
  );
}

使用示例

const groupedData = await this.prisma.people.groupBy({
  by: ['profileType', 'preferredLanguage', 'department'],
});

const filterOptions = transformGroupByResult(
  groupedData,
  ['profileType', 'preferredLanguage', 'department']
);

注意事项

  • 动态字段传入时,要确保字段名与Prisma模型中的字段完全一致,避免报错;
  • 过滤空值是为了避免前端过滤器出现无效的null/undefined选项;
  • 关联字段查询时,建议按关联ID去重(distinct: ['countryId']),避免因关联表数据重复导致的冗余。

内容的提问来源于stack exchange,提问作者Israel Pereira

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 01:09:26