假设我有这样的文件
{
"id" : "1415166669",
"color" : {
"14" : "Silver"
},
"name":"Random Name"
}
其中内部颜色键可以更改,即“下一个文档”可能如下所示
{
"id" : "1415126969",
"color" : {
"11" : "Gold"
},
"name":"Random Name 2"
}
我想展平并重命名它们,以便我的文档具有如下结构:
{
"id" : "1415126969",
"color" : "Gold"
"name":"Random Name 2"
}
和
{
"id" : "1415166669",
"color" : "Silver"
"name":"Random Name"
}
我尝试使用聚合管道,但我不确定如何继续进行。 任何人都可以建议使用Robomongo的方式,这也很好。
答案 0 :(得分:5)
我会为此运行一个简单的JavaScript循环,因为它将非常快速且易于编码。它也可以应用于同一个集合:
var ops = [];
db.collection.find().forEach(function(doc) {
let color;
Object.keys(doc.color).forEach(function(key) {
color = doc.color[key];
});
ops = [
...ops,
{ "updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "color": color } }
}}
];
if ( ops.length >= 500 ) {
db.collection.bulkWrite(ops);
ops = [];
}
})
if ( ops.length > 0 ) {
db.collection.bulkWrite(ops);
ops = [];
}
所以你基本上遍历对象的“键”并获取值,稍后我们用$set
或者,从MongoDB 3.4开始,您可以使用$objectToArray
运行聚合语句以访问密钥。但是你真的只应该在集合有数百万个文档的情况下这样做。 $out
的要求意味着结果是新集合,而不是当前正在更新的结果:
db.collecion.aggregate([
{ "$addFields": {
"color": {
"$arrayElemAt": [
{ "$map": {
"input": { "$objectToArray": "$color" },
"as": "c",
"in": "$$c.v"
}},
0
]
}
}},
{ "$out": "newcollection" }
])
这是有效的,因为$objectToArray
会改变你的结构:
"color": { "11": "Gold" }
进入这个:
"color": [{ "k": "11", "v": "Gold" }]
因此,我们可以使用$map
运算符来提取"color.v"
路径值,并$arrayElemAt
将其转换为奇异值而不是数组。
一般来说,对于聚合管道语句而言,这种转换要比通过在代码中操作文档结构然后写回目标集合而实现的更复杂。
鉴于现有文件:
{
"_id" : ObjectId("59389951fc04695e84e7f4ae"),
"id" : "1415166669",
"color" : {
"14" : "Silver"
},
"name" : "Random Name"
}
{
"_id" : ObjectId("59389a75fc04695e84e7f4af"),
"id" : "1415126969",
"color" : {
"11" : "Gold"
},
"name" : "Random Name 2"
}
两种方法的输出是:
{
"_id" : ObjectId("59389951fc04695e84e7f4ae"),
"id" : "1415166669",
"color" : "Silver",
"name" : "Random Name"
}
{
"_id" : ObjectId("59389a75fc04695e84e7f4af"),
"id" : "1415126969",
"color" : "Gold",
"name" : "Random Name 2"
}