【问题标题】:mongodb mapreduce exclude nested fieldmongodb mapreduce 排除嵌套字段
【发布时间】:2014-02-10 15:50:47
【问题描述】:

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

> 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 = 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 = 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.textvalues[idx].hashtag["text"] 没有帮助。

更新: 我怀疑我的问题类似于MapReduce problem,但我不知道如何解决我的问题

【问题讨论】:

    标签: mongodb mapreduce


    【解决方案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。在这里,哈希标签被简化为只有文本属性,名称被重用为"hashtags"
    2. 接下来,由于此时"hashtags" 是一个数组(例如:['tagger', 'stackoverflow', 'twitter'],管道会为"hashtag" 数组中的每个元素创建一个新文档。
    3. 最后,分组管道操作符使用useriddate的组合作为分组器,并将所有唯一的哈希标签添加到名为"hashtags"的字段中。

    作为拆分日期的替代方法,您还可以将 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 脚本,将哈希标签提取到一个新集合中。然后,在需要时运行它。

    【讨论】:

    • 感谢您的回答和很好的解释。结果还可以,但我遇到了“异常:聚合结果超过最大文档大小(16MB)”的问题,似乎 map reduce 是解决方案。你知道如何用 mapreduce 得到同样的结果吗?
    • 添加了一个 Map-Reduce 示例。您会发现它可能会受到限制,因为 map-reduce 并非旨在执行此类工作。
    • 再次感谢。我还设法修复了我的代码并生成了与聚合相同的结果。你也可以看看那个并给我反馈。这是我的第一个 mongo MR 代码,您的意见很有价值。我发布了我的代码
    【解决方案2】:

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

    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;
        }
    

    【讨论】:

      【解决方案3】:

      试试:

      值[idx].text

      标签不是对象的属性,但文本是。

      【讨论】:

        猜你喜欢
        • 2015-02-25
        • 2016-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-13
        • 1970-01-01
        • 2016-03-24
        相关资源
        最近更新 更多