mongodb学习之五:聚合之group复习

之前参照书中的例子进行学习group,以为有点懂了,想自己找个联系做做看,发现,原来并没有懂。今天再慢慢复习一下看看group的具体用法。

数据使用java循环插入3600条数据,结构比较统一,结构如下:

{
    "_id" : ObjectId("5343a44474d0946a30cd26b1"),
    "name" : "赵小强",
    "sex" : "男",
    "age" : 39,
    "date" : "2010-9-14",
    "salary" : 8000,
    "dep" : "测试部"
}

很简单,保存的是部门员工的信息。


1、求每个部门的平均工资

这个问题肯定得用group,因为要分组按照部门输出

db.runCommand({
	group:{
		ns:"emp",
		key:{"dep":true},
		initial:{avgSalary:0,sum:0,count:0},
		$reduce:function(doc,prev){
			prev.sum=prev.sum+doc.salary;
			prev.count++;
		},
		finalize:function(prev){
			prev.avgSalary = prev.sum/prev.count;
			delete prev.sum;
			delete prev.count;
		}
	}
});
输出结果:
/* 0 */
{
    "retval" : [ 
        {
            "dep" : "软件一部",
            "avgSalary" : 7509.505703422054
        }, 
        {
            "dep" : "工程实施部",
            "avgSalary" : 7214.285714285715
        }, 
        {
            "dep" : "软件二部",
            "avgSalary" : 7477.358490566037
        }, 
        {
            "dep" : "测试部",
            "avgSalary" : 7564.96062992126
        }, 
        {
            "dep" : "信息管理部",
            "avgSalary" : 7334
        }, 
        {
            "dep" : "运维部",
            "avgSalary" : 7293.50104821803
        }, 
        {
            "dep" : "人力资源部",
            "avgSalary" : 7275.142314990512
        }
    ],
    "count" : 3600,
    "keys" : 7,
    "ok" : 1
}

其实我一直迷惑的是叠加函数$reduce的编写。之前书中的例子中都使用了for循环,把我迷惑了。我没看清的是书中例子的for是循环的文档中某个数组类型的键

之所以叫叠加函数是因为,$reduce函数本身就是循环集合中的文档,不需要自己再写。自己需要写的只是每次循环你需要做的操作即可。

上面的例子中,初始文档定义了三个键:{avgSalary:0,sum:0,count:0},其实这个初始文档只是叠加的时候的初始文档,与最后输出的结果文档没太大的关系。

在$reduce函数中,我将salary进行叠加并计数,当叠加完成,还有使用最后的完成函数进行最后的过滤操作,将不必要的sum和count两个键删除。

2、统计每个部门的工资分布情况,部门总共多少人,以及每个部门的平均工资。按部门输出

db.runCommand({
	group:{
		ns:"emp",
		key:{"dep":true},
		initial:{salaryCount:{},sum:0,count:0,avgSalary:0},
		$reduce:function(doc,prev){
			if(doc.salary in prev.salaryCount){
				prev.salaryCount[doc.salary]++;
			}else{
				prev.salaryCount[doc.salary]=1;
			}
			prev.sum=prev.sum+doc.salary;
			prev.count++;
		},
		finalize:function(prev){
			prev.avgSalary = prev.sum/prev.count;
			delete prev.sum;
		}
	}
});

结果:
/* 0 */
{
    "retval" : [ 
        {
            "dep" : "软件一部",
            "salaryCount" : {
                "3000" : 57,
                "4000" : 56,
                "5000" : 55,
                "6000" : 52,
                "7000" : 55,
                "8000" : 59,
                "9000" : 84,
                "10000" : 53,
                "15000" : 55
            },
            "count" : 526,
            "avgSalary" : 7509.505703422054
        }, 
        {
            "dep" : "工程实施部",
            "salaryCount" : {
                "3000" : 60,
                "4000" : 55,
                "5000" : 57,
                "6000" : 69,
                "7000" : 75,
                "8000" : 54,
                "9000" : 63,
                "10000" : 54,
                "15000" : 45
            },
            "count" : 532,
            "avgSalary" : 7214.285714285715
        }, 
        {
            "dep" : "软件二部",
            "salaryCount" : {
                "3000" : 50,
                "4000" : 59,
                "5000" : 61,
                "6000" : 62,
                "7000" : 50,
                "8000" : 68,
                "9000" : 64,
                "10000" : 62,
                "15000" : 54
            },
            "count" : 530,
            "avgSalary" : 7477.358490566037
        }, 
        {
            "dep" : "测试部",
            "salaryCount" : {
                "3000" : 45,
                "4000" : 54,
                "5000" : 53,
                "6000" : 67,
                "7000" : 56,
                "8000" : 58,
                "9000" : 61,
                "10000" : 58,
                "15000" : 56
            },
            "count" : 508,
            "avgSalary" : 7564.96062992126
        }, 
        {
            "dep" : "信息管理部",
            "salaryCount" : {
                "3000" : 61,
                "4000" : 57,
                "5000" : 55,
                "6000" : 53,
                "7000" : 55,
                "8000" : 62,
                "9000" : 53,
                "10000" : 51,
                "15000" : 53
            },
            "count" : 500,
            "avgSalary" : 7334
        }, 
        {
            "dep" : "运维部",
            "salaryCount" : {
                "3000" : 51,
                "4000" : 56,
                "5000" : 50,
                "6000" : 53,
                "7000" : 57,
                "8000" : 54,
                "9000" : 52,
                "10000" : 65,
                "15000" : 39
            },
            "count" : 477,
            "avgSalary" : 7293.50104821803
        }, 
        {
            "dep" : "人力资源部",
            "salaryCount" : {
                "3000" : 61,
                "4000" : 65,
                "5000" : 63,
                "6000" : 57,
                "7000" : 49,
                "8000" : 66,
                "9000" : 52,
                "10000" : 63,
                "15000" : 51
            },
            "count" : 527,
            "avgSalary" : 7275.142314990512
        }
    ],
    "count" : 3600,
    "keys" : 7,
    "ok" : 1
}

3、接上面第二个例子,我只想看软件一部的统计信息,怎么办?

这就用到了可选的cond参数,这个参数就是用来过滤查询条件的

db.runCommand({
	group:{
		ns:"emp",
		key:{"dep":true},
		initial:{salaryCount:{},sum:0,count:0,avgSalary:0},
        cond:{"dep":"软件一部"},
		$reduce:function(doc,prev){
			if(doc.salary in prev.salaryCount){
				prev.salaryCount[doc.salary]++;
			}else{
				prev.salaryCount[doc.salary]=1;
			}
			prev.sum=prev.sum+doc.salary;
			prev.count++;
		},
		finalize:function(prev){
			prev.avgSalary = prev.sum/prev.count;
			delete prev.sum;
		}
	}
});
结果:
/* 0 */
{
    "retval" : [ 
        {
            "dep" : "软件一部",
            "salaryCount" : {
                "3000" : 57,
                "4000" : 56,
                "5000" : 55,
                "6000" : 52,
                "7000" : 55,
                "8000" : 59,
                "9000" : 84,
                "10000" : 53,
                "15000" : 55
            },
            "count" : 526,
            "avgSalary" : 7509.505703422054
        }
    ],
    "count" : 526,
    "keys" : 1,
    "ok" : 1
}

4、统计每年公司新进员工数量,以年的形式输出

db.runCommand({
	group:{
		ns:"emp",
		$keyf:function(doc){
			return {year:doc.date.substr(0,4)};
		},
		initial:{empCount:0},
		$reduce:function(doc,prev){
			prev.empCount++;
		},
		finalize:function(prev){}
	}
});
结果:
/* 0 */
{
    "retval" : [ 
        {
            "year" : "2009",
            "empCount" : 761
        }, 
        {
            "year" : "2008",
            "empCount" : 736
        }, 
        {
            "year" : "2012",
            "empCount" : 682
        }, 
        {
            "year" : "2011",
            "empCount" : 699
        }, 
        {
            "year" : "2010",
            "empCount" : 722
        }
    ],
    "count" : 3600,
    "keys" : 5,
    "ok" : 1
}

上面这个例子演示了$keyf的使用。如果想自定义分组条件,则使用$keyf函数自定义。函数的参数是原文档,函数操作完后,返回的是自定义键的文档。

上面的例子中,我将文档中的date键的值进行截取,只留年份,返回是一个以year为键,以截取后的值为值的文档。

走完上面4个例子,对mongodb的group了解也慢慢明朗起来。以上的数据是我使用java循环插入的,结构比较整齐,因此在写$reduce函数时并没有对结构进行严谨的判断。只是简单的进行叠加等操作。

我也是刚开始学习,数据都是造的,包括问题和需求,在实际项目中遇到的问题可能要比这复杂的多的多,以后在实际项目中遇到什么问题再慢慢学习吧。



你可能感兴趣的:(mongodb,mongodb)