MongoDB 3.6文档结构迁移:合并新旧格式数据方案咨询
解决MongoDB混合格式文档的结构迁移问题
问题背景
需要将MongoDB集合productDetails中的数据从旧结构迁移至新结构:
- 旧结构:
_id为字符串类型 - 新结构:
_id是包含productId(原_id值)和countryCode(固定值DE)的复合对象 - 集合当前包含约4000万旧格式文档,以及70万已完成转换的新格式文档
- 使用MongoDB 3.6,计划通过聚合函数创建新集合后替换旧集合,但现有聚合语句会错误覆盖已有的新格式文档,需修正。
旧结构示例
{ "_id": "some-id", "_class": "org.some.class", "number": 1015, "timestamp": {"$date": "2020-09-05T12:08:02.809Z"}, "cost": 0.9200000166893005 }
新结构示例
{ "_id": { "productId": "some-id", "countryCode": "DE" }, "_class": "org.some.class", "number": 1015, "timestamp": {"$date": "2020-09-05T12:08:02.809Z"}, "cost": 0.9200000166893005 }
现有语句问题
原聚合语句会对所有文档强制重新构造_id,包括已转换的新格式文档,导致这部分数据被错误修改,无法保留:
db.productDetails.aggregate( {$match: {_id: {$exists: true}}}, {$addFields: {"_id": {"productId": "$_id", "country": "DE"}}}, {$project: {_id: 1, _class: 1, number: 1, timestamp: 1, cost: 1}}, {$out: "productDetailsV2"} )
正确聚合方案
通过$cond判断_id的类型,仅对字符串类型的旧格式文档进行转换,保留已有的新格式文档,同时修正字段名(原语句中country应为countryCode):
db.productDetails.aggregate([ { $addFields: { "_id": { $cond: { if: {$eq: [{$type: "$_id"}, "string"]}, then: {productId: "$_id", countryCode: "DE"}, else: "$_id" } } } }, { $project: { _id: 1, _class: 1, number: 1, timestamp: 1, cost: 1 } }, { $out: "productDetailsV2" } ])
关键逻辑说明
- 类型判断:使用
$type检查_id的类型,字符串类型为旧格式文档,对象类型为已转换的新格式文档 - 条件转换:仅对旧格式文档生成复合
_id,新格式文档直接保留原_id - 字段修正:将原语句中的
country改为countryCode,与目标结构一致 - 输出集合:将结果写入新集合
productDetailsV2,避免直接修改原集合
注意事项
- 执行前务必备份原集合,防止数据丢失
- 数据量较大(约4700万条),建议在业务低峰期执行,避免影响正常业务
- MongoDB 3.6支持
$type操作符判断字段类型,字符串类型对应值为"string",对象类型对应值为"object"
内容的提问来源于stack exchange,提问作者xard4sTR
相关产品推荐
相关产品推荐

