MongoDB中$addToSet更新数组并同步$size更新长度失败及$inc疑问
MongoDB更新数组去重并同步长度,以及管道中使用$inc的方法
问题背景
定义的Mongoose Schema如下:
const appJobSchema = new mongoose.Schema({ data: [ { type: Schema.Types.Mixed } ], stat: { dataCount: { type: Number, default: 0 }, } })
需求是:向data数组中批量添加记录(自动去重),同时将stat.dataCount同步更新为数组的最新长度。
最初的错误尝试
最初编写的更新代码:
export async function updateDataById(id: string, records: any[]) { return await model.updateOne({ '_id': id }, { '$addToSet': { 'data': { '$each': records } } , '$set': { 'stat.dataCount': { $size: "$data" } } }); }
运行后报错:
UncaughtException: Cast to Number failed for value "{ '$size': '$data' }" (type Object) at path "stat.dataCount"
原因是:传统更新操作(非管道模式)中,$set不能直接使用聚合表达式(比如$size),只能赋值常量或引用文档外的值。
中间尝试的方案
- 使用
$concatArrays拼接数组:能同步更新长度,但无法实现去重,不符合需求; - 在更新管道中直接使用
$addToSet/$inc:报错
UncaughtException: Invalid update pipeline operator: "$addToSet".
因为更新管道只能使用聚合阶段操作符(比如$set、$project等),不能直接用传统的更新操作符($addToSet、$inc属于此类)。
已实现的可行方案
通过$setUnion在更新管道中实现去重更新,并同步数组长度:
await Model.updateOne({'_id':id}, [ { "$set": { "data": { $setUnion: [ "$data", records ] } } }, { '$set': { 'stat.dataCount': { $size: "$data" } } }, ]);
疑问解答:如何在更新管道中使用类似$inc的递增操作
更新管道中无法直接使用$inc操作符,需要通过聚合表达式模拟递增逻辑。核心思路是先计算出增量值,再用$add将原字段值与增量相加。
实现代码示例
如果需要针对stat.dataCount实现类似$inc的效果(比如只增加本次新增的元素数量),可以这样写:
await Model.updateOne({'_id': id}, [ { "$set": { // 计算本次新增的元素(原数组中不存在的记录) "tempAddedElements": { $setDifference: [records, "$data"] }, // 更新data为去重后的完整数组 "data": { $setUnion: ["$data", records] } } }, { "$set": { // 用$add实现递增:原count + 新增元素的数量 "stat.dataCount": { $add: ["$stat.dataCount", { $size: "$tempAddedElements" }] }, // 清理临时字段 "tempAddedElements": "$$REMOVE" } } ]);
逻辑说明
- 用
$setDifference计算出本次要添加的记录中,原数组没有的元素,得到临时数组tempAddedElements; - 用
$size获取临时数组的长度,也就是本次新增的元素数量; - 用
$add将stat.dataCount的原值与新增数量相加,实现类似$inc的递增效果; - 最后用
$$REMOVE删除临时字段,避免污染文档结构。
如果只是需要同步数组的最新长度,直接使用$size: "$data"会更简单,无需计算增量。但如果业务场景需要基于增量更新,上述方法就可以满足需求。
内容的提问来源于stack exchange,提问作者user824624
相关产品推荐
相关产品推荐

