mongodb mapreduce排除嵌套字段

时间:2014-02-10 15:50:48

标签: mongodb mapreduce

我是mongodb新手!我正在尝试处理一些高音扬声器数据。我的目标是在每个时间间隔(为简单起见,每日间隔)对用户进行分组,并计算当天的唯一主题标签。我的想法是构建新的DB,它只包含用户,日期和主题标签。这是数据格式:

> db.sampledDB.findOne()
{
    "_id" : NumberLong("2334234"),
    "replyid" : NumberLong(-1),
    "userid" : NumberLong(21313),
    "replyuserid" : NumberLong(-1),
    "createdAt" : ISODate("2013-07-02T22:35:06Z"),
    "tweettext" : "RT @BBCBreaking: Plane carrying Bolivia President Morales is diverted to Austria on suspicion US fugitive #Snowden is on board - Bolivian m…",
    "screenName" : "x83",
    "name" : "david x",
    "retweetCount" : NumberLong(0),
    "retweet_id" : NumberLong("12313223"),
    "retweet_userid" : NumberLong(123123123),
    "source" : "<a href=\"http://www.twitter.com\" rel=\"nofollow\">Twitter for Windows Phone</a>",
    "hashtags" : [
        {
            "start" : 106,
            "end" : 114,
            "text" : "Snowden"
        }
    ],
    "mentions" : [
        {
            "start" : 3,
            "end" : 15,
            "id" : NumberLong(876678),
            "screenName" : "BBCBreaking",
            "name" : "BBC Breaking News"
        }
    ],
    "media" : [ ]
}

我像这样使用mapReduce: MAP:

    map = function(){ 
//format date to year/month/day
    var format = this.createdAt.getFullYear() + '/' + (this.createdAt.getMonth()+1) + '/' + this.createdAt.getDate();
    var key = {userid:this.userid, date:format}; 
    emit(key,{hashtags:this.hashtags}); }

REDUCE:

reduce = function(key,values){ 
var result = {a:[]}; 
for (var idx=0;idx<values.length;idx++){ 
result.a.push(values[idx].hashtag); 
} 
return result};

结果是:

{
        "_id" : {
            "userid" : NumberLong(7686787),
            "date" : "2013/7/5"
        },
        "value" : {
            "hashtag" : [
                {
                    "start" : 24,
                    "end" : 44,
                    "text" : "SıkSöylenenYalanlar"
                },
                {
                    "start" : 45,
                    "end" : 60,
                    "text" : "ZimmermanTrial"
                },
                {
                    "start" : 61,
                    "end" : 84,
                    "text" : "ZaynMalikYouArePerfect"
                },
                {
                    "start" : 85,
                    "end" : 99,
                    "text" : "TrayvonMartin"
                },
                {
                    "start" : 100,
                    "end" : 110,
                    "text" : "Wimbledon"
                },
                {
                    "start" : 111,
                    "end" : 118,
                    "text" : "Футбол"
                },
                {
                    "start" : 119,
                    "end" : 127,
                    "text" : "Snowden"
                },
                {
                    "start" : 128,
                    "end" : 138,
                    "text" : "TFFistifa"
                }
            ]
        }
    },
    {
        "_id" : {
            "userid" : NumberLong(45666),
            "date" : "2013/7/5"
        },
        "value" : {
            "hashtag" : [
                {
                    "start" : 24,
                    "end" : 44,
                    "text" : "SıkSöylenenYalanlar"
                },
                {
                    "start" : 45,
                    "end" : 60,
                    "text" : "ZimmermanTrial"
                },
                {
                    "start" : 61,
                    "end" : 84,
                    "text" : "ZaynMalikYouArePerfect"
                },
                {
                    "start" : 85,
                    "end" : 99,
                    "text" : "TrayvonMartin"
                },
                {
                    "start" : 100,
                    "end" : 110,
                    "text" : "Wimbledon"
                },
                {
                    "start" : 111,
                    "end" : 118,
                    "text" : "Футбол"
                },
                {
                    "start" : 119,
                    "end" : 127,
                    "text" : "Snowden"
                },
                {
                    "start" : 128,
                    "end" : 138,
                    "text" : "TFFistifa"
                }
            ]
        }
    },

但我只想保留主题标签的 text 元素。我试图将reducer更改为 values [idx] .hashtag.text values [idx] .hashtag [“text”] ,但没有帮助。

更新 我怀疑我的问题与MapReduce problem类似,但我不知道修复我的问题

3 个答案:

答案 0 :(得分:1)

您也可以考虑使用聚合框架,它可以产生如下所示的结果。管道看起来与此类似:

{$project: { 
    userid: "$userid", 
    "hashtags": "$hashtags.text",  
    date: { 
        year: { $year: "$createdAt" }, 
        month: { $month: "$createdAt"}, 
        day: {$dayOfMonth: "$createdAt"} }}},
{$unwind: "$hashtags" },
{ $group: { _id : {
    date: "$date", 
    userid: "$userid"}, 
    hashtags: { $addToSet:"$hashtags" }
}} )

可能会产生如下结果:

[
    {
            "_id" : {
                    "date" : {
                            "year" : 2013,
                            "month" : 8,
                            "day" : 4
                    },
                    "userid" : NumberLong(362337301)
            },
            "hashtags" : [
                    "tagger",
                    "stackoverflow",
                    "twitter"
            ]
    },  /* more */

汇总框架管道的简要说明:

  1. 使用$project,只抓取通过管道其余部分重要的字段。在此之前,如果有一个特定的日期或范围,使用$match将是一个很好的步骤,可以有效地过滤一些结果)。请注意,createdAt字段已拆分为相应的部分,以便稍后在分组时忽略时间。投影发生后,示例中的新字段将被称为date。这里,哈希标记已简化为text属性,名称重用为"hashtags"
  2. 接下来,由于"hashtags"此时是一个数组(例如:['tagger', 'stackoverflow', 'twitter']),管道会为"hashtag"数组中的每个元素创建一个新文档。
  3. 最后,分组管道运算符使用useriddate的组合作为分组,并将所有唯一哈希标记添加到名为"hashtags"的字段中。
  4. 作为拆分日期的替代方法,您还可以将createdAt字段视为字符串,并在管道中使用此字段来删除时间:

    date: {$substr: ["$createdAt",0, 10]  }
    

    它会产生类似的东西:

    2013-07-02
    

    修改

    正如您所指出的,从聚合输出的文档中目前有16MB的限制。虽然计划在2.6版本的MongoDB中更改,但您也可以获得MapReduce。鉴于MapReduce不一定适用于此类工作,因此结果可能不一定是您想要的结果。

    map = function() {
        var format = this.createdAt.getFullYear() + '/' 
        + (this.createdAt.getMonth()+1) + '/' + this.createdAt.getDate();
        var key = {userid:this.userid, date:format}; 
        var hashtags = this.hashtags || [];
        for(var i=0, l=hashtags.length; i < l; i++) {
            emit(key, hashtags[i].text); 
        }    
    };
    
    reduce = function(key, values){ 
        values = values || [];
        var tag;
        var tags = {};
        for(var i=0, l=values.length; i<l ; i++) {
            tag = values[i] || "";
            if (tag.length > 0) {
                tags[tag] = "";
            }
        };
        values = [];
        for(var t in tags) {
            values.push(t);
        }
        return values.join(',');
    };
    

    它不会发出数组,而是发出map中的每个哈希标记。 reduce使用简单的关联数组消除重复,然后返回带有所有哈希标记的连接字符串。 MongoDB不支持通过reduce函数返回结果数组(想法是reduce应该提供一个结果,而不是结果数组)。

    结果:

    {
            "_id" : {
                    "userid" : NumberLong(262317302),
                    "date" : "2013/7/2"
            },
            "value" : "Wisconsin,Space,Cheese"
    }
    

    如果您不需要经常这样做,您也可以在MongoDB控制台中编写一个shell脚本,将哈希标记提取到新的集合中。然后,只需在需要时运行它。

答案 1 :(得分:0)

这是我如何设法产生与上述答案相同的结果。只是为了呈现另一个解决方

map = function(){
    var day = this.createdAt.getFullYear() + '/' + (this.createdAt.getMonth()+1) + '/' + this.createdAt.getDate();
    var key = {userid:this.userid, date:day}; 
    var values = {hashtags:[]}; 
    for (var idx=0;idx<this.hashtags.length;idx++){ 
         values.hashtags.push(this.hashtags[idx].text);

        } 
    emit(key,values);
    };


reduce = function(key,values){
    hashtag_list = {hashtags: []} ; 
    for(var i in values) {
    hashtag_list.hashtags= values[i].hashtags.concat(hashtag_list.hashtags); 
    }
    return hashtag_list;
    }

答案 2 :(得分:-1)

尝试:

值[IDX]的.text

hashtag不是对象的属性,但是text是。