Mongoose聚合:如何根据用户请求动态创建字段
MongoDB聚合返回字段格式异常修正
场景与问题
在Nest.js项目中使用Mongoose调用MongoDB聚合,对文档做格式化和过滤。现有MongoDB文档结构如下:
{ _id: '1', outputs: [ { fileName: 'fileName1', data: [ {columnName1: 3, columnName2: 4, ..., columnName30: 5}, {columnName1: 1, columnName2: 2, ..., columnName30: 3}, ... ] }, { fileName: 'fileName1', data: [ {columnName1: 3, columnName2: 4, ..., columnName30: 5}, {columnName1: 1, columnName2: 2, ..., columnName30: 3}, ... ] }, ... ] }
需求:
- 仅返回用户指定的
columnNamesToChoose字段 - 根据
mainColumnName的gte、lte条件过滤数据
现有聚合代码尝试在$project阶段用map生成目标字段,但输出出现带索引的嵌套对象(如"0""1"键),不符合平级字段的预期格式。
现有聚合代码
const mainColumnName = 'column1'; // 来自用户请求体 const columnNamesToChoose = ['column2', 'column5']; // 来自用户请求体 myModel.aggregate([ { $match: { _id: Number(id) } }, { $unwind: '$outputs' }, { $match: { 'outputs.fileName': fileName } }, { $project: { _id: '$_id', fileName: '$outputs.fileName', [mainColumnName]: { $map: { input: { $filter: { input: '$outputs.data', as: 'item', cond: { $and: [ { $gte: [`$$item.${mainColumnName}`, gte] }, { $lte: [`$$item.${mainColumnName}`, lte] }, ], }, }, }, as: 'file', in: `$$file.${mainColumnName}`, }, }, }, }, ]);
当前输出结果
{ "0": {"column2": [4,2,1,5]}, "1": {"column5": [1,8,9,0]}, "_id": 1, "fileName": "somefilename.txt", "column1": [3,1,2,20] }
预期输出结果
{ "_id": 1, "fileName": "somefilename.txt", "column1": [3,1,2,20], "column2": [4,2,1,5], "column5": [1,8,9,0] }
解决方案
问题出在动态构建$project字段的方式,需要将columnNamesToChoose中的每个字段作为平级字段添加到$project中,而非生成带索引的嵌套结构。通过动态构建$project字段对象实现:
const mainColumnName = 'column1'; const columnNamesToChoose = ['column2', 'column5']; const gte = ...; // 用户传入的gte值 const lte = ...; // 用户传入的lte值 // 统一定义data数组的过滤逻辑,避免重复代码 const filteredDataExpr = { $filter: { input: '$outputs.data', as: 'item', cond: { $and: [ { $gte: [`$$item.${mainColumnName}`, gte] }, { $lte: [`$$item.${mainColumnName}`, lte] }, ], }, }, }; // 初始化$project的基础字段 const projectFields = { _id: '$_id', fileName: '$outputs.fileName', [mainColumnName]: { $map: { input: filteredDataExpr, as: 'file', in: `$$file.${mainColumnName}`, }, }, }; // 为每个要选择的字段添加平级的map表达式 columnNamesToChoose.forEach(col => { projectFields[col] = { $map: { input: filteredDataExpr, as: 'file', in: `$$file.${col}`, }, }; }); // 执行聚合 myModel.aggregate([ { $match: { _id: Number(id) } }, { $unwind: '$outputs' }, { $match: { 'outputs.fileName': fileName } }, { $project: projectFields }, ]);
说明
- 先定义
filteredDataExpr统一处理data数组的过滤逻辑,减少重复代码 - 遍历
columnNamesToChoose数组,为每个字段在projectFields中添加对应的$map表达式,确保每个字段都是顶层平级键 - 最终
$project阶段使用构建好的projectFields,输出即为预期的平级字段格式
内容的提问来源于stack exchange,提问作者nayaCirdan
相关产品推荐
相关产品推荐

