# 挑戰全棧 MongoDB基礎視頻教程 (共21集) #
[TOC]
上套課程講了Koa2,挑戰全棧就必須和數據庫打交道,個人覺的作為一個前端開發人員,操作MongoDB還是比較愜意的。因為它是一種NoSql數據庫,不用寫SQL語句,而且里邊的用法都是JSON對象的形式。所以我們在Koa2的課程中間查了MongoDB的課程,等會操作數據庫后,我們再返回去學Koa2的數據庫連接和操作,最后技術胖會帶著大家作個小實戰。
這里學的是最新的MongoDB版本,現在是3.6版本。
說明:課程每周更新2-3節,每節課12分鐘左右,因為我覺的我每次聽課只能保持12-15分鐘的注意力(一般都是早上4點起床,開始更新,比別人笨就要比別人努力),以視頻配合圖文的形式進行更新.
技術胖不是專職講師,還是一個一線程序員,所以課程中難免有錯誤和紕漏,希望小伙伴們在留言中指出,讓我們共同進步。課程參照了《MongoDB權威指南 第2版》。
## 第01節:認識和安裝MongoDB ##
都知道MongoDB是非關系型數據庫,要了解非關系型數據庫就必須先了解關系型數據庫,關系數據庫,是建立在關系模型基礎上的數據庫。比較有名氣的關系型數據庫,比如Oracle、DB2、MSSQL、Mysql。
非關系數據庫和關系型數據庫的區別是什么?
實質:非關系型數據庫的實質:非關系型數據庫產品是傳統關系型數據庫的功能閹割版,通過減少用不到或很少用的功能,來大幅度提高產品性能。
價格:目前的非關系型數據庫基本都是免費的,而比較有名氣的關系型數據庫都是收費的,比如:Oracle、DB2、MSSQL。MySql雖然是免費的,但是處理大型數據還是要提前作很多工作的。
功能:實際開發中,很多業務需求,其實并不需要完整的關系型數據庫功能,非關系型數據庫的功能就足夠使用了。這種情況下,使用性能更高、成本更低的非關系型數據庫當然是更明智的選擇。
了解關系型數據庫和非關系型數據庫的區別后,需要有一點的取舍,比較復雜和大型的項目不建議使用非關系型數據庫,但是如果你想作個博客,CMS系統這類業務邏輯不復雜的程序,MongoDB是完全可以勝任的。
MongoDB簡介:
MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫。目的是為WEB應用提供擴展的高性能的數據存儲解決方案。MongoDB是一個介于關系型數據庫和非關系型數據庫之間的產品,是非關系型數據庫當中功能最豐富,最像關系數據庫的。他支持的數據結構非常松散,是類似json的bson格式,因此可以存儲比較復雜的數據類型。Mongo最大的特點是他支持的查詢語言非常強大,其語法有點類似于面向對象的查詢語言,幾乎可以實現類似關系數據庫單表查詢的絕大部分功能,而且還支持對數據建立索引。
安裝MongoDB:
MongoDB的安裝分為Windows下的安裝和Mac的安裝(Liunx和Mac趨同)。這里我們只講解windows的安裝(別問我為什么,你們都知道的,我沒Mac電腦)。
安裝步驟:
去官網下載MongoDB,[https://www.mongodb.com/](https://www.mongodb.com/),在網站中找到Download按鈕。下載會有點忙,國外的服務器,你懂的。
下載后進行安裝,安裝沒什么難度,但是對于新手建議選擇默認安裝,而不選擇自己配置。等我們完全熟悉后再定制式配置。
安裝時如果有安全軟件,會報一些攔截,一律允許就好,不允許會安裝失敗的。
安裝完成后,需要配置“環境變量”,目的是再命令行中直接使用,而不需要輸入很長的路徑了。(此步驟觀看視頻)
運行MongoDB服務端:
安裝好MongoDB數據庫后,我們需要啟用服務端才能使用。啟用服務的命令是:`Mongod`。
打開命令行:先打開運行(快捷鍵win+R),然后輸入cmd后回車,就可以打開命令行工具。
執行mongod:在命令中直接輸入`mongod`,但是你會發現服務并沒有啟動,報了一個exception,服務停止了。
新建文件夾:出現上邊的錯誤,是因為我們沒有簡歷Mongodb需要的文件夾,一般是安裝盤的根目錄,建立`data/db`,這兩個文件夾。
運行mongod:這時候服務就可以開啟了,鏈接默認端口是27017。
鏈接服務:
服務端開啟后,我們可以使用命令行來鏈接服務端,鏈接命令是mongo。重新打開一個命令行工具,然后輸入mongo命令。也許你在鏈接時會報幾個warning(警告),我們先不用管它,以后我們再慢慢學習。
查看存在數據庫命令:`show dbs`
查看數據庫版本命令:`db.version()`
如果這兩條命令都可以正常的顯示出結果,證明我們的MongoDB數據庫已經安裝成功了,以后我們就可以愉快的開始學習了。想學習的小伙伴學完后自己動手安裝一下吧,只有自己動手才能真正學會。
## 第02節:Mongo基本命令-1 ##
接下來的幾節直接上手Mongo命令,先讓小伙伴對MongoDB的基本命令會使用,算是快速上手。快速上手后,我們再用以后的課程把這些命令細化和升級,形成一套完整的MongoDB知識體系。小伙伴們不要著急,讓我們開始學習吧。
mongo shell 如果以前接觸過數據庫一定知道每個數據庫都有自己獨特的命令,MSSQL和MYsql用的都是Sql命令,MongoDB的操作命令就是前端最熟悉的JavaScript命令。看到這里作為前端你一定會小激動一下,這對前端來說那是極好的。
先來一個常用的賦值和輸出命令,熟悉一下。(操作前你需要打開Mongo服務器和鏈接到服務器-也就是我們上節課講的mongod命令和mongo命令)在命令行中輸入以下代碼。
```
var x='Hello World'
print(x)
```
需要注意的是這里的輸出不再使用`console.log(‘巴拉巴拉’)`,而是使用`print(‘巴拉巴拉’)`,這個稍有區別。
還可以定義函數:
```
function jspang(){
return 'JSPang';
}
```
學到這里,我們用的都是JavaScript的語法,這些我們可以信手拈來,非常容易。
MongoDB的存儲結構 以前我們的關系型數據庫的數據結構都是頂層是庫,庫下面是表,表下面是數據。但是MongoDB有所不同,庫下面是集合,集合下面是文件,可以看下面這張圖進行了解一下。
pic
在學習中我們可以對比記憶,這樣才能更好的了解這些名詞,其實數據表就是集合,數據行就是文件,當然這只是為了記憶,實質還是有區別的。
基礎Shell命令:
了解存儲結構后,就可以開始學習我們的基礎Shell命令了,因為這些命令比較基礎,我會以列表形式展現,具體使用方法可以到視頻中進行觀看。
* `show dbs` :顯示已有數據庫,如果你剛安裝好,會默認有local、admin(config),這是MongoDB的默認數據庫,我們在新建庫時是不允許起這些名稱的。
* `use admin`: 進入數據,也可以理解成為使用數據庫。成功會顯示:switched to db admin。
* `show collections`: 顯示數據庫中的集合(關系型中叫表,我們要逐漸熟悉)。
* `db`:顯示當前位置,也就是你當前使用的數據庫名稱,這個命令算是最常用的,因為你在作任何操作的時候都要先查看一下自己所在的庫,以免造成操作錯誤。
總結:這節課的學習已經讓我們進入了MongoDB的世界,你會感覺使用起來還是比較簡單的,都是些基本的命令,敲入回車就可以使用,但是大家還是要進行多聯系。這節課的重點是要轉變以前數據庫存儲結構的思想,掌握MongoDB的存儲結構。
## 第03節:Mongo基本命令-2 ##
這節課繼續學習MongoDB的基礎命令,上節課我們學了一些最簡單的查看命令,比如查看數據庫,查看集合,顯示當前位置。這節課我們開始真實的操作一下數據,學會基本的數據增刪改查。直接看視頻開始學習吧。
數據操作基礎命令:
* `use db(建立數據庫)`:use不僅可以進入一個數據庫,如果你敲入的庫不存在,它還可以幫你建立一個庫。但是在沒有集合前,它還是默認為空。
* `db.集合.insert( )`:新建數據集合和插入文件(數據),當集合沒有時,這時候就可以新建一個集合,并向里邊插入數據。Demo:db.user.insert({“name”:”jspang”})
*` db.集合.find( )`:查詢所有數據,這條命令會列出集合下的所有數據,可以看到MongoDB是自動給我們加入了索引值的。Demo:db.user.find()
* `db.集合.findOne( )`:查詢第一個文件數據,這里需要注意的,所有MongoDB的組合單詞都使用首字母小寫的駝峰式寫法。
* `db.集合.update({查詢},{修改})`:修改文件數據,第一個是查詢條件,第二個是要修改成的值。這里注意的是可以多加文件數據項的,比如下面的例子。
```
db.jspang.update({"name":"jspang"},{"name":"jspang","age":"32"})
```
* `db.集合.remove(條件)`:刪除文件數據,注意的是要跟一個條件。
```
Demo:db.user.remove({“name”:”jspang”})
```
* `db.集合.drop( )`:刪除整個集合,這個在實際工作中一定要謹慎使用,如果是程序,一定要二次確認。
* `db.dropDatabase( )`:刪除整個數據庫,在刪除庫時,一定要先進入數據庫,然后再刪除。實際工作中這個基本不用,實際工作可定需要保留數據和痕跡的。
總結:這節課都是對數據或者庫的操作,這些雖然簡單,但是你每天都要用到,所以建議反復練習,增強記憶。
## 第04節:用js文件寫mongo命令 ##
在命令行中寫mongo的命令(shell)實在是太麻煩了(反正技術胖是無法忍受windows系統的這種命令行的),急需一種更好的方式來進行工作,這節課我們就學一下用JS文件來寫shell命令和執行。在JS里寫mongo的Shell命令大部分是相同的,只有小部分不一樣。
把命令寫入JS中: 現在模擬一個用戶登錄日志表的信息,用JS進行編寫。新在一個新建的目錄下,比如D:/mongoShell/,新建一個goTask.js文件。文件內容如下:
goTask.js文件
```
var userName="jspang"; //聲明一個登錄名
var timeStamp=Date.parse(new Date()); //聲明登錄時的時間戳
var jsonDdatabase={"loginUnser":userName,"loginTime":timeStamp}; //組成JSON字符串
var db = connect('log'); //鏈接數據庫
db.login.insert(jsonDdatabase); //插入數據
print('[demo]log print success'); //沒有錯誤顯示成功
```
如果上邊這個基礎的代碼你看起來還不是那么容易,那我建議你先停下這門課的學習,去學一下JS的基礎知識,只有基礎打牢了,學習起來才會快速。
執行JS文件
寫好了JS文件,需要執行起來,看一下文件是否存在問題,能否順利的插入數據到MongoDB中。
執行其實很簡單,只要使用mongo xxx.js(xxx就是我們寫的goTask.js文件)。
```
mongo goTask.js
```
然后我們可以在命令行看到已經執行成功,可以到終端中進行查看插入結果。
總結:這節課很好的解決了在終端中寫命令行的難題,雖然大部分Shell和在命令行中寫法一樣,但是也稍有不同,希望小伙伴們可以輕松掌握。
## 第05節:批量插入的正確方法 ##
前4節課,我們都是簡單講解了MongoDB的使用方法,目的是快速入門。從這節課開始,我們要詳細深入的講解MongoDB的各項操作,難度也開始逐漸提高。需要提示如果前邊的課程還沒有聯系熟練,那先練習一下,否則接下來學習會稍有困難。
在操作數據庫時要注意兩個能力:
1. 第一個是快速存儲能力。
2. 第二個是方便迅速查詢能力。
批量插入:
批量數據插入是以數組的方式進行的(如果寫錯,可以3個回車可以切出來)。我們現在命令行中敲入下面的代碼,我們可以看到數據順利插入了。
```
db.test.insert([
{"_id":1},
{"_id":2},
{"_id":3}
])
```
老版本MongoDB(3.2以前的版本基本都需要)是需要在Insert前加一個batch單詞的,如下代碼。
```
db.test.batchInsert([
{"_id":1},
{"_id":2},
{"_id":3}
])
```
注意一次插入不要超過48M,向.zip和大圖片什么的盡量用靜態存儲,MongoDB存儲靜態路徑就好,這也算是一個規則。
批量插入性能測試
剛學了批量插入,那是循環插入快?還是批量插入快那?在一般人的認知里肯定是批量插入更快(其實這毋庸置疑),但我們要拿出極客精神,探個究竟,試著寫一個小Shell,來驗證一下結果。
先寫一個循環插入方法:
```
var startTime = (new Date()).getTime(); //得到開始時間
var db = connect('log'); //鏈接數據庫
//開始循環
for(let i=0;i<1000;i++){
db.test.insert({num:i});
}
var runTime = (new Date()).getTime()-startTime;//計算時間差
print ('This run this is:'+runTime+'ms');//打印出來
```
我測試的時間507ms,這個速度雖然和電腦性能有關,但還是不太理想,1000條數據用了將近半秒,組長會發飆的。
批量插入代碼:
```
var startTime = (new Date()).getTime();
var db = connect('log');
var tempArray = [] //聲明一個數組
for(let i=0;i<1000;i++){ //循環向數組中放入值
tempArray.push({num:i});
}
db.test.insert(tempArray) //批量一次插入
var runTime = (new Date()).getTime()-startTime;
print ('This run this is:'+runTime+'ms');
```
這次用了17ms,性能遠遠超過循環插入。
總結:在工作中一定要照顧數據庫性能,這也是你水平的提現,一個技術會了很簡單,但是要作精通不那么簡單。學完這節,記得在工作中如果在循環插入和批量插入舉起不定,那就選批量插入吧,它會給我們更優的性能體驗。
## 第06節:修改:Update常見錯誤 ##
這節課開始我們要說一說Update的詳細操作,我們先來看一下常見錯誤,我們知道了困難或者說問題在哪里,我們再提出解決方案。這節課我會先演示一些錯誤的Update方法,然后再說正確的方法。希望不要誤導小伙伴。
錯誤1:只update修改項
如果你有過關系型數據庫的經驗,你會很容易犯只修改需要改變的一項,因為在關系型數據庫中就是這樣作的。先來準備一些數據,這些數據模擬了一個軟件開發小組的組成(當然這不能當真)。
```
var workmate1={
name:'JSPang',
age:33,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
SkillTwo:'JavaScript',
SkillThree:'PHP'
},
regeditTime:new Date()
}
var workmate2={
name:'ShengLei',
age:30,
sex:1,
job:'JAVA后端',
skill:{
skillOne:'HTML+CSS',
SkillTwo:'J2EE',
SkillThree:'PPT'
},
regeditTime:new Date()
}
var workmate3={
name:'MinJie',
age:20,
sex:1,
job:'UI設計',
skill:{
skillOne:'PhotoShop',
SkillTwo:'UI',
SkillThree:'Word+Excel+PPT'
},
regeditTime:new Date()
}
var db=connect('company')
var workmateArray=[workmate1,workmate2,workmate3]
db.workmate.insert(workmateArray)
print('[SUCCESS]: The data was inserted successfully.');
```
上面的代碼,我們以文件的形式向數據庫中插入了3條數據。
這時候我突然發現UI職位的性別出現了錯誤,本來人家是個美女,這里缺寫成了男,我們需要修改這條數據,但是經常會這樣寫。
這樣寫的問題是,我們的最后一條數據變成了只有sex:0,其它數據全部丟失了,這肯定不是我們想要的。這是新手在操作數據庫修改時經常犯的一個錯誤,就是只修改變動的數據。
正確修改方法:
可以聲明一個變量,然后把要改變數據的全部信息放入變量,最后執行修改操作。
```
var db=connect('company')
var workmate3={
name:'MinJie',
age:20,
sex:0,
job:'UI設計',
skill:{
skillOne:'PhotoShop',
SkillTwo:'UI',
SkillThree:'Word+Excel+PPT'
},
regeditTime:new Date()
}
db.workmate.update({name:'MinJie'},workmate3)
print('[update]: The data was updated successfully');
```
這時候你需要刪除(db.workmate.drop())表中的數據,因為MinJie這個用戶已經不在數據庫中了,然后重新使用load方法載入插入數據再進行修改。
//執行命令如下:
```
db.workmate.drop()
load('./demo02.js')
load('./demo03.js')
```
現在這種方法才是正確的,數據修改正常了,但是你會發現寫起來非常麻煩,而且特別容易寫錯。下節課我們會介紹update修改器,可以很好的解決這個問題。
## 第07節:修改:初識update修改器 ##
上節課的修改用起來實在是不夠優雅,這是我們一個偉大的前端不能接受的,所以我們要學習新知識update修改器,來解決這個問題。update修改器可以幫助我們快速和簡單的修改數據,讓我們的操作更簡單方便。
$set修改器
用來修改一個指定的鍵值(key),這時候我們要修改上節課的sex和age就非常方便了,只要一句話就可以搞定。
```
dbd .workmate.update({"name":"MinJie"},{"$set":{sex:2,age:21}})
```
修改好后,我們可以用`db.workmate.find()`來進行查看,你會發現數據已經被修改。
修改嵌套內容(內嵌文檔)
比如現在的UI的技能發生了變化,說她不會作PPT而是word作的很好,需要進行修改。這時候你會發現skill數據是內嵌的,這時候我們可以屬性的形式進行修改,skill.skillThree。具體看下面的代碼。
```
db.workmate.update({"name":"MinJie"},{"$set":{"skill.skillThree":'word'}})
```
這樣就可以簡單的修改內嵌文檔了。
$unset用于將key刪除
它的作用其實就是刪除一個key值和鍵。一般女孩子都是不希望看到自己的年齡的,所以要求我們把年齡刪除。這時候我們就可以使用$unset的形式。
```
db.workmate.update({"name":"MinJie"},{$unset:{"age":''}})
```
當你刪除后,想加回來可以直接用set進行添加。
$inc對數字進行計算
它是對value值的修改,但是修改的必須是數字,字符串是不起效果的。我們現在要對MiJie的年齡減去2歲,就可以直接用$inc來操作。
```
db.workmate.update({"name":"MinJie"},{$inc:{"age":-2}})
```
multi選項
現在領導說了,你要把每個人的愛好也加入進來,但是如果你直接寫會只加一個,比如下面這種形式。
```
db.workmate.update({},{$set:{interset:[]}})
```
這時候你用db.workmate.find()查找,你會發現只改變了第一個數據,其他兩條沒有改變。這時候我們想改變就要用到multi選項。
```
db.workmate.update({},{$set:{interset:[]}},{multi:true})
```
這時候每個數據都發生了改變,multi是有ture和false兩個值,true代表全部修改,false代表只修改一個(默認值)
upsert選項
upsert是在找不到值的情況下,直接插入這條數據。比如我們這時候又來了一個新同事xiaoWang,我們這時候修改他的信息,age設置成20歲,但集合中并沒有這條數據。這時候可以使用upsert選項直接添加。
```
db.workmate.update({name:'xiaoWang'},{$set:{age:20}},{upsert:true})
```
upsert也有兩個值:true代表沒有就添加,false代表沒有不添加(默認值)。
總結:這節課的內容非常多,主要學習了update修改器有關的一些東西。一定要課下多練習幾遍,否則很快就會忘記的。
## 第08節:修改:update數組修改器 ##
已經會了一些基礎的修改器,這節課我們主要學習一下數組修改器的操作,當然也可以修改內嵌文檔,也就是對象形式的數據。讓我們努力學習吧。
$push追加數組/內嵌文檔值
$push的功能是追加數組中的值,但我們也經常用它操作內嵌穩文檔,就是{}對象型的值。先看一個追加數組值的方式,比如我們要給小王加上一個愛好(interset)為畫畫(draw):
db.workmate.update({name:'xiaoWang'},{$push:{interest:'draw'}})
當然$push修飾符還可以為內嵌文檔增加值,比如我們現在要給我們的UI,增加一項新的技能skillFour為draw,這時候我們可以操作為:
db.workmate.update({name:'MinJie'},{$push:{"skill.skillFour":'draw'}})
$push修飾符在工作中是最常用的,因為我們的數據一般都會涉及數組和內嵌文檔的操作,一定要掌握。
$ne查找是否存在
它主要的作用是,檢查一個值是否存在,如果不存在再執行操作,存在就不執行,這個很容易弄反,記得我剛學的時候就經常弄反這個修改器的作用,給自己增加了很多坑。
例子:如果xiaoWang的愛好(interest)里沒有palyGame這個值,我們就加入Game這個愛好。
db.workmate.update({name:'xiaoWang',"interest":{$ne:'playGame'}},{$push:{interest:'Game'}})
總結:沒有則修改,有則不修改。
$addToSet 升級版的$ne
它是$ne的升級版本(查找是否存在,不存在就push上去),操作起來更直觀和方便,所以再工作中這個要比$en用的多。
例子:我們現在要查看小王(xiaoWang)興趣(interest)中有沒有閱讀(readBook)這項,沒有則加入讀書(readBook)的興趣.
db.workmate.update({name:"xiaoWang"},{$addToSet:{interest:"readBook"}})
$each 批量追加
它可以傳入一個數組,一次增加多個值進去,相當于批量操作,性能同樣比循環操作要好很多,這個是需要我們注意的,工作中也要先組合成數組,然后用批量的形式進行操作。
例子:我們現在要給xiaoWang,一次加入三個愛好,唱歌(Sing),跳舞(Dance),編碼(Code)。
var newInterset=["Sing","Dance","Code"];
db.workmate.update({name:"xiaoWang"},{$addToSet:{interest:{$each:newInterset}}})
$pop 刪除數組值
$pop只刪除一次,并不是刪除所有數組中的值。而且它有兩個選項,一個是1和-1。
1:從數組末端進行刪除
-1:從數組開端進行刪除
例子:現在要刪除xiaoWang的編碼愛好(code)。
db.workmate.update({name:'xiaoWang'},{$pop:{interest:1}})
數組定位修改
有時候只知道修改數組的第幾位,但并不知道是什么,這時候我們可以使用interest.int 的形式。
例子,比如我們現在要修改xiaoWang的第三個興趣為編碼(Code),注意這里的計數是從0開始的。
db.workmate.update({name:'xiaoWang'},{$set:{"interest.2":"Code"}})
總結:這節課主要講了于數組和內嵌文檔有關的update修改器,內容很多,都需要不斷熟練記憶。當然如果你記不住,你至少記住這個博客網址,因為技術胖把筆記已經給你整理好了。
## 第09節:修改:狀態返回與安全 ##
在操作數據庫時,對數據的修改是需要有足夠的安全措施的,其實在實際工作中,我們用db.collections.update的時候不多,在修改時我們都會用findAndModify,它可以給我們返回來一些必要的參數,讓我們對修改多了很多控制力,控制力的加強也就是對安全的強化能力加強了。
應答式寫入:
在動手學習代碼之前,我們先來了解一個概念:應答式寫入。在以前的文章中,我們的操作都是非應答式寫入,就是在操作完數據庫后,它并沒有給我們任何的回應和返回值,而是我們自己安慰自己寫了一句話(print(‘[update]:The data was updated successfully’);)。這在工作中是不允許的,因為根本不能提現我們修改的結果。
應答式寫入就會給我們直接返回結果(報表),結果里邊的包含項會很多,這樣我們就可以很好的進行程序的控制和安全機制的處理。有點像前端調用后端接口,無論作什么,后端都要給我一些狀態字一樣。
db.runCommand( ):
它是數據庫運行命令的執行器,執行命令首選就要使用它,因為它在Shell和驅動程序間提供了一致的接口。(幾乎操作數據庫的所有操作,都可以使用runCommand來執行)現在我們試著用runCommand來修改數據庫,看看結果和直接用db.collections.update有什么不同。
db.workmate.update({sex:1},{$set:{money:1000}},false,true)
var resultMessage=db.runCommand({getLastError:1})
printjson(resultMessage);
上邊的代碼,我們修改了所有男士的數據,每個人增加了1000元錢(money),然后用db.runCommand()執行,可以看到執行結果在控制臺返回了。
{
"connectionId" : 1,
"updatedExisting" : true,
"n" : 2,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1
}
false:第一句末尾的false是upsert的簡寫,代表沒有此條數據時不增加;
true:true是multi的簡寫,代表修改所有,這兩個我們在前邊課程已經學過。
getLastError:1 :表示返回功能錯誤,這里的參數很多,如果有興趣請自行查找學習,這里不作過多介紹。
printjson:表示以json對象的格式輸出到控制臺。
db.listCommands( ):查看所有的Commad命令,內容很多,本套課程只講解工作中經常使用的內容。
比如我們要查看是否和數據庫鏈接成功了,就可以使用Command命令。
db.runCommand({ping:1})
返回ok:1就代表鏈接正常。
findAndModify:
從名字上就可以看出,findAndModify是查找并修改的意思。配置它可以在修改后給我們返回修改的結果。我們先看下面的代碼:
var myModify={
findAndModify:"workmate",
query:{name:'JSPang'},
update:{$set:{age:18}},
new:true //更新完成,需要查看結果,如果為false不進行查看結果
}
var ResultMessage=db.runCommand(myModify);
printjson(ResultMessage)
findAndModify的性能是沒有直接使用db.collections.update的性能好,但是在實際工作中都是使用它,畢竟要商用的程序安全性還是比較重要的。
findAndModify屬性值:
query:需要查詢的條件/文檔
sort: 進行排序
remove:[boolean]是否刪除查找到的文檔,值填寫true,可以刪除。
new:[boolean]返回更新前的文檔還是更新后的文檔。
fields:需要返回的字段
upsert:沒有這個值是否增加。
總結:這節課講了一些跟安全有關的操作,但這不是全部,我們隨著課程的深入還會繼續學習更多的知識。工作中盡量使用findAndModify來進行更新數據,這樣會更安全和直觀,這點性能的損失是值得的。
## 第10節:查詢:find的不等修飾符 ##
MongoDB的查找操作我們會分幾節課來講,因為內容還是比較多的,而且在開發中查找是應用最多的操作,幾乎每個模塊都會用到,所以查找的部分將是本套課的重中之重。這節課我們先來看看簡單的查詢條件,也了解一下find基礎用法。如果你以前操作過關系型數據庫,比如MySql,你會對>大于),<(小于),=(等于)這些東西很熟悉,但是非關系型數據庫不能直接使用這些符號,稍有區別。
構造數據:
我們需要構造更多的數據到集合中,這樣我們才能很好的講解查詢條件,下面代碼你可以直接復制進行添加。當然你也可以自己隨意加一些數據到集合中,只要方便我們學習就可以了。
var workmate1={
name:'JSPang',
age:33,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
skillThree:'PHP'
},
regeditTime:new Date(),
interest:[]
}
var workmate2={
name:'ShengLei',
age:31,
sex:1,
job:'JAVA后端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'J2EE',
skillThree:'PPT'
},
regeditTime:new Date(),
interest:[]
}
var workmate3={
name:'MinJie',
age:18,
sex:0,
job:'UI',
skill:{
skillOne:'PhotoShop',
skillTwo:'UI',
skillThree:'PPT'
},
regeditTime:new Date(),
interest:[]
}
var workmate4={
name:'XiaoWang',
age:25,
sex:1,
job:'UI',
skill:{
skillOne:'PhotoShop',
skillTwo:'UI',
skillThree:'PPT'
},
regeditTime:new Date(),
interest:[]
}
var workmate5={
name:'LiangPeng',
age:28,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
},
regeditTime:new Date(),
interest:[]
}
var workmate6={
name:'HouFei',
age:25,
sex:0,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
},
regeditTime:new Date(),
interest:[]
}
var workmate7={
name:'LiuYan',
age:35,
sex:0,
job:'美工',
skill:{
skillOne:'PhotoShop',
skillTwo:'CAD',
},
regeditTime:new Date(),
interest:[]
}
var workmate8={
name:'DingLu',
age:20,
sex:0,
job:'美工',
skill:{
skillOne:'PhotoShop',
skillTwo:'CAD',
},
regeditTime:new Date(),
interest:[]
}
var workmate9={
name:'JiaPeng',
age:29,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
skillThree:'PHP'
},
regeditTime:new Date(),
interest:[]
}
var workmate10={
name:'LiJia',
age:26,
sex:0,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
skillThree:'PHP'
},
regeditTime:new Date(),
interest:[]
}
var db=connect('company');
var workmateArray=[workmate1,workmate2,workmate3,workmate4,workmate5,workmate6,workmate7,workmate8,workmate9,workmate10];
db.workmate.insert(workmateArray);
print('[SUCCESS]:The data was inserted successfully');
簡單查找:
比如我們現在要查找數據中技能一會HTML+CSS的所有人。那我們直接進行查找加條件就可以了。
db.workmate.find({"skill.skillOne":"HTML+CSS"})
這時候我們不能使用load來載入了,以后我會給大家講使用的方法,這里先用比較笨的方法,使用粘貼復制的方法執行。
篩選字段
現在返回來的數據項太多,太亂,有時候我們的程序并不需要這么多選項。比如我們只需要姓名和技能就可以了。這時候我們需要寫第二個參數,看下面的代碼。
db.workmate.find(
{"skill.skillOne":"HTML+CSS"},
{name:true,"skill.skillOne":true}
)
你會在終端中看到如下結果:
{ "_id" : ObjectId("5a611350c4e36dee6008987a"), "name" : "JSPang", "skill" : { "skillOne" : "HTML+CSS" } }
{ "_id" : ObjectId("5a611350c4e36dee6008987b"), "name" : "ShengLei", "skill" : { "skillOne" : "HTML+CSS" } }
{ "_id" : ObjectId("5a611350c4e36dee6008987e"), "name" : "LiangPeng", "skill" : { "skillOne" : "HTML+CSS" } }
{ "_id" : ObjectId("5a611350c4e36dee6008987f"), "name" : "HouFei", "skill" : { "skillOne" : "HTML+CSS" } }
{ "_id" : ObjectId("5a611350c4e36dee60089882"), "name" : "JiaPeng", "skill" : { "skillOne" : "HTML+CSS" } }
{ "_id" : ObjectId("5a611350c4e36dee60089883"), "name" : "LiJia", "skill" : { "skillOne" : "HTML+CSS" } }
細心的小伙伴會發現還不夠完美,多了一個ID字段,這個也不是我們想要的,這時候只要把_id:false就可以了。當然這里的false和true,也可以用0和1表示。
db.workmate.find(
{"skill.skillOne":"HTML+CSS"},
{name:true,"skill.skillOne":true,_id:false}
)
這時候你在終端中查看結果,已經是我們想要的了。
{ "name" : "JSPang", "skill" : { "skillOne" : "HTML+CSS" } }
{ "name" : "ShengLei", "skill" : { "skillOne" : "HTML+CSS" } }
{ "name" : "LiangPeng", "skill" : { "skillOne" : "HTML+CSS" } }
{ "name" : "HouFei", "skill" : { "skillOne" : "HTML+CSS" } }
{ "name" : "JiaPeng", "skill" : { "skillOne" : "HTML+CSS" } }
{ "name" : "LiJia", "skill" : { "skillOne" : "HTML+CSS" } }
其實這些查找操作,都是在作等于的階段,但是不光只有等于查詢,我們需要更多的查詢條件。
不等修飾符
小于($lt):英文全稱less-than
小于等于($lte):英文全稱less-than-equal
大于($gt):英文全稱greater-than
大于等于($gte):英文全稱greater-than-equal
不等于($ne):英文全稱not-equal 我們現在要查找一下,公司內年齡小于30大于25歲的人員。看下面的代碼。
db.workmate.find(
{age:{$lte:30,$gte:25}},
{name:true,age:true,"skill.skillOne":true,_id:false}
)
日期查找
MongoDB也提供了方便的日期查找方法,現在我們要查找注冊日期大于2018年1月10日的數據,我們可以這樣寫代碼。
var startDate= new Date('01/01/2018');
db.workmate.find(
{regeditTime:{$gt:startDate}},
{name:true,age:true,"skill.skillOne":true,_id:false}
)
我們先生命了一個日期變量,然后把使用大于符進行篩選。
總結:這節課內容并不多,但如果你是個DBA(數據庫管理員)工作中每天都會用到,所以這節課的內容練習是必須的,如果你懶得動手,那接下來的課程你可能無法學會。
## 第11節:查詢:find的多條件查詢 ##
很多時候我們需要查詢的值不只是有一個簡單的條件,比如我們現在要查詢一下同事中是33歲和25歲的,還比如我們要查詢同事中大于30歲并且會PHP技能的。MongoDB在這方面也支持的很好,我們來學習一下。
$in修飾符
in修飾符可以輕松解決一鍵多值的查詢情況。就如上面我們講的例子,現在要查詢同事中年齡是25歲和33歲的信息。
db.workmate.find({age:{$in:[25,33]}},
{name:1,"skill.skillOne":1,age:1,_id:0}
)
于$in相對的修飾符是$nin,就是查詢除了$in條件以為的指,小伙伴們可以自己進行練習一下,這里我就不作過多的演示了。
$or修飾符
它用來查詢多個鍵值的情況,就比如查詢同事中大于30歲或者會做PHP的信息。主要區別是兩個Key值。$in修飾符是一個Key值,這個需要去比較記憶。
db.workmate.find({$or:[
{age:{$gte:30}},
{"skill.skillThree":'PHP'}
]},
{name:1,"skill.skillThree":1,age:1,_id:0}
)
or很好理解,就是或者的意思,我們查出來的結果也是一樣的,查出了年齡大于30歲的,或者會做PHP的信息。相對應的還有$nor修飾符,這里不作演示了,自己試驗一下。
$and修飾符
$and用來查找幾個key值都滿足的情況,比如要查詢同事中大于30歲并且會做PHP的信息,這時需要注意的是這兩項必須全部滿足。當然寫法還是比較簡單的。只要把上面代碼中的or換成and就可以了。
db.workmate.find({$and:[
{age:{$gte:30}},
{"skill.skillThree":'PHP'}
]},
{name:1,"skill.skillThree":1,age:1,_id:0}
)
$not修飾符
它用來查詢除條件之外的值,比如我們現在要查找除年齡大于20歲,小于30歲的人員信息。需要注意的是$not修飾符不能應用在條件語句中,只能在外邊進行查詢使用。
db.workmate.find({
age:{
$not:{
$lte:30,
$gte:20
}
}
},
{name:1,"skill.skillOne":1,age:1,_id:0}
)
總結:這節課的知識比較簡單,但是要區分記憶,很容易搞混。幸運的是這里已經為你準備好了學習筆記。當你忘記的時候過來看看吧。
## 第12節:查詢:find的數組查詢 ##
這節主要學習數組的查詢,在學習update時就花了重墨去講數組的操作,可見數組的操作在MongoDB中很受重視,因為稍微大型一點的項目,設計的數據集合都復雜一些,都會涉及數組的操作。
完善數據
以前的我們的workmate集合對數組涉及還很少,現在在數據中加入了興趣(interest),并且給每個人加入了一些興趣,比如有寫代碼,做飯,看電影…..
當然這些數據你可以自己隨意構建,但是如果你不想自己費事費腦,這里也為你準備好了數據,你只要把以前的表刪除(drop)掉,重新載入(load)就可以了。
var workmate1={
name:'JSPang',
age:33,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
skillThree:'PHP'
},
regeditTime:new Date(),
interest:['看電影','看書','吃美食','釣魚','旅游']
}
var workmate2={
name:'ShengLei',
age:31,
sex:1,
job:'JAVA后端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'J2EE',
skillThree:'PPT'
},
regeditTime:new Date(),
interest:['籃球','看電影','做飯']
}
var workmate3={
name:'MinJie',
age:18,
sex:0,
job:'UI',
skill:{
skillOne:'PhotoShop',
skillTwo:'UI',
skillThree:'PPT'
},
regeditTime:new Date(),
interest:['做飯','畫畫','看電影']
}
var workmate4={
name:'XiaoWang',
age:25,
sex:1,
job:'UI',
skill:{
skillOne:'PhotoShop',
skillTwo:'UI',
skillThree:'PPT'
},
regeditTime:new Date(),
interest:['寫代碼','籃球','畫畫']
}
var workmate5={
name:'LiangPeng',
age:28,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
},
regeditTime:new Date(),
interest:['玩游戲','寫代碼','做飯']
}
var workmate6={
name:'HouFei',
age:25,
sex:0,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
},
regeditTime:new Date(),
interest:['化妝','讀書','做飯']
}
var workmate7={
name:'LiuYan',
age:35,
sex:0,
job:'美工',
skill:{
skillOne:'PhotoShop',
skillTwo:'CAD',
},
regeditTime:new Date(),
interest:['畫畫','聚會','看電影']
}
var workmate8={
name:'DingLu',
age:20,
sex:0,
job:'美工',
skill:{
skillOne:'PhotoShop',
skillTwo:'CAD',
},
regeditTime:new Date(),
interest:['美食','看電影','做飯']
}
var workmate9={
name:'JiaPeng',
age:29,
sex:1,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
skillThree:'PHP'
},
regeditTime:new Date(),
interest:['寫代碼','籃球','游泳']
}
var workmate10={
name:'LiJia',
age:26,
sex:0,
job:'前端',
skill:{
skillOne:'HTML+CSS',
skillTwo:'JavaScript',
skillThree:'PHP'
},
regeditTime:new Date(),
interest:['玩游戲','美食','籃球']
}
var db=connect('company');
var workmateArray=[workmate1,workmate2,workmate3,workmate4,workmate5,workmate6,workmate7,workmate8,workmate9,workmate10];
db.workmate.insert(workmateArray);
print('[SUCCESS]:The data was inserted successfully');
基本數組查詢
比如現在我們知道了一個人的愛好是’畫畫’,’聚會’,’看電影’,但我們不知道是誰,這時候我們就可以使用最簡單的數組查詢(實際工作中,這種情況基本不常用,所以這種查詢只作知識點儲備就可以了)。
db.workmate.find({interest:['畫畫','聚會','看電影']},
{name:1,interest:1,age:1,_id:0}
)
在終端中運行后,我們得到了數據。這時候我們說,想查出看興趣中有看電影的員工信息。按照正常邏輯,應該使用下面的代碼。
db.workmate.find({interest:['看電影']},
{name:1,interest:1,age:1,_id:0}
)
運行后,并沒有如我們所愿得到相應的人員數據,數據為空。那問題出現在哪里?問題就在于我們寫了一個中括號([]),因為加上中括號就相當于完全匹配了,所以沒有得到一條符合查詢條件的數據。我們去掉中括號再看看結果。
db.workmate.find({interest:'看電影'},
{name:1,interest:1,age:1,_id:0}
)
這就是我們在數組中查詢一項的方法,這也是數組查詢的最簡單用法。
$all-數組多項查詢
現在我們的條件升級了,要查詢出喜歡看電影和看書的人員信息,也就是對數組中的對象進行查詢,這時候要用到一個新的查詢修飾符$all。看下面的例子:
db.workmate.find(
{interest:{$all:["看電影","看書"]}},
{name:1,interest:1,age:1,_id:0}
)
這時候找到了興趣中既有看電影又有看書的人員。
$in-數組的或者查詢
用$all修飾符,是需要滿足所有條件的,$in主要滿足數組中的一項就可以被查出來(有時候會跟$or弄混)。比如現在要查詢愛好中有看電影的或者看書的員工信息。
db.workmate.find(
{interest:{$in:["看電影","看書"]}},
{name:1,interest:1,age:1,_id:0}
)
$size-數組個數查詢
$size修飾符可以根據數組的數量查詢出結果。比如現在我們要查找興趣的數量是5個人員信息,這時候就可以使用$size。
db.workmate.find(
{interest:{$size:5}},
{name:1,interest:1,age:1,_id:0}
)
這時候是5項愛好的人員就會顯示出來了。
$slice-顯示選項
有時候我并不需要顯示出數組中的所有值,而是只顯示前兩項,比如我們現在想顯示每個人興趣的前兩項,而不是把每個人所有的興趣都顯示出來。
db.workmate.find(
{},
{name:1,interest:{$slice:2},age:1,_id:0}
)
這時候就顯示出了每個人興趣的前兩項,如果我們想顯示興趣的最后一項,可以直接使用slice:-1,來進行查詢。
db.workmate.find(
{},
{name:1,interest:{$slice:-1},age:1,_id:0}
)
總結:如果你只看視頻一定學不會,程序這東西必須要動手練習,我在所有的視頻中都反復強調,目的沒有別的就是想讓你們真的學會,并應用到工作中去。
## 第13節:查詢:find的參數使用方法 ##
前邊已經講了3節查詢,都是在操作find方法的第一個參數(query)和第二個參數(fields)。find還有幾個常用的參數,這些參數多用在分頁和排序上。這節我們就把這些常用的選項說一說,理解后我們演示一個分頁的效果。
find參數:
query:這個就是查詢條件,MongoDB默認的第一個參數。
fields:(返回內容)查詢出來后顯示的結果樣式,可以用true和false控制是否顯示。
limit:返回的數量,后邊跟數字,控制每次查詢返回的結果數量。
skip:跳過多少個顯示,和limit結合可以實現分頁。
sort:排序方式,從小到大排序使用1,從大到小排序使用-1。
分頁Demo:
明白了上面這些選項,現在可以作一個最簡單的分頁,我們把同事集合(collections)進行分頁,每頁顯示兩個,并且按照年齡從小到大的順序排列。
dbd .workmate.find({},{name:true,age:true,_id:false}).limit(0).skip(2).sort({age:1});
$where修飾符
它是一個非常強大的修飾符,但強大的背后也意味著有風險存在。它可以讓我們在條件里使用javascript的方法來進行復雜查詢。我們先來看一個最簡單的例子,現在要查詢年齡大于30歲的人員。
db.workmate.find(
{$where:"this.age>30"},
{name:true,age:true,_id:false}
)
這里的this指向的是workmate(查詢集合)本身。這樣我們就可以在程序中隨意調用。雖然強大和靈活,但是這種查詢對于數據庫的壓力和安全性都會變重,所以在工作中盡量減少$where修飾符的使用。
## 第14節:查詢:find如何在js文本中使用 ##
前邊使用find都是JS在文本中寫完,然后復制到終端中執行,這樣非常麻煩。在講的過程中已經有很多小伙伴在問我如何像寫update語句一樣,在文本中直接運行。這節課我們就學習一下如何直接在文本中執行。
hasNext循環結果
想在文本中執行我們的find語句要用到游標和循環的操作,先看一下代碼,代碼中我已經對每一句進行了注釋。
var db = connect("company") //進行鏈接對應的集合collections
var result = db.workmate.find() //聲明變量result,并把查詢結果賦值給result
//利用游標的hasNext()進行循環輸出結果。
while(result.hasNext()){
printjson(result.next()) //用json格式打印結果
}
寫完后,現在你只需要在終端中進行load()就可以執行了,再也不用麻煩的復制粘貼了。
forEach循環
利用hasNext循環結果,需要借助while的幫助,MongoDB也為我們提供了forEach循環,現在修改上邊的代碼,使用forEach循環來輸出結果。
var db = connect("company") //進行鏈接對應的集合collections
var result = db.workmate.find() //聲明變量result,并把查詢結果賦值給result
//利用游標的hasNext()進行循環輸出結果。
result.forEach(function(result){
printjson(result)
})
作為個人覺的forEach循環更為優雅。這兩種方法都是非常不錯的,憑借自己愛好進行選擇吧。
總結:那我們MongoDB的基礎部分就全部講完了,我們學會了它的增、刪、改、查,你也可以使用MongoDB進行一些操作了。需要注意的是,只是這篇文章的完結,下篇文章我們進行講解MongoDB,開始講解MongoDB的索引。
## 第15節:索引:構造百萬級數據 ##
索引的性能提現必須要有大量數據才能看出來,你說你有10條20條數據,這是根本看不出來效果的,這節課就通過隨機數的方法,創造出一個百萬級數據的數據庫出來。(建議收看視頻學習)。
安裝Node
為了課程調試代碼方便,我們安裝Node,用來在終端中執行js,查看結果。
Node下載地址:https://nodejs.org/en/ (直接下載LTS版本就可以了)
安裝非常簡單,只要會安裝QQ,就應該可以安裝上,一直下一步也是沒有問題的。安裝好后,是不用配置我們的環境變量的。
我們可以在終端中輸入 node -v 來查看是否安裝成功,出現版本號就是安裝成功。
制作隨機數方法:
我們要想生成一個百萬級的數據集合,必須要有隨機數的參與,我們需要寫一個隨機數的方法。下面的代碼,創建了一個隨機數方法。
//生成隨機數
function GetRandomNum(min,max){
let range = max-min; //得到隨機數區間
let rand = Math.random(); //得到隨機值
return (min + Math.round(rand *range)); //最小值+隨機數取整
}
console.log(GetRandomNum(10000,99999));
制作隨機用戶名:
有了隨機數的方法,我們就可以制作一個隨機生成的用戶名。目的是存在不同的用戶名,方便我們測試查詢速度。
//生成隨機數
function GetRandomNum(min,max){
let range = max-min; //得到隨機數區間
let rand = Math.random(); //得到隨機值
return (min + Math.round(rand *range)); //最小值+隨機數取整
}
//console.log(GetRandomNum(10000,99999));
//生成隨機用戶名
function GetRadomUserName(min,max){
let tempStringArray= "123456789qwertyuiopasdfghjklzxcvbnm".split("");//構造生成時的字母庫數組
let outPuttext = ""; //最后輸出的變量
//進行循環,隨機生產用戶名的長度,這里需要生成隨機數方法的配合
for(let i=1 ;i<GetRandomNum(min,max);i++){
//隨機抽取字母,拼裝成需要的用戶名
outPuttext=outPuttext+tempStringArray[GetRandomNum(0,tempStringArray.length)]
}
return outPuttext;
}
console.log(GetRadomUserName(7,16))
插入200萬數據
有了生成隨機數和隨機用戶名的方法,就可以生產百萬級數據了。此處代碼會在視頻中作詳細講解。代碼如下:
var db = connect('company');
db.randomInfo.drop();
var tempInfo = [];
for (let i=0;i<2000000;i++){
tempInfo.push({
username:GetRadomUserName(7,16),
regeditTime:new Date(),
randNum0:GetRandomNum(100000,999999),
randNum1:GetRandomNum(100000,999999),
randNum2:GetRandomNum(100000,999999),
randNum3:GetRandomNum(100000,999999),
randNum4:GetRandomNum(100000,999999),
randNum5:GetRandomNum(100000,999999),
randNum6:GetRandomNum(100000,999999),
randNum7:GetRandomNum(100000,999999),
randNum8:GetRandomNum(100000,999999),
randNum8:GetRandomNum(100000,999999),
})
}
db.randomInfo.insert(tempInfo);
這個過程可能2-3分鐘,根據自己的電腦配置不同,會有差別。
插入完成后,我們可以使用 db.randomInfo.stats() 這個命令查看數據中的數據條數。
全部代碼:
//生成隨機數
function GetRandomNum(min,max){
let range = max-min; //得到隨機數區間
let rand = Math.random(); //得到隨機值
return (min + Math.round(rand *range)); //最小值+隨機數取整
}
//console.log(GetRandomNum(10000,99999));
//生成隨機用戶名
function GetRadomUserName(min,max){
let tempStringArray= "123456789qwertyuiopasdfghjklzxcvbnm".split("");//構造生成時的字母庫數組
let outPuttext = ""; //最后輸出的變量
//進行循環,隨機生產用戶名的長度,這里需要生成隨機數方法的配合
for(let i=1 ;i<GetRandomNum(min,max);i++){
//隨機抽取字母,拼裝成需要的用戶名
outPuttext=outPuttext+tempStringArray[GetRandomNum(0,tempStringArray.length)]
}
return outPuttext;
}
// console.log(GetRadomUserName(7,16))
// var startTime=(new Date()).getTime();
var db = connect('company');
db.randomInfo.drop();
var tempInfo = [];
for (let i=0;i<2000000;i++){
tempInfo.push({
username:GetRadomUserName(7,16),
regeditTime:new Date(),
randNum0:GetRandomNum(100000,999999),
randNum1:GetRandomNum(100000,999999),
randNum2:GetRandomNum(100000,999999),
randNum3:GetRandomNum(100000,999999),
randNum4:GetRandomNum(100000,999999),
randNum5:GetRandomNum(100000,999999),
randNum6:GetRandomNum(100000,999999),
randNum7:GetRandomNum(100000,999999),
randNum8:GetRandomNum(100000,999999),
randNum8:GetRandomNum(100000,999999),
})
}
db.randomInfo.insert(tempInfo);
總結:這節課主要是為講解MongoDB的索引作準備,我們用隨機數的方法構建了一個百萬級的數據表,如果你有興趣繼續往下學習聯系,這節課必須動手作一下。以后這篇文章的學習全是基于這個代碼。
## 第16節:索引:索引入門 ##
集合中已經有了200萬條的數據,可以進行索引的操作了。我們先來建立一個索引,然后看看它的查詢性能到底提升了多少倍。這節課的內容不會很難,主要掌握索引的建立方法即可。
普通查詢性能
我們先制作一個普通查詢,隨便查找一個用戶名,并計算出查詢和打印的時間,因為有200萬條數據,所以性能不會很高。
var startTime = new Date().getTime() //得到程序運行的開始時間
var db = connect('company') //鏈接數據庫
var rs=db.randomInfo.find({username:"tfruhjy8k"}) //根據用戶名查找用戶
rs.forEach(rs=>{printjson(rs)}) //循環輸出
var runTime = new Date().getTime()-startTime; //得到程序運行時間
print('[SUCCESS]This run time is:'+runTime+'ms') //打印出運行時間
上邊的代碼就是一個普通的查詢,只不過是記錄了時間。在終端運行后,可以得到大概的運行時間是0.8秒左右(電腦性能不同,有所不同),第一次無緩存的運行時間大概是3.5秒左右。這個時間是沒辦法滿足我們的日常查詢的。
建立索引
試著為用戶名(username)建立索引。建立索引只需要一句話就可以了。
db.randomInfo.ensureIndex({username:1})
查看現有索引
db.randomInfo.getIndexes()
終端的結果,現在只有一個索引值:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "company.randomInfo"
}
]
那現在使用命令建立一下索引db.randomInfo.ensureIndex({uername:1}),我的電腦大概要50秒左右,建立好后我們重新使用db.randomInfo.getIndexes(),查看一下結果。
結果如下:已經變成了兩條索引。
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "company.randomInfo"
},
{
"v" : 2,
"key" : {
"username" : 1
},
"name" : "uername_1",
"ns" : "company.randomInfo"
}
]
然后我們在來load一下demo07.js文件(load(‘./demo07.js’)),看一下現在多少秒可以查詢出來。這時候查詢的時間縮短到了4ms左右,查詢性能提升了大概200倍左右。
總結:無論是在關系型數據庫還是文檔數據庫,建立索引都是非常重要的。前邊講了,索引這東西是要消耗硬盤和內存資源的,所以還是要根據程序需要進行建立了。MongoDB也給我們進行了限制,只允許我們建立64個索引值。
## 第17節:索引:復合索引 ##
這節課先來看看什么樣的數據你使用索引會變慢,然后學習一下復合索引的使用和語法。通過這節課我們需要對所以使用的時機有所了解,避免畫蛇添足,產生不必的麻煩。
索引中的小坑
記得我剛學MongoDB時,學會了索引,我就到處想用,甚至幾百條數據的集合(collections),我也自作聰明的用一下,但結果往往是畫蛇添足,走了不少彎路。通過實際開發和性能對比,我自己總結了幾條不用索引的情況(不一定對,但是自己的經驗之談)。
數據不超萬條時,不需要使用索引。性能的提升并不明顯,而大大增加了內存和硬盤的消耗。
查詢數據超過表數據量30%時,不要使用索引字段查詢。實際證明會比不使用索引更慢,因為它大量檢索了索引表和我們原表。
數字索引,要比字符串索引快的多,在百萬級甚至千萬級數據量面前,使用數字索引是個明確的選擇。
把你經常查詢的數據做成一個內嵌數據(對象型的數據),然后集體進行索引。
復合索引
好了上邊我們講了一大堆理論,現在來看復合索引。復合索引就是兩條以上的索引。上節課我們已經把username字段建立了索引,我們現在把randNum0,這個字段也設置成索引。
db.randomInfo.ensureIndex({randNum0:1})
建立好后,我們再用查詢索引狀態命令進行查詢。
db.randomInfo.getIndexes()
這時候已經是兩個自建索引了,一共有三個索引。
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "company.randomInfo"
},
{
"v" : 2,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "company.randomInfo"
},
{
"v" : 2,
"key" : {
"randNum0" : 1
},
"name" : "randNum0_1",
"ns" : "company.randomInfo"
}
]
兩個索引同時查詢
我們同時查詢兩個索引的值,看看效果是怎么樣的。
var startTime=new Date().getTime();
var db = connect('company');
var rs= db.randomInfo.find({username:'7xwb8y3',randNum0:565509});
rs.forEach(rs=>{printjson(rs)});
var runTime = new Date().getTime()-startTime;
print('[Demo]this run time is '+runTime+'ms');
從性能上看并沒有什么特殊的變化,查詢時間還是在4ms左右。MongoDB的復合查詢是按照我們的索引順序進行查詢的。就是我們用db.randomInfo.getIndexes()查詢出的數組。
指定索引查詢(hint)
課程開始時,我說了數字的索引要比字符串的索引快,這就需要一個方法來打破索引表的查詢順序,用我們自己指定的索引優先查詢,這個方法就是hint().
var rs= db.randomInfo.find({username:'7xwb8y3',randNum0:565509}).hint({randNum0:1});
由于數據量和復雜成都還是不大,所以你看不出來明顯的性能提升,但是要相信技術胖,等工作中遇到大數據時,一定會得到很好的效果的。
刪除索引
當索引性能不佳或起不到作用時,我們需要刪除索引,刪除索引的命令是dropIndex().
db.randomInfo.dropIndex('randNum0_1');//索引的唯一ID
這里需要注意的是刪除時填寫的值,并不是我們的字段名稱(key),而是我們索引查詢表中的name值。這是一個小坑,希望小伙伴們不要踩中。
總結:這節主要內容還是操作索引,包括復合索引的建立,刪除。和一些使用索引的小竅門。
## 第18節:索引:全文索引 ##
有些時候需要在大篇幅的文章中搜索關鍵詞,比如我寫的文章每篇都在萬字以上,這時候你想搜索關鍵字是非常不容易的,MongoDB為我們提供了全文索引。
準備工作:
這節我們先建立一個集合(collections)-info,然后插入一小段文章,作用就是為建立全文索引提供數據,當然我們不再建立百萬級數據,我們只是看一下效果。
db.info.insert({contextInfo:"I am a programmer, I love life, love family. Every day after work, I write a diary."})
db.info.insert({contextInfo:"I am a programmer, I love PlayGame, love drink. Every day after work, I playGame and drink."}
當然這很簡單,再次強調這只是文章需要,實際工作中這么簡單的數據沒必要建立全文索引。
建立全文索引
db.info.ensureIndex({contextInfo:'text'})
需要注意的是這里使用text關鍵詞來代表全文索引,我們在這里就不建立數據模型了。
全文索引查找 建立好了全文索引就可以查找了,查找時需要兩個關鍵修飾符:
$text:表示要在全文索引中查東西。
$search:后邊跟查找的內容。
db.info.find({$text:{$search:"programmer"}})
查找多個詞
全文索引是支持多個次查找的,比如我們希望查找數據中有programmer,family,diary,drink的數據(這是或的關系),所以兩條數據都會出現。
db.info.find({$text:{$search:"programmer family diary drink"}})
如果我們這時候希望不查找出來有drink這個單詞的記錄,我們可以使用“-”減號來取消。
dbd .info.find({$text:{$search:"programmer family diary -drink"}})
轉義符:
全文搜索中是支持轉義符的,比如我們想搜索的是兩個詞(love PlayGame和drink),這時候需要使用\斜杠來轉意。
db.info.find({$text:{$search:"\"love PlayGame\" drink"}})
總結:全文索引在工作還是經常使用的,比如博客文章的搜索,長文件的關鍵詞搜索,這些都需要使用全文索引來進行。這節課的知識并不難,還是那句話,你看是不可能學會的,一定要動手練習。當然索引還有很多知識,這里我們只講最常用的知識,把小伙伴引入門就好,也就是常說的用20%的精力,學會80%的知識,然后在工作中進行迭代磨練。雖然MongoDB的索引文章結束了,但是MongoDB的文章還沒有結束,下篇文章開始學習如何管理MongoDB。
## 第19節:管理:用戶的創建、刪除與修改 ##
安裝好MongoDB時,它為我們默認開了一個最高管理權限方便我們管理數據庫,我們可以用mongo鏈接數據庫,就是這個原理。但在實際開發中并一般不能使用這個用戶,因為大家都知道和最高權限的原因,安全性和可靠性都不適合,所以要對MongoDB的用戶進行管理。這節課我們就學習一下MongoDB的用戶管理。
創建用戶:
首先要進入我們的admin庫中,進入方法是直接使用use admin 就可以。進入后可以使用show collections來查看數據庫中的集合。默認是只有一個集合的(system.version)。
創建用戶可以用db.createUser方法來完成,里邊參數還是蠻多的,代碼我寫在下邊,然后對每一項做出了解釋。
db.createUser({
user:"jspang",
pwd:"123456",
customData:{
name:'技術胖',
email:'web0432@126.com',
age:18,
},
roles:['read']
})
當然我們還可以單獨配置一個數據庫的權限,比如我們現在要配置compay數據庫的權限為讀寫:
db.createUser({
user:"jspang",
pwd:"123456",
customData:{
name:'技術胖',
email:'web0432@126.com',
age:18,
},
roles:[
{
role:"readWrite",
db:"company"
},
'read'
]
})
內置角色:
數據庫用戶角色:read、readWrite;
數據庫管理角色:dbAdmin、dbOwner、userAdmin;
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManage;
備份恢復角色:backup、restore;
所有數據庫角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
超級用戶角色:root
內部角色:__system
查找用戶信息
我們直接可以使用查找的方法,查找用戶信息。命令很簡單:
dbd .system.users.find()
刪除用戶:
刪除名利也是非常簡單,直接用remove就可以刪除這個用戶的信息和權限。
db.system.users.remove({user:"jspang"})
建權:
有時候我們要驗證用戶的用戶名密碼是否正確,就需要用到MongoDB提供的健全操作。也算是一種登錄操作,不過MongoDB把這叫做建權。
db.auth("jspang","123456")
如果正確返回1,如果錯誤返回0。(Error:Authentication failed。)
啟動建權
重啟MongoDB服務器,然后設置必須使用建權登錄。
mongod --auth
啟動后,用戶登錄只能用用戶名和密碼進行登錄,原來的mongo形式鏈接已經不起作用了。相應的用戶權限也對應妥當。實際項目中我們啟動服務器必須使用建權形式。
登錄
如果在配置用戶之后,用戶想登錄,可以使用mongo的形式,不過需要配置用戶名密碼:
mongom -u jspang -p 123456 127.0.0.1:27017/admin
這時候我們就可以用給我們的權限對數據庫操作了。
## 第20節:管理:備份和還原 ##
作為一個數據庫管理員,對數據庫的備份和還原是比作的兩項工作。其實用起來是相當簡單的,就是mongodump和mongorestore兩個命令。
數據備份
先來看一下mongodump備份的基本格式,其實這就是條在終端中執行的命令。
mongodump
--host 127.0.0.1
--port 27017
--out D:/databack/backup
--collection myCollections
--db test
--username username
--password password
比如現在我們備份所有MongoDB里的庫到D盤的databack文件夾下,就可以把命令寫成這樣
mongodump --host 127.0.0.1 --port 27017 --out D:/databack/
數據恢復
備份好后,如果數據庫有意外或者遭受到了攻擊,我們要進行回復數據庫,這時候就可以使用mongorestore命令。
還是先看一下它的基本格式
mongorestore
--host 127.0.0.1
--port 27017
--username username
--password password
<path to the backup>
比如我們現在不小心刪除了一個collections的數據,要進行恢復。現在刪除randomInfo集合。
db.randomInfo.drop()
使用命令進行恢復
mongorestore --host 127.0.0.1 --port 27017 D:/databack/
總結:兩個命令很簡單,甚至你可以寫成腳本和定時任務,讓他每天自己執行。但是如果你真的使用了MongoDB數據庫,這是一個最基本的操作,還是要會使用的。
## 第21節:管理:圖形界面管理(完結) ##
這節課我們主要看看圖形界面,圖形界面相對比較簡單,特別是我們已經會了很多終端的操作方法,所以我就不寫文章了,大家直接看視頻吧。
本文鏈接:http://jspang.com/post/mongodb.html
- 說明
- 開發任務
- 星課-真光
- 課表
- Excel Down
- 調課
- 課表修改
- 課表代碼分析
- 課堂
- 課堂:應用商店通信管理協議
- 教師賬號強制綁定手機或郵箱
- 強制綁定手機和修改密碼的規則
- 學堂
- 課程學習:討論功能
- 后臺:課程討論管理
- 課程直播接口
- 學習統計功能(舊版)
- 學習統計功能(新版)
- 同步課程統計功能
- 同步課程編輯-新增視頻
- 第三方接口
- 學科網
- 安徽第三方
- 大賽
- 管控系統
- 日志管理
- 設備日志
- 平板接口
- 渝教
- 教學總結
- 空白目錄
- Yii 1.1
- 學堂架構
- Yii 1.1一些方法的解讀
- MVCS結構
- 基礎使用語法
- 創建1個新模塊
- 關聯模型
- CDbCriteria
- 學生-課堂記錄
- 學生端頁面展示
- 教師端頁面展示
- 編輯課程文檔
- SQL
- 課堂項目運行入口
- 上傳資源示意圖
- 行為
- PHPStorm
- 源碼閱讀
- 會診答卷頁面
- 考點練習
- 資源首頁
- 同步課程
- 同步課程:章節信息
- 升學復習
- 統計圖-范例
- 模塊
- 非法詞
- 服務層
- MongoDB類
- 學堂作答記錄從Mongo新集合獲取數據
- MongoYii
- 錯題集
- 小技巧
- 完善資料
- 郵件發送
- K12
- JSpang視頻課程
- MongoDB
- 創業
- 項目
- 包包