MongoDB数组嵌套字段批量重命名及save方法失效问题求助
MongoDB批量重命名嵌套数组字段解决方案
集合文档结构
{ "_id" : NumberLong(7499), "CommitId" : LUUID("dee196c8-8a7b-d146-b1e8-f5c7279f056e"), "CommitStamp" : ISODate("2017-11-01T05:58:40.331Z"), "Headers" : [ [ "AggregateType", "Kimo.Engine.Model.Documents.Document" ], [ "command.timestamp", "2017-11-01T05:58:40.3318022Z" ] ], "Events" : [ { "StreamRevision" : 1, "Payload" : { "Headers" : {}, "Body" : { "_t" : "DocumentCreatedFromMobile_1", "MessageId" : LUUID("70dbc516-9f0e-8c4d-a502-3876c5f09309"), "AggregateId" : "Document_49", "Code" : "Document_49", "Info" : { "DocumentMarkup" : "0.0", "DocumentMarkupOnNetCost" : "100.0", "Lines" : [ { "LineMarkup" : "0.0", "LineMarkupOnNetCost" : "100.0" } ] }, "UserId" : { "_id" : "User_4" }, "CreationDate" : ISODate("2017-10-31T10:00:55.000Z"), "MobileId" : "C896E1DE-7B8A-46D1-B1E8-F5C7279F056E", "UploadToServerTimeStamp" : ISODate("2017-11-01T05:58:40.331Z") } } }, { "StreamRevision" : 2, "Payload" : { "Headers" : {}, "Body" : { "_t" : "DocumentArchivedFromMobile_1", "MessageId" : LUUID("310473ef-9d23-9242-a483-fa8daed06cbf"), "AggregateId" : "Document_49", "MobileId" : "C896E1DE-7B8A-46D1-B1E8-F5C7279F056E", "SyncReference" : null, "Info" : { "DocumentMarkup" : "0.0", "DocumentMarkupOnNetCost" : "100.0", "Lines" : [ { "LineMarkup" : "0.0", "LineMarkupOnNetCost" : "100.0" } ] }, "UserId" : { "_id" : "User_4" }, "LastModifiedTimeStamp" : ISODate("2017-10-31T10:00:55.000Z"), "UploadToServerTimeStamp" : ISODate("2017-11-01T05:58:40.331Z") } } } ], "Dispatched" : false, "StreamRevisionFrom" : 1, "StreamRevisionTo" : 2, "BucketId" : "Kimo", "StreamId" : "Document_49", "CommitSequence" : 1 }
需求
批量更新所有满足以下条件的文档:
StreamId匹配正则/Document_/BucketId等于'Kimo'
需要完成的字段操作:
- 将
Events.Payload.Body.Info.DocumentMarkupOnNetCost重命名为DocumentSaleMargin,并删除DocumentMarkupAmountOnNetCost字段 - 将
Events.Payload.Body.Info.Lines.LineMarkupOnNetCost重命名为LineSaleMargin,并删除LineMarkupAmountOnNetCost字段
原尝试代码(save方法失效)
var docs = db.getCollection('Commits').find({StreamId: /Document_/, BucketId:'Kimo'}); docs.forEach(function(doc){ doc.Events.forEach(function(event){ event.Payload.Body.Info.DocumentSaleMargin = event.Payload.Body.Info.DocumentMarkupOnNetCost; delete event.Payload.Body.Info["DocumentMarkupOnNetCost"]; delete event.Payload.Body.Info["DocumentMarkupAmountOnNetCost"]; event.Payload.Body.Info.Lines.forEach(function(line){ line.LineSaleMargin = line.LineMarkupOnNetCost; delete line["LineMarkupOnNetCost"]; delete line["LineMarkupAmountOnNetCost"]; }) }) db.getCollection('Commits').save(doc) })
解决方案
方案1:使用updateOne替代save方法
save()方法在新版MongoDB中已被标记为过时,推荐使用updateOne确保修改正确写入数据库:
db.getCollection('Commits').find({StreamId: /Document_/, BucketId:'Kimo'}).forEach(function(doc) { // 遍历Events数组修改字段 doc.Events.forEach(function(event) { // 处理Info层级的字段 if (event.Payload?.Body?.Info) { event.Payload.Body.Info.DocumentSaleMargin = event.Payload.Body.Info.DocumentMarkupOnNetCost; delete event.Payload.Body.Info.DocumentMarkupOnNetCost; delete event.Payload.Body.Info.DocumentMarkupAmountOnNetCost; // 处理Lines数组的字段 if (event.Payload.Body.Info.Lines && Array.isArray(event.Payload.Body.Info.Lines)) { event.Payload.Body.Info.Lines.forEach(function(line) { line.LineSaleMargin = line.LineMarkupOnNetCost; delete line.LineMarkupOnNetCost; delete line.LineMarkupAmountOnNetCost; }); } } }); // 使用updateOne更新文档,通过_id定位 db.getCollection('Commits').updateOne( { _id: doc._id }, { $set: { Events: doc.Events } } ); });
方案2:使用聚合管道更新(MongoDB 4.2+推荐)
如果你的MongoDB版本是4.2或以上,推荐使用聚合管道在服务端完成修改,无需拉取数据到客户端,效率更高:
db.getCollection('Commits').updateMany( { StreamId: /Document_/, BucketId: 'Kimo' }, [ { $set: { Events: { $map: { input: "$Events", as: "event", in: { $mergeObjects: [ "$$event", { Payload: { $mergeObjects: [ "$$event.Payload", { Body: { $mergeObjects: [ "$$event.Payload.Body", { Info: { $mergeObjects: [ "$$event.Payload.Body.Info", { DocumentSaleMargin: "$$event.Payload.Body.Info.DocumentMarkupOnNetCost", DocumentMarkupOnNetCost: "$$REMOVE", DocumentMarkupAmountOnNetCost: "$$REMOVE", Lines: { $map: { input: "$$event.Payload.Body.Info.Lines", as: "line", in: { $mergeObjects: [ "$$line", { LineSaleMargin: "$$line.LineMarkupOnNetCost", LineMarkupOnNetCost: "$$REMOVE", LineMarkupAmountOnNetCost: "$$REMOVE" } ] } } } } ] } } ] } } ] } } ] } } } } } ] );
说明
- 方案1适配所有MongoDB版本,但数据量大时需拉取文档到客户端修改,效率较低
- 方案2利用MongoDB服务端聚合能力完成修改,性能更优,适合大规模批量更新
$$REMOVE操作符用于删除旧字段,仅MongoDB 4.2及以上版本支持
内容的提问来源于stack exchange,提问作者Stefano.DePietri
相关产品推荐
相关产品推荐

