在Mongodb中聚合的有效方式

时间:2015-06-05 11:30:34

标签: python mongodb pymongo aggregation-framework

我有一个集合

{
"name" : "foo"
"clicked" : {"0":6723,"1": 1415,"2":1122}
}
{
    "name" : "bar"
    "clicked" : {"8":1423,"9": 1415,"10":1122}
    }
{
"name" : "xyz"
"clicked" : {"22":6723,"23": 1415,"2":1234}
}

点击基本上是{"position of item-clicked in the list" : "id of the item"}

我想要的最终输出是项目被点击的总次数,即以上样本的以下内容:

    {
     6723:2, 
     1415:3, 
     1423:1,
     1122:2,
     1234:1
    }

通过维护内存字典(在python脚本中)并在每个文档中查找"clicked"字段来更新字典来实现此目的的一种方法。 我是mongo的新手请帮忙!

3 个答案:

答案 0 :(得分:2)

使用collections.Counter

In [58]: import pymongo

In [59]: from collections import Counter

In [61]: conn = pymongo.MongoClient()

In [62]: db = conn.test

In [63]: col = db.collection

In [64]: result = col.aggregate([{"$group": {"_id": None, "clicked": {"$push": "$clicked"}}}]).next()['clicked']

In [65]: c = Counter()

In [66]: for el in [Counter(i.values()) for i in result]:
   ....:     c += el
   ....:     

In [67]: print(dict(c))
{1122: 2, 6723: 2, 1415: 3, 1234: 1, 1423: 1}

答案 1 :(得分:1)

我终于能够构建map-reduce聚合来完成我的工作,而无需更改架构。

expression = '''([^'']+)''';
matchStr = regexp(myStr,expression,'match');

答案 2 :(得分:0)

如果您可以取消当前架构并以clicked是一个以键值对作为其元素的数组的方式重新设计它,那么您可以应用聚合框架来实现所需的结果。

在Mongo中,您可以使用 forEach() 游标的 find() 方法对文档进行迭代并更新点击后来转换架构具有键值对对象数组的字段:

db.collection.find().forEach(function (doc){
    var obj     = {},
        keys    = Object.keys(doc.clicked), 
            clicked = keys.map(function (key){ 
                obj.position = parseInt(key);
                obj.elementId = doc.clicked[key]
                return obj;
            }); 
    doc.clicked = clicked;
    db.collection.save(doc);
});

使用上述方法更改架构后,您的文档将具有以下结构:

{
    "name": "foo",
    "clicked": [
        { "position": 0, "elementId": 6723 },
        { "position": 1, "elementId": 1415 },
        { "position": 2, "elementId": 1122 }
    ]
},
{
    "name": "bar",
    "clicked": [
        { "position": 8, "elementId": 1423 },
        { "position": 9, "elementId": 1415 },
        { "position": 10, "elementId": 1122 }
    ]    
},
{
    "name": "xyz"
    "clicked": [
        { "position": 22, "elementId": 6723 },
        { "position": 23, "elementId": 1415 },
        { "position": 2,  "elementId": 1234 }
    ]
}

使用 aggregation framework 获得所需的聚合将是一件非常容易的事。这需要一个由 $unwind $group 运算符组成的聚合管道, $unwind 作为其第一个管道步骤。这将从输入文档中解构clicked数组字段,以输出每个元素的文档。每个输出文档都使用元素值替换数组。

$group 运算符按指定的elementId标识符/键对输入文档进行分组,并将累加器表达式 $sum 应用于每个组都会给出分组文档的计数:

var pipeline = [
      {
        "$unwind": "$clicked"
      },
      {
        "$group": {
          "_id": "$clicked.elementId",
          "count": {
            "$sum": 1
          }
        }
      }
    ];
    db.collection.aggregate(pipeline)

<强>输出

/* 0 */
{
    "result" : [ 
        {
            "_id" : 1234,
            "count" : 1
        }, 
        {
            "_id" : 1423,
            "count" : 1
        }, 
        {
            "_id" : 1122,
            "count" : 2
        }, 
        {
            "_id" : 1415,
            "count" : 3
        }, 
        {
            "_id" : 6723,
            "count" : 2
        }
    ],
    "ok" : 1
}

将结果转换为您需要的对象只需采用聚合游标结果的 map() 方法:

var result = db.test.aggregate(pipeline)
               .map(function(doc){ return {doc["_id"]: doc["count"]} });
printjson(result);

<强>输出

[
    {
         6723: 2, 
         1415: 3, 
         1423: 1,
         1122: 2,
         1234: 1
    }
]