MongoDB中通过MapReduce实现合计Sum功能及返回格式不一致问题分析

建立下述测试数据,通过MapReduce统计每个班级学生数及成绩和。

技术分享

代码如下:

public string SumStudentScore()
{
    var collection = _dataBase.GetCollection("StudentInfo");

    //按照班级(Class)进行分组统计,并将每条记录的记录数(1)和成绩(this.Score)作为Reduce参数
    string mapFunction = @"function(){
                                            emit(this.Class,{Count:1,Score:this.Score});
                                        };";

    //注意此处Values是分组后的一到多条记录
    string reduceFunction = @"function(Class,Values){
                                            var reduced = {SumCount:0,SumScore:0};
                                                    
                                            Values.forEach(function(val){
                                                reduced.SumCount += val.Count;
                                                reduced.SumScore += val.Score;
                                            });
                                            return reduced;
                                        }";

    string outputInfo = string.Empty;
    var result = collection.MapReduce(mapFunction, reduceFunction);

    foreach (var item in result.GetResults())
    {
        outputInfo += item.ToString() + Environment.NewLine;
    }

    return outputInfo;
}

执行结果如下:

{ "_id" : "11班", "value" : { "SumCount" : 2.0, "SumScore" : 9.0 } }
{ "_id" : "12班", "value" : { "SumCount" : 2.0, "SumScore" : 8.0 } }
{ "_id" : "13班", "value" : { "Count" : 1.0, "Score" : 5.0 } }

可见统计结果没有问题,但是第三个分组13班的返回格式明显不同于前两条,经查阅资料为误用MapReduce所致,MapReduce使用有如下要求:

Reduce方法的Value参数必须与返回结果一致。

再分析上述代码:

Reduce的Values参数是由Map方法emit而来,因此参数格式为:{Count:Number,Score:Number},而Reduce的返回参数格式为:{SumCount:Number,SumScore:Number},格式并不一致,因此导致了上述问题。

修改上述代码,将emit结果与Reduce返回格式保持一致:

public string SumStudentScore()
{
    var collection = _dataBase.GetCollection("StudentInfo");

    //按照班级(Class)进行分组统计,并将每条记录的记录数(1)和成绩(this.Score)作为Reduce参数
    string mapFunction = @"function(){
                                            emit(this.Class,{SumCount:1,SumScore:this.Score});
                                        };";

    //注意此处Values是分组后的一到多条记录
    string reduceFunction = @"function(Class,Values){
                                            var reduced = {SumCount:0,SumScore:0};
                                                    
                                            Values.forEach(function(val){
                                                reduced.SumCount += val.SumCount;
                                                reduced.SumScore += val.SumScore;
                                            });
                                            return reduced;
                                        }";

    string outputInfo = string.Empty;
    var result = collection.MapReduce(mapFunction, reduceFunction);

    foreach (var item in result.GetResults())
    {
        outputInfo += item.ToString() + Environment.NewLine;
    }

    return outputInfo;
}

输出结果正确:

{ "_id" : "11班", "value" : { "SumCount" : 2.0, "SumScore" : 9.0 } }
{ "_id" : "12班", "value" : { "SumCount" : 2.0, "SumScore" : 8.0 } }
{ "_id" : "13班", "value" : { "SumCount" : 1.0, "SumScore" : 5.0 } }

参考资料:

关于《来,我给你们看一段神奇的mongodb的mapreduce操作!》的解释

db.collection.mapReduce() — MongoDB Manual 2.6.7

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。