使用MongoDB框架将keymap转换为vector

时间:2015-04-21 09:03:44

标签: mongodb aggregation-framework

我在MongoDB的集合x上有这样的文档:

{
    "_id" : ...
    "attrKeys": [ "A1", "A2" ],
    "attrs" : {
        "A1" : {
            "type" : "T1",
            "value" : "13"
        },
        "A2" : {
            "type" : "T2",
            "value" : "14"
        }
    }
}

上面的A1A2元素只是示例:attrs字段可以包含任意数量的任意名称的键。 attrs中的密钥名称存储在attrNames字段中。

我想使用MongoDB聚合框架将该文档转换为如下文档:

{
    "_id" : ...
    "attrs" : [
        {   
            "key": "A1",
            "type" : "T1",
            "value" : "13"
        },
        {   
            "key": "A2",
            "type" : "T2",
            "value" : "14"
        }
    ]
}

也就是说,将attrs变成数组,哪些元素与键值相同"传递"键入名为key的每个数组元素内的新字段。

有可能使用聚合框架进行suck转换吗?我倾向于认为可以使用$project运算符,但我还没弄清楚如何运作。

2 个答案:

答案 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" }
    }}
])