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

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 },
]);

说明

  1. 先定义filteredDataExpr统一处理data数组的过滤逻辑,减少重复代码
  2. 遍历columnNamesToChoose数组,为每个字段在projectFields中添加对应的$map表达式,确保每个字段都是顶层平级键
  3. 最终$project阶段使用构建好的projectFields,输出即为预期的平级字段格式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:15:33