MongoDB批量更新:将会话最后一个成员的left_at设为end_time
MongoDB批量更新sessions集合中members数组最后一个元素的left_at字段
需求说明
我有一个包含731,000+文档的sessions集合,原始文档结构如下:
{ _id: ObjectId(""), end_time: Date(164), members: [ { user_id: "1", left_at: Date(73) }, { user_id: "2", left_at: Date(0) } ] }
需要批量更新所有文档,将members数组最后一个元素的left_at字段设置为文档自身的end_time值,更新后预期结构:
{ _id: ObjectId(""), end_time: Date(164), members: [ { user_id: "1", left_at: Date(73) }, { user_id: "2", left_at: Date(164) } ] }
注意:
- 每个文档的
members数组至少包含1个元素 - 「最后一个成员」指索引为
members[members.length-1]的元素
解决方案
使用MongoDB 4.2及以上版本支持的聚合管道式更新,可直接引用文档自身字段并修改数组元素,执行以下命令:
db.sessions.updateMany( {}, // 匹配集合中所有文档 [ { $set: { members: { $concatArrays: [ // 取出members数组除最后一个元素外的所有元素 { $slice: ["$members", 0, { $subtract: [{ $size: "$members" }, 1] }] }, // 取出最后一个元素,替换left_at为end_time后组成新数组 [ { $mergeObjects: [ { $arrayElemAt: ["$members", { $subtract: [{ $size: "$members" }, 1] }] }, { left_at: "$end_time" } ] } ] ] } } } ] )
命令解释
$slice:截取members数组从索引0开始、长度为「数组总长度-1」的部分,即去掉最后一个元素的子数组$arrayElemAt:通过$size获取数组长度后减1得到索引,取出members数组的最后一个元素$mergeObjects:将最后一个成员对象与{ left_at: "$end_time" }合并,覆盖原left_at值$concatArrays:将前面截取的子数组和修改后的最后一个成员重新拼接成新的members数组updateMany({}, [...]):对集合中所有文档执行上述更新逻辑
注意事项
- 由于文档量超过73万,建议先在测试环境验证命令逻辑,避免影响生产数据
- 如果是MongoDB 5.0及以上版本,若担心单次更新压力过大,可使用
bulkWrite分批次执行更新,降低对数据库性能的影响 - 执行更新前确保数据库有足够的CPU、内存资源,避免出现性能瓶颈
内容的提问来源于stack exchange,提问作者haste
相关产品推荐
相关产品推荐

