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函數時並沒有對結構進行嚴謹的判斷。只是簡單的進行疊加等操作。

我也是剛開始學習,數據都是造的,包括問題和需求,在實際項目中遇到的問題可能要比這複雜的多的多,以後在實際項目中遇到什麼問題再慢慢學習吧。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章