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
相关产品推荐
相关产品推荐

