MongoDB MapReduce - 如何在reduce函数中填充数组?

时间:2016-10-05 07:39:05

标签: mongodb hadoop mapreduce nosql

我有一个包含userIdmovieIdmovie-categoryIdreviewIdmovieRatingreviewDate列的MovieRatings数据库。

在我的映射器中,我想提取 userId - > (movieId,movieRating)

然后在reducer中我想按用户对所有 movieId,movieRating 对进行分组。

这是我的尝试:

地图功能

var map = function() {
    var values={movieId : this.movieId, movieRating : this.movieRating};
    emit(this.userId, values);}

减少功能

var reduce = function(key,values) {
    var ratings = [];
    values.forEach(function(V){
        var temp = {movieId : V.movieId, movieRating : V.movieRating};
        Array.prototype.push.apply(ratings, temp);
        });
    return {userId : key, ratings : ratings };
}

运行MapReduce

db.ratings.mapReduce(map, reduce, { out: "map_reduce_step1" })

输出db.map_reduce_step1.find()

{ "_id" : 1, "value" : { "userId" : 1, "ratings" : [ ] } } 
{ "_id" : 2, "value" : { "userId" : 2, "ratings" : [ ] } } 
{ "_id" : 3, "value" : { "userId" : 3, "ratings" : [ ] } } 
{ "_id" : 4, "value" : { "userId" : 4, "ratings" : [ ] } } 
{ "_id" : 5, "value" : { "userId" : 5, "ratings" : [ ] } } 
{ "_id" : 6, "value" : { "userId" : 6, "ratings" : [ ] } } 
{ "_id" : 7, "value" : { "userId" : 7, "ratings" : [ ] } } 
{ "_id" : 8, "value" : { "userId" : 8, "ratings" : [ ] } } 
{ "_id" : 9, "value" : { "userId" : 9, "ratings" : [ ] } } 
{ "_id" : 10, "value" : { "userId" : 10, "ratings" : [ ] } } 
{ "_id" : 11, "value" : { "userId" : 11, "ratings" : [ ] } } 
{ "_id" : 12, "value" : { "userId" : 12, "ratings" : [ ] } } 
{ "_id" : 13, "value" : { "userId" : 13, "ratings" : [ ] } } 
{ "_id" : 14, "value" : { "userId" : 14, "ratings" : [ ] } } 
{ "_id" : 15, "value" : { "movieId" : 1, "movieRating" : 3 } } 
{ "_id" : 16, "value" : { "userId" : 16, "ratings" : [ ] } }

我没有得到预期的输出。事实上,这个输出对我来说毫无意义!

这是python相当于我在减速器中尝试做的事情(以防万一减速器的目的不清楚):

def reducer_ratings_by_user(self, user_id, itemRatings):
        #Group (item, rating) pairs by userID
        ratings = []
        for movieID, rating in itemRatings:
            ratings.append((movieID, rating))
        yield user_id, ratings

修改1 @chridam

以下是我真正想要做的概述:

Movies.csv 文件如下所示:

用户id,movieId,电影的categoryId,reviewId,movieRating,REVIEWDATE
1,1,1,1,5,7 /2000分之12
2,1,1,2,5,7 /2000分之12
3,1,1,3,5,7 /2000分之12
4,1,1,4,4,7 /2000分之12
5,1,1,5,4,7 /2000分之12
6,1,1,6,5,7 /2000分之15
1,2,1,7,4,7 /2000分之25
8,1,1,8,4,7 /2000分之28
9,1,1,9,3,8 /2000分之3
...
...

我将其导入mongoDB:

mongoimport --db SomeName --collection ratings --type csv --headerline --file Movies.csv 

然后我尝试应用map-reduce函数,如上所述。在那之后,我会通过做一些像以下的事情将它导出回csv:

mongoexport --db SomeName --collection map_reduce_step1 --csv --out movie_ratings_out.csv --fields ...

这个movie_ratings_out.csv文件应该是:

userId,movieId1,rating1,movieId2,rating2,...
1,1,5,2,4
...
...

因此每一行都包含每个用户的所有(电影,评级)对。

修改2

示例:

db.ratings.find().pretty()
{
    "_id" : ObjectId("57f4a0dd9cb74fc4d344a40f"),
    "userId" : 4,
    "movieId" : 1,
    "movie-categoryId" : 1,
    "reviewId" : 4,
    "movieRating" : 4,
    "reviewDate" : "7/12/2000"
}
{
    "_id" : ObjectId("57f4a0dd9cb74fc4d344a410"),
    "userId" : 5,
    "movieId" : 1,
    "movie-categoryId" : 1,
    "reviewId" : 5,
    "movieRating" : 4,
    "reviewDate" : "7/12/2000"
}
{
    "_id" : ObjectId("57f4a0dd9cb74fc4d344a411"),
    "userId" : 4,
    "movieId" : 2,
    "movie-categoryId" : 1,
    "reviewId" : 6,
    "movieRating" : 5,
    "reviewDate" : "7/15/2000"
}
{
    "_id" : ObjectId("57f4a0dd9cb74fc4d344a412"),
    "userId" : 4,
    "movieId" : 3,
    "movie-categoryId" : 1,
    "reviewId" : 2,
    "movieRating" : 5,
    "reviewDate" : "7/12/2000"
}
...

然后MapReduce预期输出json为:

{
    "_id" : ....,
    "userId" : 4,
    "movieList" : [ {
           "movieId" : 2
           "movieRating" : 5
         },
         {
           "movieId" : 1
           "movieRating" : 4
         }
         ...
        ]
   }
   {
    "_id" : ....,
    "userId" : 5,
    "movieList" : ...
   }
   ...

1 个答案:

答案 0 :(得分:1)

您只需要运行一个汇总管道,该管道由一个汇总文档的 $group 阶段组成。它按指定的标识符表达式对输入文档进行分组,并应用累加器表达式。 $group 管道运算符类似于SQL的GROUP BY子句。在SQL中,除非使用任何聚合函数,否则不能使用GROUP BY。同样,您也必须在MongoDB中使用聚合函数。您可以在此处阅读有关聚合函数的更多信息。

您需要创建movieList数组的累加器运算符为 $push

$group 阶段之后的另一个管道是 $project 运算符,用于选择或重塑流中的每个文档,包括,排除或重命名字段,注入计算字段,创建子文档字段,使用数学表达式,日期,字符串和/或逻辑(比较,布尔,控制)表达式 - 类似于您对SQL {{1}所做的操作}子句。

最后一步是 $out 管道,它将汇总管道的结果文档写入集合。它必须是管道中的最后一个阶段。

因此,您可以运行以下聚合操作:

SELECT

使用上面的示例5文档,如果查询db.ratings.aggregate([ { "$group": { "_id": "$userId", "movieList": { "$push": { "movieId": "$movieId", "movieRating": "$movieRating", } } } }, { "$project": { "_id": 0, "userId": "$_id", "movieList": 1 } }, { "$out": "movie_ratings_out" } ]) ,示例输出将产生:

db.getCollection('movie_ratings_out').find({})