适用版本: v1.0.8+
最后更新: 2026-01-15
monSQLize 提供了三种 update 方法:
| 方法 | 说明 | 聚合管道支持 |
|---|---|---|
updateOne() |
更新单个匹配的文档 | ✅ v1.0.8+ |
updateMany() |
更新所有匹配的文档 | ✅ v1.0.8+ |
updateBatch() |
分批更新大量文档 | ✅ v1.0.8+ |
从 v1.0.8 开始,所有 update 方法都支持聚合管道语法,提供更强大的字段计算和转换能力。
await users.updateOne(
{ userId: 'user1' },
{ $set: { name: 'Alice', age: 25 } }
);await users.updateOne(
{ userId: 'user1' },
{ $unset: { tempField: '' } }
);await users.updateOne(
{ userId: 'user1' },
{ $inc: { loginCount: 1, balance: -100 } }
);await users.updateOne(
{ userId: 'user1' },
{ $push: { tags: 'newTag' } }
);await users.updateOne(
{ userId: 'user1' },
{ $pull: { tags: 'oldTag' } }
);await users.updateOne(
{ userId: 'user1' },
{
$set: { status: 'active' },
$inc: { loginCount: 1 },
$push: { loginHistory: new Date() }
}
);什么是聚合管道更新?
聚合管道更新允许你在 update 操作中使用聚合表达式,支持:
- ✅ 字段间计算(引用其他字段)
- ✅ 条件表达式($cond、$switch)
- ✅ 数组操作($arrayElemAt、$slice)
- ✅ 字符串操作($concat、$trim)
- ✅ 日期计算($add、$subtract)
- ✅ 多阶段转换(多个 $set/$unset)
MongoDB 版本要求: MongoDB 4.2+
await collection.updateOne(
filter, // 筛选条件(对象)
[ // ✨ 聚合管道(数组)
{ $set: { field1: expression1 } },
{ $unset: ['field2'] },
{ $addFields: { field3: expression3 } }
],
options // 可选参数
);| 操作符 | 说明 | 示例 |
|---|---|---|
$set |
设置字段值 | { $set: { total: { $add: ['$a', '$b'] } } } |
$unset |
删除字段 | { $unset: ['tempField'] } |
$addFields |
添加字段($set 的别名) | { $addFields: { computed: '$value' } } |
$project |
字段投影 | { $project: { name: 1, age: 1 } } |
$replaceRoot |
替换根文档 | { $replaceRoot: { newRoot: '$nested' } } |
$replaceWith |
替换文档($replaceRoot 的别名) | { $replaceWith: '$newDoc' } |
需求: 订单总价 = 单价 × 数量 + 运费
await orders.updateOne(
{ orderId: 'ORDER-123' },
[
{
$set: {
totalPrice: {
$add: [
{ $multiply: ['$unitPrice', '$quantity'] },
'$shippingFee'
]
},
updatedAt: new Date()
}
}
]
);为什么用聚合管道?
- ✅ 一次操作完成计算
- ✅ 避免先查询再计算
- ✅ 服务端计算,减少网络往返
需求: 根据积分自动设置会员等级
await users.updateOne(
{ userId: 'user1' },
[
{
$set: {
memberLevel: {
$switch: {
branches: [
{ case: { $gte: ['$points', 10000] }, then: 'platinum' },
{ case: { $gte: ['$points', 5000] }, then: 'gold' },
{ case: { $gte: ['$points', 1000] }, then: 'silver' }
],
default: 'bronze'
}
}
}
}
]
);为什么用聚合管道?
- ✅ 复杂条件判断
- ✅ 原子操作,避免竞态条件
- ✅ 代码更简洁
需求: 提取数组第一个元素作为默认值
await products.updateOne(
{ productId: 'p123' },
[
{
$set: {
defaultImage: { $arrayElemAt: ['$images', 0] },
totalTags: { $size: '$tags' },
firstTag: { $arrayElemAt: ['$tags', 0] }
}
}
]
);需求: 生成全名字段
await users.updateOne(
{ userId: 'user1' },
[
{
$set: {
fullName: {
$concat: ['$firstName', ' ', '$lastName']
},
displayName: {
$cond: [
{ $ne: ['$nickname', null] },
'$nickname',
{ $concat: ['$firstName', ' ', '$lastName'] }
]
}
}
}
]
);需求: 设置过期时间(创建时间 + 30天)
await subscriptions.updateOne(
{ subscriptionId: 'sub123' },
[
{
$set: {
expiresAt: {
$add: ['$createdAt', 30 * 24 * 60 * 60 * 1000] // +30天(毫秒)
}
}
}
]
);需求: 数据清洗、计算、时间戳更新
await products.updateOne(
{ productId: 'p789' },
[
// 阶段1: 数据规范化
{
$set: {
name: { $trim: { input: '$name' } },
sku: { $toUpper: '$sku' }
}
},
// 阶段2: 计算派生字段
{
$set: {
discountedPrice: {
$multiply: [
'$price',
{ $subtract: [1, '$discountRate'] }
]
}
}
},
// 阶段3: 更新时间戳
{
$set: { updatedAt: new Date() }
}
]
);需求: 订单状态自动流转
await orders.updateOne(
{ orderId: 'ORDER-456' },
[
{
$set: {
status: {
$cond: [
{ $eq: ['$paymentStatus', 'paid'] },
{
$cond: [
{ $eq: ['$inventoryStatus', 'reserved'] },
'processing',
'pending-inventory'
]
},
'pending-payment'
]
},
updatedAt: new Date()
}
}
]
);| 需求 | 传统方式 | 聚合管道方式 | 优势 |
|---|---|---|---|
| 字段间计算 | ❌ 需查询→计算→更新 | ✅ 一次操作 | 减少网络往返 |
| 条件赋值 | ❌ 需多次查询判断 | ✅ 原子操作 | 避免竞态条件 |
| 数组操作 | ✅ 完整支持 | 更灵活 | |
| 字符串拼接 | ❌ 需客户端处理 | ✅ 服务端计算 | 性能更好 |
| 日期计算 | ❌ 需客户端计算 | ✅ 服务端计算 | 避免时区问题 |
✅ 适用场景:
- 简单的字段赋值(
$set、$unset) - 数值增减(
$inc) - 数组元素添加/删除(
$push、$pull) - 不需要字段间计算
示例:
// ✅ 简单赋值,用传统方式即可
await users.updateOne(
{ userId: 'user1' },
{ $set: { status: 'active' }, $inc: { loginCount: 1 } }
);✅ 适用场景:
- 需要引用其他字段值
- 需要条件表达式(if/switch)
- 需要复杂的数组/字符串/日期操作
- 需要多阶段数据转换
示例:
// ✅ 字段间计算,必须用聚合管道
await orders.updateOne(
{ orderId: 'ORDER-123' },
[
{ $set: { total: { $add: ['$price', '$tax'] } } }
]
);// ❌ 错误:简单赋值使用聚合管道(过度复杂)
await users.updateOne({ userId: 'user1' }, [
{ $set: { status: 'active' } }
]);
// ✅ 正确:简单赋值使用传统操作符
await users.updateOne({ userId: 'user1' }, {
$set: { status: 'active' }
});
// ✅ 正确:字段间计算使用聚合管道
await orders.updateOne({ orderId: 'ORDER-123' }, [
{ $set: { total: { $add: ['$price', '$tax'] } } }
]);// ❌ 过度分阶段(不必要)
await products.updateOne({ productId: 'p1' }, [
{ $set: { field1: value1 } },
{ $set: { field2: value2 } },
{ $set: { field3: value3 } }
]);
// ✅ 合并到一个阶段
await products.updateOne({ productId: 'p1' }, [
{
$set: {
field1: value1,
field2: value2,
field3: value3
}
}
]);
// ✅ 有依赖关系时才分阶段
await products.updateOne({ productId: 'p1' }, [
// 阶段1: 先规范化数据
{ $set: { price: { $toDouble: '$priceString' } } },
// 阶段2: 再基于规范化后的数据计算
{ $set: { discountedPrice: { $multiply: ['$price', 0.9] } } }
]);try {
await users.updateOne(
{ userId: 'user1' },
[{ $set: { total: { $add: ['$a', '$b'] } } }]
);
} catch (error) {
if (error.code === 'UNSUPPORTED_OPERATION') {
// MongoDB 版本不支持聚合管道
console.error('请升级到 MongoDB 4.2+');
} else {
// 其他错误
console.error('更新失败:', error.message);
}
}// ✅ 确保筛选字段有索引
await users.createIndex({ userId: 1 });
await users.updateOne({ userId: 'user1' }, [...]);// ❌ 过度复杂(性能差)
await users.updateOne({ userId: 'user1' }, [
{
$set: {
result: {
$cond: [
{ $and: [
{ $gte: ['$a', 10] },
{ $lte: ['$b', 20] },
{ $ne: ['$c', null] },
{ $in: ['$d', ['x', 'y', 'z']] }
]},
{ $multiply: ['$e', { $add: ['$f', '$g'] }] },
{ $divide: ['$h', { $subtract: ['$i', '$j'] }] }
]
}
}
}
]);
// ✅ 简化逻辑(或拆分为多次操作)
await users.updateOne({ userId: 'user1' }, [
{ $set: { temp: { $add: ['$f', '$g'] } } },
{ $set: { result: { $multiply: ['$e', '$temp'] } } },
{ $unset: ['temp'] }
]);| 操作类型 | 传统操作符 | 聚合管道 | 性能差异 |
|---|---|---|---|
| 简单赋值 | 快 | 略慢 | ~5-10% |
| 字段间计算 | 不支持 | 快 | - |
| 条件逻辑 | 多次操作 | 一次完成 | 快 50%+ |
| 复杂表达式 | 不支持 | 中等 | - |
-
简单操作优先用传统操作符
// ✅ 快速 await users.updateOne({ _id }, { $set: { name: 'Alice' } });
-
复杂计算才用聚合管道
// ✅ 适合 await orders.updateOne({ _id }, [ { $set: { total: { $add: ['$price', '$tax'] } } } ]);
-
批量更新使用 updateBatch
// ✅ 大批量更新(10000+) await users.updateBatch( { status: 'inactive' }, [{ $set: { status: 'archived' } }], { batchSize: 1000 } );
A: 不会。聚合管道中的字符串保持原样,不会自动转换为 ObjectId。
// ❌ 不会自动转换
await users.updateOne({ _id }, [
{ $set: { refId: '507f1f77bcf86cd799439011' } } // 保持字符串
]);
// ✅ 需要手动转换(如果需要)
const { ObjectId } = require('mongodb');
await users.updateOne({ _id }, [
{ $set: { refId: new ObjectId('507f1f77bcf86cd799439011') } }
]);A: 支持大部分聚合表达式操作符,包括:
- 算术:
$add,$subtract,$multiply,$divide,$mod - 条件:
$cond,$switch,$ifNull - 数组:
$arrayElemAt,$size,$slice,$filter - 字符串:
$concat,$substr,$trim,$toUpper,$toLower - 日期:
$dateToString,$year,$month,$dayOfMonth - 类型:
$type,$convert,$toDouble,$toString
完整列表请参考: MongoDB 聚合表达式
A: 会。空数组不是有效的聚合管道。
// ❌ 错误:空数组
await users.updateOne({ _id }, []);
// Error: update 聚合管道不能为空数组
// ✅ 正确:至少包含一个阶段
await users.updateOne({ _id }, [
{ $set: { updatedAt: new Date() } }
]);A: 使用日志记录和分阶段测试。
// 1. 开启调试日志
const msq = new MonSQLize({
config: { uri: '...' },
logger: console // 输出调试日志
});
// 2. 分阶段测试
// 先测试第一阶段
await users.updateOne({ _id }, [
{ $set: { step1: { $add: ['$a', '$b'] } } }
]);
// 再添加第二阶段
await users.updateOne({ _id }, [
{ $set: { step1: { $add: ['$a', '$b'] } } },
{ $set: { step2: { $multiply: ['$step1', 2] } } }
]);A: 会。与传统更新操作一样,聚合管道更新后会自动失效相关缓存。
// 查询并缓存
await users.find({ status: 'active' }, { cache: 5000 });
// 聚合管道更新(缓存会自动失效)
await users.updateOne({ userId: 'user1' }, [
{ $set: { status: 'inactive' } }
]);
// 下次查询会重新从数据库读取
await users.find({ status: 'active' }, { cache: 5000 });文档版本: v1.0.0
适用于: monSQLize v1.0.8+
最后更新: 2026-01-15