MapReduce计数有问题

时间:2013-05-16 13:06:34

标签: mongodb mapreduce

我遇到了问题,我在mongodb中有数据,如下所示:

{"miejscowosci_str":"OneCity", "wojewodztwo":"FirstRegionName", "ZIP-Code" : "...", ...}
{"miejscowosci_str":"TwoCity", "wojewodztwo":"FirstRegionName", "ZIP-Code" : "...", ...}
{"miejscowosci_str":"ThreeCity", "wojewodztwo":"SecondRegionName", "ZIP-Code" : "...", ...}
{"miejscowosci_str":"FourCity", "wojewodztwo":"SecondRegionName", "ZIP-Code" : "...", ...}

等等 我想要的是列出所有地区(wojewodztwo)并计算每个地区的平均邮政编码数量,我知道如何计算地区的所有邮政编码:

var map = function() {
    emit(this.wojewodztwo,1);
};
var reduce = function(key, val) {
    var count = 0;
    for(i in val) {
        count += val[i];
    }
    return count;
};
db.kodypocztowe.mapReduce(
    map,
    reduce,
    { out : "result" }
);

但我不知道如何统计城市数量(miejscowosci_str),因此我可以根据同一地区的城市数量划分区域内的邮政编码数量。 一个城市可以有多个邮政编码。

你有什么想法吗?

1 个答案:

答案 0 :(得分:2)

我在这里做了几个假设:

  1. 城市可以有多个邮政编码
  2. 邮政编码是唯一的
  3. 你不是想得到M101P第5周问题的答案!
  4. 为什么不在地图阶段建立城市/邮政对象列表,然后将其减少为地图中的拉链列表和唯一城市,而不是一次性计算城市数量相。然后,您可以使用最终确定阶段来计算平均值。

    注意:如果数据集很大,您可能需要考虑使用aggregation framework,这会在map / reduce示例

    之后显示
    db.kodypocztowe.drop();
    db.result.drop();
    
    db.kodypocztowe.insert([
        {"miejscowosci_str":"OneCity", "wojewodztwo":"FirstRegionName", "ZIP-Code" : "1"},
        {"miejscowosci_str":"TwoCity", "wojewodztwo":"FirstRegionName", "ZIP-Code" : "2"},
        {"miejscowosci_str":"ThreeCity", "wojewodztwo":"SecondRegionName", "ZIP-Code" : "3"},
        {"miejscowosci_str":"FourCity", "wojewodztwo":"SecondRegionName", "ZIP-Code" : "4"},
        {"miejscowosci_str":"FourCity", "wojewodztwo":"SecondRegionName", "ZIP-Code" : "5"},
    ]);
    
    // map the data to { region : [{citiy : name , zip : code }] } 
    // Note : a city can be in multiple zips but zips are assumed to be unique
    var map = function() {
        emit(this.wojewodztwo, {city:this.miejscowosci_str, zip:this['ZIP-Code']});
    };
    
    // 
    // convert the data to :
    //
    //    {region : {cities: [], zips : []}}
    //
    // note : always add zips
    // note : only add cities if they are not already there
    //
    var reduce = function(key, val) {
        var res = {zips:[], cities:[]}
        for(i in val) {
            var city = val[i].city;
            res.zips.push(val[i].zip);
            if(res.cities.indexOf(city) == -1) {
                res.cities.push(city);
            }
        }
        return res;
    };
    
    // 
    // finalize the data to get the average number of zips / region
    var finalize = function(key, res) {
        res.average  = res.zips.length / res.cities.length;
        delete res.cities;
        delete res.zips;
        return res;
    }
    
    print("==============");
    print(" map/reduce")
    print("==============");
    
    db.kodypocztowe.mapReduce(
        map,
        reduce,
        { out : "result" , finalize:finalize}
    );
    db.result.find().pretty()
    
    
    print("==============");
    print(" aggregation")
    print("==============");
    
    db.kodypocztowe.aggregate( [
        // get the number of zips / [region,city]
        { "$group" :
          {
              _id : {"region" : "$wojewodztwo", city : "$miejscowosci_str"},
              zips:{$sum:1}
          }
        },
        // get the number of cities per region and sum the number of zips
        { "$group" :
          {
              _id : "$_id.region" ,
              cities:{$sum:1},
              zips:{$sum:"$zips"},
          }
        },
        // project the data into the same format that map/reduce generated
        { "$project" :
          {
              "value.average":{$divide: ["$zips","$cities"]}
          }
        }
    ]);
    

    我希望有所帮助。