一、取得集合的數據量
範例:統計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");
如果要修改密碼,那麼就請關閉授權登陸