我在MongoDB的集合x
上有这样的文档:
{
"_id" : ...
"attrKeys": [ "A1", "A2" ],
"attrs" : {
"A1" : {
"type" : "T1",
"value" : "13"
},
"A2" : {
"type" : "T2",
"value" : "14"
}
}
}
上面的A1
和A2
元素只是示例:attrs
字段可以包含任意数量的任意名称的键。 attrs
中的密钥名称存储在attrNames
字段中。
我想使用MongoDB聚合框架将该文档转换为如下文档:
{
"_id" : ...
"attrs" : [
{
"key": "A1",
"type" : "T1",
"value" : "13"
},
{
"key": "A2",
"type" : "T2",
"value" : "14"
}
]
}
也就是说,将attrs
变成数组,哪些元素与键值相同"传递"键入名为key
的每个数组元素内的新字段。
有可能使用聚合框架进行suck转换吗?我倾向于认为可以使用$project
运算符,但我还没弄清楚如何运作。
答案 0 :(得分:1)
正如@Philipp在评论中正确提到的那样
拥有未知密钥是MongoDB中一种危险的反模式
但是,如果您事先知道密钥是什么,那么您可以使用聚合运算符$literal,$addToSet
和$setUnion
来获得所需的结果。聚合管道如下:
db.collection.aggregate([
{
"$project": {
"attrs.A1.key": { "$literal": "A1" },
"attrs.A1.type": "$attrs.A1.type",
"attrs.A1.value": "$attrs.A1.value",
"attrs.A2.key": { "$literal": "A2" },
"attrs.A2.type": "$attrs.A2.type",
"attrs.A2.value": "$attrs.A2.value"
}
},
{
"$group": {
"_id": "$_id",
"A1": { "$addToSet": "$attrs.A1" },
"A2": { "$addToSet": "$attrs.A2" }
}
},
{
"$project": {
"attrs": {
"$setUnion": [ "$A1", "$A2" ]
}
}
}
])
<强>结果强>:
/* 0 */
{
"result" : [
{
"_id" : ObjectId("55361320180e849972938fea"),
"attrs" : [
{
"type" : "T1",
"value" : "13",
"key" : "A1"
},
{
"type" : "T2",
"value" : "14",
"key" : "A2"
}
]
}
],
"ok" : 1
}
答案 1 :(得分:1)
聚合框架不是您在此处理转换的方式。在重新编写集合时,您可能一直希望$out
运算符能够提供一些帮助,但聚合框架无法满足您的要求。
基本上,聚合框架缺乏访问&#34;密钥的方法。动态地使用&#34;数据点&#34;以任何方式。您可以像使用mapReduce一样处理数据,但它通常不如使用聚合框架那么高效,而且主要是因为有人指出修改后的结构更好。
另外,尝试使用mapReduce作为重塑&#34;重新塑造&#34;您的存储集合通常不是一个好主意。 MapReduce输出本质上是&#34;始终&#34; &#34;键/值&#34;,这意味着您获得的输出总是包含在强制性&#34;值&#34;字段。
这实际上意味着更改集合的内容,并且在使用文档中存在的值时,唯一可以真正做到这一点的方法是通过&#34;阅读&#34;文档内容然后&#34;写&#34;回来。
使用"Bulk"操作API方法
可以最好地处理循环特性db.collection.intializeOrderedBukOp(), var bulk = db.collection.intializeOrderedBukOp(), count = 0;
db.collection.find({ "attrKeys": { "$exists": true }}).forEach(function(doc) {
// Re-map attrs
var attrs = doc.attrKeys.map(function(key) {
return {
"key": key,
"type": doc.attrs[key].type,
"value": parseInt(doc.attrs[key].value)
};
});
// Queue update operation
bulk.find({ "_id": doc._id, "attrKeys": { "$exists": true } })
.updateOne({
"$set": { "attrs": attrs },
"$unset": { "attrKeys": 1 }
});
count++;
// Execute every 1000
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.collection.intializeOrderedBukOp();
}
});
// Drain any queued remaining
if ( count % 1000 != 0 )
bulk.execute();
更新了收藏内容后(请注意您的&#34;值&#34;字段也已从&#34;字符串&#34;更改为&#34;整数&#34;格式)然后,您可以对新结构执行有用的聚合操作,例如:
db.collection.aggregate([
{ "$unwind": "$attrs" },
{ "$group": {
"_id": null,
"avgValue": { "$avg": "$attrs.value" }
}}
])