MongoDB:每个user_id聚合和计算项目

时间:2014-01-19 12:56:35

标签: mongodb count mapreduce

我是MongoDB和Map-Reduce的新手,我无法找到问题的解决方案。

我有多个文档存储了“user_id”和一组不同的项目。 user_id不是唯一的。我想要一个集合,我可以看到用户使用某个项目的次数。

文件如下:

db.items.find()
{"items": ["abc", "def", ghi], "user_id": 328252955}
{"items": ["klm"], "user_id": 328250000}
{"items": ["abc", ghi], "user_id": 328252955}

我想要这样的事情:

{ "_id" : { "user_id" : 328252955}, 
        "value" : [{"items": "abc", "count" : 2},{"items": "ghi", "count" : 2},{"items": "def", "count" : 1}]
}

{ "_id" : { "user_id" : 328250000}, 
        "value" : [{"items": "klm", "count" : 1}]
}

但是我能想到的最好的解决方案并不是那样的,因为如果用户使用多种项目,文档会有另一种结构。

看起来像这样:

{ "_id" : { "user_id" : 328252955 }, 
        "value" : { 
            "preferences" : [[{"items": "abc", "count" : 2},{"items": "ghi", "count" : 2},{"items": "def", "count" : 1}]]
              }
}

{ "_id" : { "user_id" : 328250000}, 
        "value" : {"items": "klm", "count" : 1}
}

DEF

为此,我将user_id和items映射到一个键,并将“count:1”作为值发出。 结果文档如下所示:

db.items_01.find()
{ "_id" : { "user_id" : 328250000, "items" : [  "klm" ] }, "value" : { "count" : 1 } }
{ "_id" : { "user_id" : 328252955, "items" : [  "abc" ] }, "value" : { "count" : 2 } }
{ "_id" : { "user_id" : 328252955, "items" : [  "ghi" ] }, "value" : { "count" : 2 } }
{ "_id" : { "user_id" : 328252955, "items" : [  "def" ] }, "value" : { "count" : 1 } }

Map-Reduce:

map = function() {

  if (this.items == null){
    return;
  }

  for(var i in this.items){
    key = { user_id: this.user_id, items: this.items[i] };    
    value = { count: 1 };
    emit(key, value);
  }
}

function reduce(key, values) {
    var cnt = 0;
    for (var i = 0; i < values.length; i++) {
        cnt += 1;
    }
    return { count: cnt };
}
db.items.mapReduce(map, reduce, "items_01")

为了获得上面显示的最终输出,我使用了另一个Map-Reduce函数,其中我使用user_id作为键,使用“items”和“count”作为值。

Map-Reduce:

map = function() {
    key = { user_id: this._id.user_id };    
    value = {items: this._id.items, count: this.value.count};
    emit(key, value);  
}



reduce = function(key, values) {
  return {preferences:[values]};
}
db.items_01.mapReduce(map, reduce, "items_01_01")

我知道我无法返回values数组。但我还能做些什么呢?

感谢您的帮助。非常感谢。我已经花了好几天时间,我仍在努力寻找最佳解决方案。

1 个答案:

答案 0 :(得分:0)

您应该可以执行以下操作:

db.items.aggregate(
     {$project: { items: 1, user_id: 1}}, 
     {$unwind: "$items"}, 
     { $group: 
         { _id: { user_id: "$user_id", item: "$items" }, 
           count: { $sum: 1 }  
         }  
     })

解释

选择/缩小将在聚合中使用的字段:

 {$project: { items: 1, user_id: 1}}

这会展开items数组。这意味着对于items数组中的每个项目,管道将具有一个不同的文档,其中包含在第一步中投影的字段:

 {$unwind: "$items"}, 

结果:

{
    "_id" : ObjectId("52dbed562c3f37cf53664faf"),
    "items" : "abc",
    "user_id" : 328252955
},
{
    "_id" : ObjectId("52dbed562c3f37cf53664faf"),
    "items" : "def",
    "user_id" : 328252955
},  // ETC...

然后,最后$groupuser_id上的item分组(我已使用语法item: "$items"将其重命名为项目)。对于每个不同的分组值,使用1的{​​{1}}将count添加到名为$sum的新字段中。

1

最终结果

使用您的示例数据会导致类似:

{ $group: 
         { _id: { user_id: "$user_id", item: "$items" }, 
           count: { $sum: 1 }  
         }  
}