MongoDB聚合(重點)

一、取得集合的數據量

範例:統計students表中的數據量

db.students.count();

範例:模糊查詢

db.students.count({"name":/xie/i});

在進行數據查詢的時候,不設置條件永遠要比設置條件的查詢快很多,也就是說在之前的代碼編寫裏面不管是查詢全部還是模糊查詢,實際上最終都是使用的模糊查詢一種(只是沒有設置關鍵字)。

二、消除重複數據(此功能意義不大)

範例:查詢所有name的信息

本次的操作沒有直接的函數支持,只能夠利用runCommand()函數。

db.runCommand({"distinct":"students1","key":"name"});

此時實現了對於name數據的重複值的篩選。

三、group操作

使用“group”操作可以實現數據的分組操作,在MongoDB裏會將集合一句指定的key的不同進行分組操作,並且每一個組都會產生一個處理的文檔結果。

範例:查詢所有年齡大於等於19歲的學生信息,並且按照年齡分組

db.runCommand({"group":{"ns":"students","key":{"age":true},"initial":{"count":0},"condition":{"age":{"$gte":19}},"$reduce":function(doc,prev){

prev.count++;//表示數量加一

}

}});

以上的操作代碼就屬於一種MapReduce

四、MapReduce

MapReduce是整個大數據的精髓所在(實際中別用),所謂的MapReduce就是分位兩步處理數據:

Map:將數據分別取出;

Reduce:負責數據的最後的處理;

可是要想在MongoDB裏實現MapReduce處理,那麼複雜度是相當高的。

範例:建立一組僱員數據

db.emps.insert({"name":"張三","age":30,"sex":"男","job":"CLERK","salary":1000});

db.emps.insert({"name":"李四","age":28,"sex":"女","job":"CLERK","salary":5000});

db.emps.insert({"name":"王五","age":26,"sex":"男","job":"MANAGER","salary":6000});

db.emps.insert({"name":"趙六","age":32,"sex":"女","job":"MANAGER","salary":7000});

db.emps.insert({"name":"孫七","age":31,"sex":"男","job":"CLERK","salary":2000});

db.emps.insert({"name":"王八","age":35,"sex":"女","job":"PRESIDENT","salary":9000});

使用MapReduce操作最終會將處理結果保存在一個單獨的集合裏面,而最終處理效果如下

範例:按照職位分組,取得每個職位的人名

第一步:編寫分組的定義

var jobMapFun=function(){

emit(this.job,this.name);//按照job分組,取出name

};

//結果是:{key:"CLERK",values:[姓名,姓名,...]}

//第二步:編寫reduce操作

var jobReduceFun=function(key,values){

return {"job":key,"names":values};

};

//第三步:針對於MapReduce處理完成的數據實際上也可以執行一個最後處理。

var jobFinalizeFun=function(key,values){

        if(key=="PRESIDENT"){

           return{"job":key,"names":values,"info":"公司的老大"};

         }

           return{"job":key,"names":values};

}

//第四步:進行操作的整合

db.runCommand({

"mapreduce":"emps",

"map":jobMapFun,

"reduce":jobReduceFun,

"out":"t_job_emp",

"finalize":jobFinalizeFun

});

 

現在執行上面三步之後,所有的處理結果都保存在了t_job_emp集合中了。

db.t_job.emp.find().pretty();

範例:統計出各性別的人數、平均工資、最低工資、僱員姓名

var sexMapFun=function(){

        //定義好了分組的條件,以及每個集合要取出的內容

        emit(this.sex,{"ccount":1,"csal":this.salary,"cmax":this.salary,"cmin":this.salary,"cname":this.name});

};

 

var sexReduceFun=function(key,values){

            var total=0;//統計

            var sum=0;//計算總工資

            var max=values[0].cmax;//假設第一個數據是最高工資

            var min=values[0].cmin;//假設第一個數據是最低工資

            var names=new Array();//定義數組內容

            for(var x in values){//表示循環取出裏面的數據

                total+=values[x].ccount;//人數增加

                sum+=values[x].csal;//循環取出所有的工資,並且累加

                if(max<values[x].cmax){//不是最高工資

                      max=values[x].cmax;

                }

                if(min<values[x].cmin){//不是最低工資

                      min=values[x].cmin;

                }

                 names[x]=values[x].cname;//保存姓名

             }

            var avg=(sum/total).toFixed(2);//設置2位小數

            //返回數據的處理結果

            return {"count":total,"avg":avg,"sum" :sum,"max":max,"min":min,"names":names};

};

db.runCommand({

"mapreduce":"emps",

"map":sexMapFun,

"reduce":sexReduceFun,

"out": "t_sex_emp"});

結果如下:

雖然提供有最強悍的MapReduce支持,但是從現實的開發來將,真的不可能使用起來。

五、聚合框架(核心

Mapreduce功能強大,但是它的複雜度和功能一樣強大,那麼很多時候我們需要MapReduce的功能,可是又不想把代碼寫的太複雜,所以從Mongo2.下版本之後開始引入了聚合框架並且提供了聚合函數:aggregate()

1,&group

group主要進行分組操作

範例:實現聚合查詢的功能--求出每個職位的僱員人數

db .emps.aggregate([{"$group":{"_id":"$job","job_count":{"$sum":1}}}]);

這樣的操作更加符合傳統的group by子句的操作使用。

範例:求出每個職位的總工資

db .emps.aggregate([{"$group":{"_id":"$job","job_sal":{"$sum":"$salary"}}}]);

在整個聚合框架裏面如果要引用每行的數據使用"$字段名稱"。

範例:計算出每個職位的平均工資

db .emps.aggregate([{"$group":{

"_id":"$job",

"job_sal":{"$sum":"$salary"},

"job_avg":{"$avg":"$salary"}

}}]);

範例:求出最高與最低工資

db .emps.aggregate([{"$group":{"_id":"$job","max_sal":{"$max":"$salary"},"min_sal":{"$min":"$salary"}}}]);

範例:計算出每個職位的工資數據(數組顯示)

db.emps.aggregate([{"$group":{

"_id":"$job",

"sal_data":{"$push":"$salary"}}}]);

範例:求出每個職位的人員

db.emps.aggregate([{"$group":{

"_id":"$job",

"sal_data":{"$push":"$name"}}}]);

使用“$push“的確可以將數據變爲數組進行保存,但是有一個問題也出現了,重複的內容也會進行保存,那麼在MongoDB裏面提供了取消重複的設置

範例:求出每個職位的人員(取消重複數據)

db.emps.aggregate([{"$group":{

"_id":"$job",

"sal_data":{"$addToSet":"$name"}}}]);

默認情況下是將所有的數據都保存進去了,但是現在只希望可以保留第一個或者最後一個

範例:保存第一個內容(無序)

db.emps.aggregate([{"$group":{

"_id":"$job",

"sal_data":{"$first":"$name"}}}]);

範例:保存最後一個內容(無序)

db.emps.aggregate([{"$group":{

"_id":"$job",

"sal_data":{"$last":"$name"}}}]);

雖然可以方便的實現分組處理,但是有一點需要注意,所有的分組數據都是無序的,並且都是在內存中完成,所以不可能支持大數據量。

2,&project

可以利用“$project"來控制數據列的顯示規則,那麼可以執行的規則如下:

|-普通列({成員:1|true}):表示要顯示的內容;

|-"_id"列({"_id":0|false}):表示“_id“列是否顯示;

|-條件過濾列({成員:表達式})滿足表達式之後的數據可以進行顯示。

範例:只顯示name、job列,不顯示“_id”列

db.emps.aggregate([{"$project":{"_id":0,"name":1,"job":1}}]);

 此時,只有設置進去的列纔可以被顯示出來,而其它的列不能夠被顯示出來。實際上這就是屬於數據庫的投影機制。

實際上在進行數據投影的過程裏面也支持四則運算:加法(“$add")、減法(“$subtract)、乘法(“$multiply”)、除法(“$divide“)、求模($mod)。

範例:起一個別名

db.emps.aggregate([{"$project":{"_id":0,"name":1,"職位":"$job"}}]);

範例:四則運算(求年薪)

db.emps.aggregate([{"$project":{"_id":0,"name":1,"job":1,"salary":{"年薪":{"$multiply":["$salary",12]}}}}]);

除了四則運算之外,也支持如下的各種運算符:

關係運算:大小比較(“$cmp")、等於(”$eq“)、大於(”$gt“)、大於等於(“$gte”)、小於(“$lt”)、小於等於(“$lte”)、不等於(“$ne”)、判斷NULL(“$ifNull”),這些返回值都是布爾數據。

邏輯運算:與(“$and”)、或(“$or”)、非(“$not”);

字符串操作:連接(“$concat”)、截取(“$substr”)、轉小寫(“$toLower”)、轉大寫(“$tpUpper”)、不區分大小寫比較(“$strcasecmp”)。

範例:找出所有工資大於等於2000的僱員姓名、年齡、工資

db.emps.aggregate([{"$project":{"_id":0,"name":1,"job":1,"工資":"$salary","salary":{"$gte":["$salary",2000]}}}]);

範例:查詢職位是manager的信息

MongoDB中的數據是區分大小寫的。

db.emps.aggregate([{"$project":{"_id":0,"name":1,"job":1,"職位":"$job","job":{"$eq":["$job","MANAGER"]}}}]);

範例:查詢職位是manager的信息(轉化成大寫查詢)

db.emps.aggregate([{"$project":{"_id":0,"name":1,"job":1,"職位":"$job","job":{"$eq":["$job",{"$toUpper":"manager"}]}}}]);

範例:使用字符串截取

db.emps.aggregate([{"$project":{"_id":0,"name":1,"job":1,"職位":"$job","job":{"前三位":{"$substr":["$job",0,3]}}}}]);

3,$sort

使用“$sort”可以實現排序,設置1表示升序,設置-1表示降序。

範例:實現排序

db.emps.aggregate([{"$sort":{"age":-1,"salary":1}}]);

4,分頁處理:$limit、$skip

"$limit":負責數據的取出個數

"$skip":數據的跨過個數

範例:使用”$limit“設置取出的個數

db.emps.aggregate([

{"$project":{"_id":0,"name":1,"salary":1,"job":1}},

{"$limit":2}

]);

範例:使用”$limit“設置取出的個數,跨過3行

db.emps.aggregate([

{"$project":{"_id":0,"name":1,"salary":1,"job":1}},

{"$skip":3},

{"$limit":2}

]);

5,$unwind

在查詢數據的時候經常回返回數組信息,但是數組並不方便信息的瀏覽,所以提供有”$unwind“可以將數組數據變爲獨立的字符串內容。

範例:添加一些信息

db.depts.insert({"title":"技術部","bus":["研發","生產","培訓"]});

db.depts.insert({"title":"財務部","bus":["工資","稅收"]});

範例:將信息進行轉化

db.depts.aggregate([

{"$project":{"_id":0,"title":true,"bus":true}},

{"$unwind":"$bus"}]);

6,$geoNear(瞭解)

使用”$geoNear“可以得到附近的座標點

範例:準備測試數據

db.shop.drop();

db.shop.insert({loc:[10,10]});

db.shop.insert({loc:[11,10]});

db.shop.insert({loc:[10,11]});

db.shop.insert({loc:[12,15]});

db.shop.insert({loc:[16,17]});

db.shop.insert({loc:[90,90]});

db.shop.insert({loc:[120,130]});

db.shop.ensureIndex({"loc":"2d"});//必須加索引,下面的查詢命令纔可以執行

範例:設置查詢

db.shop.aggregate([

{"$geoNear":{

"near":[11,12],

"distanceField":"loc",

"maxDistance":1,

"num":2,

"spherical":true}}]);

地理信息的檢索必須存在有索引的支持,所以使用最原始的地理信息比較方便,這裏getNear作爲了解

7,$out

"$out":利用此操作可以將查詢結果輸出到指定的集合裏面。

範例:將投影的結果輸出到集合裏(_id會重新編號)

db.emps.aggregate([

{"$project":{"_id":0,"name":1,"salary":1,"job":1}},

{"$out":"emp_infos"}

]);

 這類的操作就相當於實現了最早的數據表的複製操作。

六、深入操作

1,固定集合

所謂的固定集合指的是規定集合大小,如果要保存的內容已經超過了集合的長度,那麼會採用LRU的算法(最近最少使用的原則),將最早的數據移除,從而保存新的數據。

默認情況下一個集合可以使用createCollection()函數創建,或者使用增加數據後自動創建,但是如果要想使用固定的集合,就必須明確的創建一個空集合。

範例:創建一個空集合(固定集合)

db.createCollection("depts",{"capped":true,"size":1024,"max":5});

capped爲true表示一個固定集合,而”size:1024“指的是集合所佔的空間容量(字節),max等於5表示最多隻能夠有5條記錄。

如果超過5條再插入,最早插入的會消失,實際上跟緩存機制的非常相似的,例如百度裏的關鍵詞(熱門詞),這些詞都是會被不斷替換的。

2,GridFS

在MongoDB裏面支持大數據的存儲(如圖片、音樂等)

3,用戶管理

在MongoDB裏面默認情況下只要是進行連接都可以不使用用戶名和密碼,因爲要想讓其起作用,則必須具備以下條件:

條件一:服務器啓動的時候打開授權認證;

條件二:需要配置用戶名和密碼。

但是需要明確的是,如果要想配置用戶名和密碼一定是針對於一個數據庫的,例如現在要創建的mldn,數據庫的用戶,那麼必須先切換到mldn數據庫上。

use mldn;

範例:執行用戶的創建(hello、java)

任何的用戶都必須具備有一個自己的操作角色,對於角色最基礎的角色:read、readWrite。

db.createUser({

"user":"hello",

"pwd":"java",

"roles":[{"role":"readWrite","db":"mldn"}]});

表示已經成功創建了hello用戶,那麼要想讓用戶名起作用,必須以授權的方式打開MongoDB的服務,修改MongoDB的啓動文件。

noauth改爲auth

這時候在使用集合的時候出現錯誤

使用密碼登陸

mongo localhost:34560/mldn -u hello -p java

範例:修改密碼

db.changeUserPassword("hello","happy");

如果要修改密碼,那麼就請關閉授權登陸

 

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