Mongoose 聚合:如何根据用户请求动态创建字段

问题描述 投票:0回答:2

请有人帮助我!我在文档或其他主题中找不到解决方案。

我在 Mongoose/Nest.js 项目中使用 mongodb 聚合来返回具有一些格式和过滤的文档数据。我有 mongo 文档的结构,如

{
_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
)。并根据
gte, lte
mainColumnName
过滤它们的值。在
$project
内部,我打算使用这样的映射,但它不起作用。你能帮我修复这部分代码吗?

...columnNamesToChoose.map((columnName) => ({ [columnName]: {
 $map: {
              input: {
                $filter: {
                  input: '$outputs.data',
                  as: 'item',
                  cond: {
                    $and: [
                      { $gte: [`$$item.${mainColumnName}`, gte] },
                      { $lte: [`$$item.${mainColumnName}`, lte] },
                    ],
                  },
                },
              },
              as: 'file',
              in: `$$file.${columnName}`,
            },

} })),

这是聚合的完整代码:

mainColumnName = 'column1' (from the body of the user request)
columnNamesToChoose = ['column2', 'column5'] (from the body of the user request)
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
        ],
}
mongodb mongoose nestjs aggregate nest
2个回答
0
投票

一个选项是先

$reduce
,然后是
$unwind
$match
$group
,其中
$group
阶段是根据输入在代码(for循环)上动态构建的:

db.collection.aggregate([
  {$match: {_id: id}},
  {$project: {
      outputs: {
        $reduce: {
          input: "$outputs",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              {$cond: [
                  {$eq: ["$$this.fileName", fileName]},
                  "$$this.data",
                  []
                ]
              }
            ]
          }
        }
      }
    }
  },
  {$unwind: "$outputs"},
  {$match: {"outputs.columnName1": {$gte: gte, $lte: lte}}},
  {$group: {
      _id: 0,
      column1: {$push: "$outputs.columnName1"},
      column2: {$push: "$outputs.columnName2"},
      column5: {$push: "$outputs.columnName5"}
  }},
  {$set: {fileName: fileName}}
])

游乐场示例中查看它是如何工作的

在 js 上它看起来像:

const matchStage = {$match: {}};
matchStage.$match[`outputs.${mainColumnName}`] = {$gte: gte, $lte: lte};
const groupStage = {$group: {_id: 0}};
for (const col of columnNamesToChoose ) {
  groupStage.$group[col] = {$push: `"$outputs.${col}"`}
};
const aggregation = [
  {$match: {_id: id}},
  {$project: {
      outputs: {$reduce: {
          input: "$outputs",
          initialValue: [],
          in: {$concatArrays: [
              "$$value",
              {$cond: [
                  {$eq: ["$$this.fileName", fileName]},
                  "$$this.data",
                  []
              ]}
          ]}
      }}
  }},
  {$unwind: "$outputs"},
  matchStage,
  groupStage,
  {$set: {fileName: fileName}}
],
  
const res = await myModel.aggregate(aggregation)


0
投票

我找到了对我来说最易读和最有用的答案,它是在

$function
中使用
$project
并仅使用 js 并且没有 mongo 语法在
args
中获取我需要的所有数据

© www.soinside.com 2019 - 2024. All rights reserved.