十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
一、簡(jiǎn)介
MongoDB的聚合框架,主要用來(lái)對(duì)集合中的文檔進(jìn)行變換和組合,從而對(duì)數(shù)據(jù)進(jìn)行分析以加以利用。
聚合框架的基本思路是:
采用多個(gè)構(gòu)件來(lái)創(chuàng)建一個(gè)管道,用于對(duì)一連串的文檔進(jìn)行處理。
這些構(gòu)件包括:
篩選(filtering)、投影(projecting)、分組(grouping)、排序(sorting)、限制(limiting)和跳過(guò)(skipping)。
使用聚合框架的方式:
db.集合.aggregate(構(gòu)件1,構(gòu)件2…)
注意:由于聚合的結(jié)果要返回到客戶端,因此聚合結(jié)果必須限制在16M以內(nèi),這是MongoDB支持的大響應(yīng)消息的大小。
二、使用例子
2.1、準(zhǔn)備樣例數(shù)據(jù)
for(var i=0;i<100;i++){ for(var j=0;j<4;j++){ db.scores.insert({"studentId":"s"+i,"course":"課程"+j,"score":Math.random()*100}); } }2.2、找出考80分以上的課程門(mén)數(shù)最多的3個(gè)學(xué)生
步驟:
1:找到所有考了80分以上的學(xué)生,不區(qū)分課程
> db.scores.aggregate({"$match":{"score":{$gte:80}}});2:將每個(gè)學(xué)生的名字投影出來(lái)
> db.scores.aggregate({"$match":{"score":{$gte:80}}},{$project:{"studentId":1}});3:對(duì)學(xué)生的名字排序,某個(gè)學(xué)生的名字出現(xiàn)一次,就給他加1
> db.scores.aggregate({"$match":{"score":{$gte:80}}},{$project:{"studentId":1}},{$group:{"_id":"$studentId","count":{$sum:1}}});4:對(duì)結(jié)果集按照count進(jìn)行降序排列
> db.scores.aggregate({"$match":{"score":{$gte:80}}},{$project:{"studentId":1}},{$group:{"_id":"$studentId","count":{$sum:1}}},{"$sort":{"count":-1}});5:返回前面的3條數(shù)據(jù)
db.scores.aggregate({"$match":{"score":{$gte:80}}},{$project:{"studentId":1}},{$group:{"_id":"$studentId","count":{$sum:1}}},{"$sort":{"count":-1}},{"$limit":3});三、管道操作符
每個(gè)操作符接受一系列的文檔,對(duì)這些文檔做相應(yīng)的處理,然后把轉(zhuǎn)換后的文檔作為結(jié)果傳遞給下一個(gè)操作符。最后一個(gè)操作符會(huì)將結(jié)果返回。
不同的管道操作符,可以按照任意順序,任意個(gè)數(shù)組合在一起使用。
3.1、篩選命令$match
用于對(duì)文檔集合進(jìn)行篩選,里面可以使用所有常規(guī)的查詢操作符。通常會(huì)放置在管道最前面的位置,理由如下:
1:快速將不需要的文檔過(guò)濾,減少后續(xù)操作的數(shù)據(jù)量
2:在投影和分組之前做篩選,查詢可以使用索引
3.2、投影命令$project
用來(lái)從文檔中提取字段,可以指定包含和排除字段,也可以重命名字段。比如要將studentId改為sid,如下:
db.scores.aggregate({"$project":{"sid":"$studentId"}})
管道操作符還可以使用表達(dá)式,以滿足更復(fù)雜的需求。
管道操作符$project的數(shù)學(xué)表達(dá)式:
比如給成績(jī)集體加20分,如下:
> db.scores.aggregate({"$project":{"studentId":1,"newScore":{$add:["$score",20]}}});支持的操作符和相應(yīng)語(yǔ)法:
1:$add : [expr1[,expr2,…exprn]]
2:$subtract:[expr1,expr2]
3:$multiply:[expr1[,expr2,…exprn]]
4:$divice:[expr1,expr2]
5:$mod:[expr1,expr2]
管道操作符$project的日期表達(dá)式:
聚合框架包含了一些用于提取日期信息的表達(dá)式,如下:
$year、$month、$week、$dayOfMonth、$dayOfWeek、$dayOfYear、$hour、$minute、$second 。
注意:這些只能操作日期型的字段,不能操作數(shù)據(jù),使用示例:
{"$project":{"opeDay":{"$dayOfMonth":"$recoredTime"}}}
管道操作符$project的字符串表達(dá)式:
1:$substr : [expr,開(kāi)始位置,要取的字節(jié)個(gè)數(shù)]
2:$concat:[expr1[,expr2,…exprn]]
3:$toLower:expr
4:$toUpper:expr
例如:{"$project":{"sid":{$concat:["$studentId","cc"]}}}
管道操作符$project的邏輯表達(dá)式:
1:$cmp:[expr1,expr2] :比較兩個(gè)表達(dá)式,0表示相等,正數(shù)前面的大,負(fù)數(shù)后面的大
2:$strcasecmp:[string1,string2] :比較兩個(gè)字符串,區(qū)分大小寫(xiě),只對(duì)由羅馬字符組成的字符串有效
3:$eq、$ne、$gt、$gte、$lt、$lte :[expr1,expr2]
4:$and、$or、$not
5:$cond:[booleanExpr,trueExpr,falseExpr]:如果boolean表達(dá)式為true,返回true表達(dá)式,否則返回false表達(dá)式
6:$ifNull:[expr,otherExpr]:如果expr為null,返回otherExpr,否則返回expr
例如:db.scores.aggregate({"$project":{"newScore":{$cmp:["$studentId","sss"]}}})
3.3、分組命令$group
用來(lái)將文檔依據(jù)特定字段的不同值進(jìn)行分組。選定了分組字段過(guò)后,就可以把這些字段傳遞給$group函數(shù)的“_id”字段了。例如:
db.scores.aggregate({“$group”:{“_id”:“$studentId”}});
或者是
db.scores.aggregate({"$group":{"_id":{"sid":"$studentId","score":"$score"}}});
$group支持的操作符:
1:$sum:value :對(duì)于每個(gè)文檔,將value與計(jì)算結(jié)果相加
2:$avg:value :返回每個(gè)分組的平均值
3:$max:expr :返回分組內(nèi)的大值
4:$min:expr :返回分組內(nèi)的最小值
5:$first:expr :返回分組的第一個(gè)值,忽略其他的值,一般只有排序后,明確知道數(shù)據(jù)順序的時(shí)候,這個(gè)操作才有意義
6:$last:expr :與上面一個(gè)相反,返回分組的最后一個(gè)值
7:$addToSet:expr :如果當(dāng)前數(shù)組中不包含expr,那就將它加入到數(shù)組中
8:$push:expr:把expr加入到數(shù)組中
3.4、拆分命令$unwind
用來(lái)把數(shù)組中的每個(gè)值拆分成為單獨(dú)的文檔。
3.5、排序命令$sort
可以根據(jù)任何字段進(jìn)行排序,與普通查詢中的語(yǔ)法相同。如果要對(duì)大量的文檔進(jìn)行排序,強(qiáng)烈建議在管道的第一個(gè)階段進(jìn)行排序,這時(shí)可以使用索引。
3.6、常見(jiàn)的聚合函數(shù)
1:count:用于返回集合中文檔的數(shù)量
2:distinct:找出給定鍵的所有不同值,使用時(shí)必須指定集合和鍵,例如:
db.runCommand({"distinct":"users","key":"userId"});
四、MapReduce
在MongoDB的聚合框架中,還可以使用MapReduce,它非常強(qiáng)大和靈活,但具有一定的復(fù)雜性,專門(mén)用于實(shí)現(xiàn)一些復(fù)雜的聚合功能。
MongoDB中的MapReduce使用JavaScript來(lái)作為查詢語(yǔ)言,因此能表達(dá)任意的邏輯,但是它運(yùn)行非常慢,不應(yīng)該用在實(shí)時(shí)的數(shù)據(jù)分析中。
4.1、MapReduce的HelloWorld
實(shí)現(xiàn)的功能,找出集合中所有的鍵,并統(tǒng)計(jì)每個(gè)鍵出現(xiàn)的次數(shù)。
1:Map函數(shù)使用emit函數(shù)來(lái)返回要處理的值,示例如下:
var map = function(){ for(var key in this){ emit(key,{count:1}); } }this表示對(duì)當(dāng)前文檔的引用。
2:reduce函數(shù)需要處理Map階段或者是前一個(gè)reduce的數(shù)據(jù),因此reduce返回的文檔必須要能作為reduce的第二個(gè)參數(shù)的一個(gè)元素,示例如下:
var reduce = function(key,emits){ var total = 0; for(var i in emits){ total += emits[i].count; } return {"count":total}; };3:運(yùn)行MapReduce,示例如下:
> var mr =db.runCommand({"mapreduce":"scores","map":map,"reduce":reduce,"out":"mrout"});說(shuō)明:scores是集合名,map是map函數(shù),reduce是reduce函數(shù),mrout是輸出的變量名
4:查詢最終的結(jié)果,示例如下:
db.mrout.find();還可以改變一下,比如統(tǒng)計(jì)studentId中值,以及每個(gè)值出現(xiàn)的次數(shù),就可以如下操作:
1:修改map函數(shù),示例如下:
var map = function(){ emit(this.studentId,{count:1}); };2:reduce函數(shù)不用改
3:重新執(zhí)行
db.runCommand({"mapreduce":"scores","map":map,"reduce":reduce,"out":"mrout"});4:查看最終結(jié)果
db.mrout.find();4.2、更多MapReduce可選的鍵
1:finalize:function :可以將reduce的結(jié)果發(fā)送到finalize,這是整個(gè)處理的最后一步
2:keeptemp:boolean :是否在連接關(guān)閉的時(shí)候,保存臨時(shí)結(jié)果集合
3:query:document :在發(fā)送給map前對(duì)文檔進(jìn)行過(guò)濾
4:sort:document :在發(fā)送給map前對(duì)文檔進(jìn)行排序
5:limit:integer :發(fā)往map函數(shù)的文檔數(shù)量上限
6:scope:document :可以在javascript中使用的變量
7:verbose:boolean :是否記錄詳細(xì)的服務(wù)器日志
示例:
var query = {"studentId":{"$lt":"s2"}} var sort = {"studentId":1}; var finalize = function(key,value){ return {"mykey":key,"myV":value}; }; var mr =db.runCommand({"mapreduce":"scores","map":map,"reduce":reduce,"out":"mrout","query":query,"sort":sort,"limit":2,"finalize":finalize});五、聚合命令group
用來(lái)對(duì)集合進(jìn)行分組,分組過(guò)后,再對(duì)每一個(gè)分組內(nèi)的文檔進(jìn)行聚合。
比如要對(duì)studentId進(jìn)行分組,找到每個(gè)學(xué)生高的分?jǐn)?shù),可以如下步驟進(jìn)行:
db.runCommand({"group":{ "ns":"scores", "key":{"studentId":1}, "initial":{"score":0}, "$reduce":function(doc,prev){ if(doc.score > prev.score){ prev.score = doc.score; } } }});ns:指定要分組的集合
key:指定分組的鍵
initial:每一組的reduce函數(shù)調(diào)用的時(shí)候,在開(kāi)頭的時(shí)候調(diào)用一次,以做初始化
$reduce:在每組中的每個(gè)文檔上執(zhí)行,系統(tǒng)會(huì)自動(dòng)傳入兩個(gè)參數(shù),doc是當(dāng)前處理的文檔,prev是本組前一次執(zhí)行的結(jié)果文檔
你還可以在group的時(shí)候添加條件,就是加入condition,示例如:
db.runCommand({"group":{ "ns":"scores", "key":{"studentId":1}, "initial":{"score":0}, "$reduce":function(doc,prev){ if(doc.score > prev.score){ prev.score = doc.score; } } ,"condition":{"studentId":{$lt:"s2"}} }});同樣可以使用finalizer來(lái)對(duì)reduce的結(jié)果進(jìn)行最后的處理,比如要求每個(gè)學(xué)生的平均分,就可以先按照studentId分組,求出一個(gè)總的分?jǐn)?shù)來(lái),然后在finalizer里面,求平均分:
db.runCommand({"group":{ "ns":"scores", "key":{"studentId":1}, "initial":{"total":0}, "$reduce":function(doc,prev){ prev.total += doc.score; }, "condition":{"studentId":{"$lt":"s2"}}, "finalize":function(prev){ prev.avg = prev.total/3; } }});注意:finalize是只在每組結(jié)果返回給用戶前調(diào)用一次,也就是每組結(jié)果只調(diào)用一次
對(duì)于分組的key較為復(fù)雜的時(shí)候,還可以采用函數(shù)來(lái)做為鍵,比如讓鍵不區(qū)分大小下,就可以如下定義:
db.runCommand({"group":{ "ns":"scores", $keyf:function(doc){ return {studentId:doc.studentId.toLowerCase()}; }, "initial":{"total":0}, "$reduce":function(doc,prev){ prev.total += doc.score; }, "condition":{"$or":[{"studentId":{"$lt":"s2"}},{"studentId":"S0"}]}, "finalize":function(prev){ prev.avg = prev.total/3; } }});注意:要使用$keyf來(lái)定義函數(shù)作為鍵,另外一定要返回對(duì)象的格式
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。