一、基本概念解釋
二、MongoDB 數據類型
下表為MongoDB中常用的幾種數據類型。
ObjectId?類似唯一主鍵,可以很快的去生成和排序,包含 12 bytes,含義是:
前 4 個字節表示創建?unix?時間戳,格林尼治時間?UTC?時間,比北京時間早了 8 個小時
接下來的 3 個字節是機器標識碼
緊接的兩個字節由進程 id 組成 PID
最后三個字節是隨機數
MongoDB 中存儲的文檔必須有一個 _id 鍵。這個鍵的值可以是任何類型的,默認是個 ObjectId 對象
由于 ObjectId 中保存了創建的時間戳,所以你不需要為你的文檔保存時間戳字段,你可以通過 getTimestamp 函數來獲取文檔的創建時間:
> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2018-07-05T07:21:10Z")
ObjectId 轉為字符串
> newObject.str
5a1919e63df83ce79df8b38f
三、Mongodb入門命令
3.1、基本查看命令
show dbs 查看所有的數據庫
use databaseName 使用某個數據庫? 例如:use edu
show tables/collections 查看當前庫下的所有collection
3.2、庫和集合的操作
查看當前所處的數據庫
db
3.3、在mongodb中,庫是隱式創建的,你可以use 一個不存在的庫,然后在該庫下創建collection,即可創建庫。
db.dropDatabase(); 刪除database,?把當前所用的庫給刪除了?,?即使里面有數據也會刪除
db.createCollection(‘collectionName’), 創建collection,collection也是允許隱式創建的
db.collectionName.insert(document); 在集合(表)中插入具體數據的時候會自動創建
db.collectionName.drop() , 刪除collection
舉例如下:
use edu #創建數據庫:edu
db.createCollection('video') #創建集合(表):video
db.video.insert({play_url:'http://www.sohu.com/a.mp4',title:'戰狼2',area:'中國'})? ?#插入一條數據
db.video.drop()? ?#刪除集合video
四、Mongodb基本增刪改查
4.1、增加數據
mongodb存儲的是文檔,文檔是json格式的對象,我們向數據庫存儲數據的時候可以使用insert方法,數據格式要以js對象格式進行存儲:
語法:?db.collectionName.insert(document);
db.createCollection('student')
db.student.insert({name:'zhangsan',age:'20'})? 向當前students表里插入數據
4.1.1、增加單篇文檔
語法:?db.student.insert({title:"nice day"});
4.1.2、增加單個文檔,并且指定_ID
語法:?db.student.insert({_id:8,age:78,name:"lisi"});
_id 是我們在插入數據的時候,mongodb自動給文檔添加的一個屬性,如果我們不需要系統分配_id ,可以在添加數據的時候手動設置,覆蓋原有_id ,雖然_id 的類型可以自由指定,但是在同一個集合當中必須唯一,如果插入重復的值,系統會拋出異常.
這個_id 的名稱是固定的,它可以是Mongodb支持的任何數據類型,默認是ObjectId,在關系型數據庫中,主鍵通常是數值型的,并且可以設置自增,而Mongodb的主鍵,原生不支持自增主鍵。
4.1.3、增加多個文檔
db.student.insert( [{time:'friday',value:'mongodb'},{_id:1,gender:'male',name:'QQ'}])
可以以數組的方式,一次性向集合插入多個數據;同時應該注意的是,由于mongodb采用的是?J?a?v?a?S?c?r?ip?t?S?he?l?l,所以我們可以根據js特性,將文檔作為值賦給變量然后進行操作:
j = {name : 'isi'};
t = {name : 'wangwu'};
db.student.insert([j,t]);
4.1.4、SAVE和INSERT的區別
save和insert都可以進行數據的插入和增加,但是也有一些異同:
對于已存在數據?{?_id:1, "name":"n1" },再次進行插入操作時,
a、insert({_id:1, "name" :"n2"}) 會報主鍵重復的錯誤提示;
b、save({ _id:1, " name ":"n2"})?會把?n1?修改為?n2?。
相同點:?若新增的數據中沒有主鍵時,會增加一條記錄。
不同點:?主鍵_id已存在時?:insert 會報錯,save會修改當前_id的數據
即:insert有則報錯,無則插入;save有則修改,無則插入
4.2、查詢操作
4.2.1、FIND()
無條件的普通查詢方式很簡單,可以直接使用
db.collectionName.find(?)?;?一次可以查出指定集合中的所有數據
db.student.find();
按照條件進行查詢操作
語法: db.collection.find(?查詢表達式?,?查詢的列?)?;
例1: db.student.find({},{name:1})??//查詢student集合中的name屬性?(_id屬性默認總是查出來)
例2: db.student.find({},{name:1, _id:0}) //查詢student集合中的name屬性,且不查詢_id屬性此處的0表示的是false,不查詢
例3: db.student.find({age:20},{name:1,_id:0?}?)?;//查詢student集合中age屬性值為20的name屬性
4.2.2、FINDONE()
findOne()和find()函數一樣,只是findOne()返回的是查詢結果中的第一條數據,或者返回null.
4.3、刪除操作
語法: db.collectionName.remove(?查詢表達式?,?選項?);
選項是指?{?justOne:true/false},是否只刪一行, 默認為false?注意
1: 查詢表達式依然是個json對象
2: 查詢表達式匹配的行,將被刪掉.
3: 如果查詢表達式為空對象{},collections中的所有文檔將被刪掉.
例1: db.student.remove({name:'n1'});//刪除stu表中name屬性值為'n1'的文檔
例2: db.student.remove({gender:'m'},true);//刪除stu表中gender屬性為m的文檔,只刪除1行.
4.4、修改操作
語法: db.collection.update(?查詢表達式?,?新值?,?選項?);
*改哪幾行? --- 查詢表達式
*改成什么樣? -- 新值 或 賦值表達式
*操作選項 ----- 可選參數
upsert:如果要更新的那條記錄沒有找到,是否插入一條新紀錄,默認為false
multi? :是否更新滿足條件的多條的記錄,默認為false
multi :是否更新滿足條件的多條的記錄,false:只更新第一條,true:更新多條,默認為false
例:db.student.update({name:'QQ'},{name:'MSN'}); //是指選中student表中,name值為QQ的文檔,并把其文檔值改為{name:"MSN"},
結果:文檔中的其他列也不見了,改后只有_id和name列了。即是新文檔直接覆蓋了舊文檔,而不是修改。
4.4.1、修改操作中的關鍵字
如果是想修改文檔的某列,可以用$set關鍵字
例:db.student.update(query,{$set:{name:’QQ’}})
修改時的賦值表達式
$set 修改某列的值
$unset 刪除某個列
$inc 增長某個列
$rename 重新命名某列
$setOnInsert 當upsert為true時,并且發生了insert操作時,可以補充的字段.
$INC實例
按照指定的步長增長某個列;
db.student.insert({"uid":"201203","type":"1",size:10})
db.student.update({"uid" :"201203"},{"$inc":{"size"? :? 2}})
$UNSET實例
db.student.find({_id:8})
db.student.update({_id:8},{$unset:{age:'sss'}})
4.5 查詢表達式
我們無論在修改刪除還是查詢的過程中,都需要傳入查詢表達式對目標數據進行查詢,表達式有很多種
1:? 最簡單的查詢表達式
{filed:value}? ,是指查詢field列的值為value的文檔
2:? $ne:!=
{field:{$ne:value}}?
db.stu.find({age:{$ne:16}}) 作用--查age列的值 不等于16的文檔
3:$gt:大于
$lt:小于
$gte:大于或等于
$lte:小于或等于
4:? $in:[]? ? 查詢某列的值在范圍內的文檔
db.goods.find({cat_id:{$in:[2,8]}}
5:? $nin:not? in? ? ? 查詢某列不在范圍內的文檔
$nin:[2,3,5]
6:? $exists
語法:? {field:{$exists:1}}
作用:? 查詢出含有field字段的文檔
7:用正則表達式查詢? 以”諾基亞”開頭的商品
例:db.goods.find({goods_name:/諾基亞.*/},{goods_name:1});
五?游標操作
通俗的說,游標不是查詢結果,而是查詢的返回資源,或者接口,通過這個接口,你可以逐條對數據進行讀取;
聲明游標?:
var? cursor? =? db.collectioName.find(query,projection);
cursor.hasNext()? //判斷游標是否已經取到盡頭?
cursor.next()? //取出游標的下1個單元
用while來循環游標
var? mycursor? =? db.bar.find({_id:{$lte:5}})
while(mycursor.hasNext())? { printjson(mycursor.next());}
游標還有一個迭代函數,允許我們自定義回調函數來逐個處理每個單元.
cursor.forEach(回調函數);
var? gettitle? =? function(obj)? {print(obj.goods_name)}
var? cursor? =? db.goods.find();
cursor.forEach(gettitle);
游標在分頁中的應用
比如查到10000行,跳過100頁,取10行,一般地,我們假設每頁N行, 當前是page頁,?就需要跳過前?(page-1)*N 行, 再取N行.
在mongo中,分頁是用skip(), limit()函數來實現的
//查詢結果中,跳過前9995行
var? mycursor? =? db.bar.find().skip(9995);
//查詢第901頁,每頁10條
則是? var? mytcursor? =? db.bar.find().skip(9000).limit(10);
六?group分組
mongodb支持聚合運算;
在goods表中插入數據
db.goods.insert([
{'_id':3,'cat_id':6,'price':29},
{'_id':4,'cat_id':7,'price':30},
{'_id':5,'cat_id':6,'price':31},
{'_id':6,'cat_id':7,'price':32},
{'_id':7,'cat_id':7,'price':28},
])
如果我們所處的是mysql數據庫,我們可以這樣查詢每個類下面的商品平均價格
select? avg(price)? from? goods? group? by? cat_id;
但如果在mongodb下,我們如何查詢分組內的平均值呢? 我們需要使用mongodb的聚合運算?https://docs.mongodb.com/manual/aggregation/
db.goods.aggregate([
{$match:{}},
{$group:{_id:"$cat_id",avg:{$avg:'$price'}}}
]);
其中,$match表示匹配的條件,$group表示分組的條件,$avg表示求平均值. 當然,指令還有很多,我們還可以使用limit,sort等操作
db.goods.aggregate([
{$match:{}},
{$group:{_id:"$cat_id",avg:{$avg:'$price'}}},
{$limit:1}
]);
按照價格降序排列
db.goods.aggregate([
{$match:{}},
{$sort:{price:-1}}
]);
七?MapReduce
7.1、MapReduce原理
隨著大數據興起,MapReduce的概念也越來越火,通常的概念是用于大規模數據集(1TB)的并行運算,實際上就是傳統關系型數據庫的group概念的延伸.
MapReduce之所以能夠流行,是因為數據的大,當數據過大的時候,單個服務器無法承載,facebook,微軟等等的數據中心都是分布在世界各地的,?我們所需?要的數據很可能分布在不同的服務器甚至世界各地.在這時候,我們就無法使用group操作了.
MapReduce通俗的講,最大的優點就是可以支持分布式的group
而MapReduce的操作即分為map和reduce兩步;
map ---> 映 射
reduce ---> 減少,規約,回歸
7.2、MapReduce統計價格
//按照cat_id? 分配? price,把price數據映射到一個數組上 var? map? =? function(){
emit(this.cat_id? ,? this.price)
}
//將映射好的數組進行操作
var? reduce? =? function(cat_id,number){ return? Array.avg(number)
}
//將統計的數據映射到res表當中db.goods.mapReduce(map,reduce,{out:'res'})
接下來我們使用mapReduce功能實現地震數據的統計
7.3、下載并導入地震信息
在國家地震科學數據共享中心下載過去一年的地震數據?http://data.earthquake.cn/sjfw/index.html?PAGEID=datasourcelist&dt=40280d0453e414e40153e44861dd0003
將數據保存為csv格式,導入到mongodb數據庫中,使用mongoimport
-d : 指明導入文件存放在哪個數據庫
-c : 指明導入文件存放在哪個集合
--type:指明要導入的文件格式。
--headerline:指明不導入第一行,csv格式的文件第一行為列名。
--file:指明要導入的文件路徑。
./bin/mongoimport? -d? test? -c? dz? --type? csv? --file? /usr/local/src/dz.csv? --headerline
7.4、按照經緯度統計數據
我們規約的時候按照經緯度的5*5方格進行分組,如果在此方格內存在地震,則地震+1
var? map? =? function(){
var? jd? =? parseInt(this.jd/5)*5; var? wd? =? parseInt(this.wd/5)*5; var? area? =? jd? +? ':'? +? wd;
emit(area,1);//如果該區域有地震,則統計為1
}
var? reduce? =? function(area,nums){ return? Array.sum(nums);
}
db.dz.mapReduce(map,reduce,{out:'dzrs'});
成功獲取區間范圍內的地震次數,此時我們要將數據導出為json,做成熱力圖;
7.5 熱力圖
使?用?百?度?地?圖?開?放?平?臺?的?熱?力?圖?api?http://lbsyun.baidu.com/index.php?title=jspopular
填入密鑰,生成熱力圖
7.6 展示地震數據
轉化地震數據為規定的json格式
var? course? =? db.dzrs.find();
var? row;
course.forEach(function(obj){
? ? row? =? obj._id.split(':');
? ? db.reli.insert({lng:parseInt(row[1])+2.5,lat:parseInt(row[0])+2.5,count:obj.value})
})
導出json
./bin/mongoexport? -d? test? -c? reli? -o? /usr/local/src/reli.json
將json數據放入熱力圖當中并配置熱力圖相關選項.