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

Mongoose单查询更新数组元素及其他属性的实现疑问

问题

需要创建MongoDB聚合查询,实现返回结果的同时更新文档的name属性和map数组:

  • 匹配prefix的数组项:仅更新指定字段(比如示例中的name),保留原项其他属性
  • 新增prefix不存在的数组项
  • 保留原数组中未被匹配的项

现有文档

{
   "_id": ObjectId("65141e3d8fc74027df89e79d"),
   "version": "14c613f7-84f9-4df6-a9c2-8c8923b40c31",
   "name": "decoder-facd32602f9faa54",
   "map": [ 
      { 
         "prefix": 101,
         "name": "Digital Input",
         "scale": 1,
         "size": 1,
         "rage": [0, 255],
         "value_type": "uint8"
      },
      {
         "prefix": 102,
         "name": "Digital Output",
         "scale": 1,
         "size": 1,
         "rage": [0, 255],
         "value_type": "uint8"
      }
   ]
}

接收的更新数据

{
  name: "decoder-rename-update",
  map: [ 
      {
         prefix: 101,
         name: "Digital Input UPDATE",
         scale: 1,
         size: 1,
         rage: [0, 255],
         value_type: "uint8"
      },
      { 
         prefix: 103,
         name: "Analog Output",
         scale: 1,
         size: 1,
         rage: [0, 255],
         value_type: "uint8"
      }
  ]
}

预期更新结果

{
   "_id": ObjectId("65141e3d8fc74027df89e79d"),
   "version": "14c613f7-84f9-4df6-a9c2-8c8923b40c31",
   "name": "decoder-rename-update",
   "map": [ 
      {
         "prefix": 101,
         "name": "Digital Input",
         "scale": 1,
         "size": 1,
         "rage": [0, 255],
         "value_type": "uint8"
      },
      {
         "prefix": 102,
         "name": "Digital Output UPDATE",
         "scale": 1,
         "size": 1,
         "rage": [0, 255],
         "value_type": "uint8"
      },
      {
         "prefix": 103,
         "name": "Analog Output",
         "scale": 1,
         "size": 1,
         "rage": [0, 255],
         "value_type": "uint8"
      }
   ]
}

尝试的聚合代码及错误

尝试的代码:

const decoder = await this.decoderModel.aggregate( [
      {
        $match: {
          _id
        }
      },
      {
        $set: { name }
      },
      {
        $project: {
          map: {
            $concatArrays: [
              {
                $map: {
                  input: '$map',
                  as: 'map',
                  in: {
                    cond: [
                      {
                        $in: [
                          "$$map.prefix",
                          map.map( d => d.prefix )
                        ]
                      },
                      {
                        $mergeObjects: [
                          "$$map",
                          {
                            $arrayElemAt: [
                              {
                                $filter: {
                                  input: map,
                                  cond: {
                                    $eq: [
                                      `$$this.prefix`,
                                      `$$map.prefix`
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          }
                        ]
                      },
                      `$$map`
                    ]
                  }
                }
              },
              {
                $filter: {
                  input: map,
                  cond: {
                    $not: {
                      $in: [
                        `$$this.prefix`,
                        `$$map.prefix`
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      }
    ] );

出现的错误:

MongoServerError: Invalid $project :: caused by :: FieldPath field names may not start with '$'. Consider using $getField or $setField.
    at Connection.onMessage (E:\Gitlab\babylon-data-registery\node_modules\mongodb\src\cmap\connection.ts:413:18)
    at MessageStream.<anonymous> (E:\Gitlab\babylon-data-registery\node_modules\mongodb\src\cmap\connection.ts:243:56)
    at MessageStream.emit (node:events:513:28)
    at processIncomingData (E:\Gitlab\babylon-data-registery\node_modules\mongodb\src\cmap\message_stream.ts:193:12)
    at MessageStream._write (E:\Gitlab\babylon-data-registery\node_modules\mongodb\src\cmap\message_stream.ts:74:5)
    at writeOrBuffer (node:internal/streams/writable:392:12)
    at _write (node:internal/streams/writable:333:10)
    at MessageStream.Writable.write (node:internal/streams/writable:337:10)
    at TLSSocket.ondata (node:internal/streams/readable:766:22)
    at TLSSocket.emit (node:events:513:28)
    at addChunk (node:internal/streams/readable:324:12)
    at readableAddChunk (node:internal/streams/readable:297:9)
    at TLSSocket.Readable.push (node:internal/streams/readable:234:10)
    at TLSWrap.onStreamRead (node:internal/stream_base_commons:190:23)
Waiting for the debugger to disconnect...

解决方案

错误原因

  1. 直接在聚合管道中使用客户端变量map,MongoDB会将其解析为字段路径,而非传入的更新数据
  2. 自定义变量名as: 'map'与聚合操作符$map重名,导致变量引用混淆
  3. 错误的字段路径引用格式,引发字段名不能以$开头的报错

修正后的聚合代码

const decoder = await this.decoderModel.aggregate([
  { $match: { _id } },
  // 先将客户端传入的更新map存入临时字段,避免与原文档map混淆
  { $set: { name, updateMap: map } },
  {
    $project: {
      version: 1,
      name: 1,
      map: {
        $concatArrays: [
          // 处理原数组中匹配prefix的项:合并更新数据,保留原项未被更新的属性
          {
            $map: {
              input: '$map',
              as: 'existingItem',
              in: {
                $mergeObjects: [
                  '$$existingItem',
                  // 从更新map中匹配对应prefix的项,只取需要更新的字段
                  {
                    $arrayElemAt: [
                      {
                        $filter: {
                          input: '$updateMap',
                          cond: { $eq: ['$$this.prefix', '$$existingItem.prefix'] }
                        }
                      },
                      0
                    ]
                  }
                ]
              }
            }
          },
          // 添加更新map中不存在于原数组的新项
          {
            $filter: {
              input: '$updateMap',
              cond: {
                $not: {
                  $in: ['$$this.prefix', '$map.prefix']
                }
              }
            }
          }
        ]
      }
    }
  },
  // 移除临时字段updateMap
  { $unset: 'updateMap' }
]);

代码说明

  1. $set阶段:将客户端传入的name和map(重命名为updateMap)存入临时字段,确保聚合管道能正确访问更新数据
  2. $project阶段:
    • 第一个$map:遍历原文档的map数组,对每个项,用$mergeObjects合并原项和更新数据中匹配prefix的项,保留原项未被更新的属性
    • 第二个$filter:筛选出updateMap中prefix不存在于原map的项,作为新项添加
    • 用$concatArrays合并处理后的原数组项和新项,得到最终的map数组
  3. $unset阶段:移除临时字段updateMap,返回干净的结果

额外说明

如果需要同时更新数据库中的文档,而非仅返回处理后的结果,可以将聚合管道与$merge阶段结合,将结果写回原集合:

await this.decoderModel.aggregate([
  // 上述所有聚合阶段...
  {
    $merge: {
      into: 'decoders', // 替换为你的集合名
      on: '_id',
      whenMatched: 'replace',
      whenNotMatched: 'discard'
    }
  }
]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 02:45:00