啟動mongoDB server
1 | mongod --dbpath "路徑" //指定資料的存放路徑 |
- 終止mongo server
1 | net stop MongoDB //windows |
- 使用mongo shell終止mongo server
1 | use admin //進入admin collection |
MongoDB設定檔
- 可以指定mongoDB讀取自己產生的設定檔,設定檔可以事先設定簡單的設定,指令如下
1 | mongod --config "路徑/檔名.cfg" |
- 設定檔內容範例
1 | storage: |
1 | mongo //進入mongo shell |
匯入Json檔到資料庫(import)
1 | mongoimport 檔案名稱 -d 資料庫名稱 -c collection名稱 --jsonArray --drop |
MongoDB shell 基礎指令
Read
1 | db."collection名稱".find(filter,option) //列出該collection所有的document |
- find實際上不會列出所有的documents(預設20筆),而是會給一個cursor object,可以在shell輸入it指令來繼續尋訪剩下的documents,或是在find()後面加上.toArray()
- $gt代表大於,例如{distance: {$gt: 1000}},distance大於1000的都會被列出來
- filter範例 : {keyname: value}
- find會列出documents內的所有key value,但有時在應用時不需要全部的key,這時可以如 db.passenger.find({},{name: 1}),只會列出name和_id(預設一定會列出)這兩個key,如不想列出_id只要將{name: 1}改成{name:1, _id:0}
Comparison Operators
- $gt : 大於
- $gte : 大於等於
- $eq : 等於
- $ne : 不等於
- $lt : 小於
- $lte : 小於等於
- $in : 出現在陣列裡的數值會被列出來
- $nin : 與$in相反,出現在陣列裡的數值不會被列出來
範例如下
1 | db.movies.find({runtime: {$gt: 30}}) //runtime大於30的documents會被找出來 |
Logical Operators
- $and : 結合複數條件,條件都符合才會被列出來
- $not : 與條件符合以外的資料才會被列出來
- $nor : 與條件都不符合的才會被列出來
- $or : 結合複數條件,符合其中一個就會被列出來
範例如下
1 | db.movies.find({$or: [{"rating.average": {$lt: 5}}, {"rating.average": {$gt: 9.3}}]}) //rating.average小於5或大於9.3的會被找出來 |
Element operator
- $exists: 找出擁有特定field(key)的document
- $type: 找出特定field是屬於特定資料型態的document
範例如下
1 | db.users.find({age: {$exists: true}}) //有age欄位的document才會被列出來 |
Evaluation operator
- $regex: field包含特定條件的字串的documents會被找出來(使用正規表達式,regular expression)
- $expr: 同一個document內比較兩個field,並找出比較後回傳的結果
1 | db.movies.find({summary: {$regex: /musical/}}) //summary只要有包含musical的document就會被列出來 |
Array
- $size: 找出陣列中擁有特定item數的document
- $all: 找出陣列中擁有特定內容的document,陣列內容順序不一樣也可以找出
- $elemMatch: 找出陣列中的item擁有符合特定條件的field(同個document)
1 | db.users.find({hobbies: {$size: 3}}) //hobbies陣列裡的item數為3的會被列出來 |
Cursor
- 使用find()查找資料時原則上會返回一個指標並且只顯示20筆資料,如果要繼續取得下一個20筆資料shell提供it指令可以繼續向下搜尋
- 依照應用程式的規模符合條件的資料可能要上千上萬筆,透過資料庫再傳回用戶端很沒效率,因此會使用指標
- next(): 回傳下一筆資料
- hasNext(): 檢查是否還有下一筆資料(boolean)
- sort(): 根據條件排序
- skip(): 跳過特定數目的資料
- limit(): 指定cursor回傳的資料數目
1 | //由於Mongodb shell是基於javascript執行環境,因此可以使用JS語法 |
Projection
- 有時我們不需要document中全部的field,這時可以指定回傳的資料只要包含哪些field
1 | db.movies.find({},{name: 1, genres: 1, runtime: 1, rating: 1}).pretty()//傳回的資料只會包含指定為1的field,要注意的是_id預設一定會回傳 |
Create
1 | db."collection名稱".insertOne(data,option) //在指定collection(類似SQL database的table)內插入資料,()內要輸入Json型態的資料 |
- data為Json格式
- collection會在insert資料時被動產生,如果需要特殊設定collection的validation可以使用以下指令
mongoDB預設的行為
- 當我們在插入新資料時可能會遇到一個狀況,就是插入了重複的資料、需要插入的資料已經在資料庫裡了,這時的MongoDB會如下應對
1 | //在下面的範例中,假如_id為cars的document已經存在資料庫,mongoDB預設的行為會在執行到cars時暫停並輸出錯誤,但sports仍會被加到資料庫裡,yoga則不會,因為cars以後就不會被執行了 |
Update
1 | db."collection名稱".updateOne(filter,data,option) //更新一筆特定的document,db.test.updateOne({test1:"123"},$set:{test2:456}) |
- 如果要操作的對象回複數時,filter設定成 {} 代表對象為全部,delete也適用
- $set: $set的對象如果存在會更新成指定的值,不存在則會新增該key value
- $inc: 可以對值進行加減法
- $mul: 可以對值進行乘法
- $min: $min在新的值比舊的值小時會更改資料
- $max: $max在新的值比舊的值大時會更改資料
- $unset: 指定的field會被drop,也就是該field會變成不存在
- $rename: 對field進行改名
1 | //_id為5的document中的hobbies會被更改為{title: "Sports", frequency: 5}, {title: "Cooking", frequency: 3} |
- upsert(): 如果不確定要更新的對象是否存在,如果存在的話需要更新,不存在的話需要新增的話,可以使用此選項
1 | //如果name為Maria的document存在的話就會更新,不存在的話就會create,包含搜尋條件name: "Maria"也會被create |
- 增加element
1 | //使用$push增加一個element到hobbies陣列裡 |
- 移除element
1 | //hobbies陣列裡title為Hiking的element會被移除 |
Delete
1 | db."collection名稱".deleteOne(filter,option) //刪除特定的一筆document,如有兩筆以上符合條件,只會刪除找到的第一筆 |
- 刪除所有document
1 | db.users.deleteMany({})//會變成空的 |
MongoDB資料型態
- text
- boolean
- number(mongo shell是基於javascript,所以預設的數字全部都是float,double的64bit數字)
- Integer(int32) : 32bit長度的數字 +-2,147,483,647
- Integer(int64) : 64bit長度的數字 +-9,223,372,036,854,775,807
- NumberDecimal
- ObjectId : 插入新資料時mongoDB會自動產生一個唯一的_id,也可以自行設定
- ISODate
- Timestamp
使用非預設的數字型態可以使用如下方法
1 | db.test.insertOne({a: NumberInt(1)}) //在大部分的情況下與預設型態沒有差別,但可以節省一點空間 |
限制 : 單一document的大小必須小於等於16mb,document的巢狀結構最多100層
設定Schema的validation
- 有些時候我們要限制create和update時資料的格式,例如create時一定要包含哪些屬性,且屬性一定要是特定的資料型態,否則會更新失敗或跳出警告訊息,我們使用以下指令來設定
1 | db.createCollection("collection名稱", { |
- 如果要更改設定,可以使用以下指令
1 | db.runCommand({ |
Index
- MongoDB提供了增加Index的功能,可以幫助在query時提供效率,當進行一個query時,如果沒有index的話MongoDB會查找整個collection來尋找符合條件的資料,但是有了index(ordered),MongoDB可以快速地找到需要的資料,index是一個指標,可以指向他所代表的整個document
- Index雖然可以增加query的效率,但還是必須付出代價,因為他是一個有排序的資訊,所以每次insert新資料時,其他document都必須更新
- 要注意的是,如果query所回傳的資料幾乎是全部的document,速度反而會比沒有index還慢,因為index會多一步(透過指標找到整個document的步驟)
讓MongoDB給出更詳細的query資訊
- explain(): 適用於update、find、delete
1 | db.contacts.explain().find({"dob.age": {$gt: 60}}) |
- createIndex(): 創造index的方法,如此一來被指定的field會代表指標指向不同的documents,並且這些指標是按照順序排列的
- dropIndex(): 刪掉之前所創造的index,傳的參數跟createIndex一樣
- getIndexes(): 可以查看現有的index,mongodb會自動以_id作為index。db.contacts.getIndexes()
1 | db.contacts.createIndex({"dob.age": 1}) //決定要使用index的field,1代表遞增,-1代表遞減 |
compound index
- 可不只根據一個field創造index,可以由兩個field組合成一個index
1 | db.contacts.createIndex({"dob.age": 1, gender: 1}) //會這樣排序: 31 male, 31 female, 32 male, 32 female etc.. |
- 使用index還有一好處,假如沒有index,當結合sort()時,mongodb會先將所有documents載入記憶體,而這個容量有個上限為32mb,在大部分的狀況下這會造成timeout
1 | db.contacts.explain().find({"dob.age": 35}).sort({gender: 1})//就算只有使用age作為query條件,但mongodb最知道你也使用了gender作為sort條件,而使用index |
unique index
- 另一個用處是,可以透過設定第二參數將index設定為unique,藉此防止重複資料的發生,使用insert插入的資料如果unique index有重複就會出現錯誤或警告
- 要注意的是如果我們有兩筆資料,一個有email另一個沒有email,將email作為index並設為unique會成功,但當insert另一個沒有email的資料時會出現錯誤,因為email為no value重複了(沒有email的資料重複),我們可以利用partial filter來解決
1 | db.contacts.createIndex({email: 1}, {unique: true}) //如果email有重複將會出現error |
partial filter with index
- 有時候必須頻繁的查找特定條件的field,就不需要幫整個collection都都加進index的對象裡,這時可以使用partial index
- unique index提到的no value重複的問題可以用partial index解決
1 | //只有gender為male的document才會根據dob.age為順序產生index |
Time to live(TTL) index
- TTL index將會設定自動消滅document的時間,適合用在session資料,或是過了期限必須自動消滅的資料
- 只能用在time data,不能用在compound index。如果使用在非時間的field不會error但會被自動忽略
1 | //假設以下的指令按照順序執行 |
Multi-key index
- 如果我們設為index的對象本身是一個array,mongodb將其視為multi-key index
- multi-key儲存index的方式和一般的index不同,如果一個array有四個elements,這些elements會被另外擷取出來形成index,如果有1000個documents一個document被設為index的field是個array且平均element數為4,index將會有4*1000之多
- multy-key index可以和一般的index組成coumpound index,但是兩個multi-key不能組成compound index
Text index
- text index也是multi-key的一種
- 前面也有介紹搜尋特定的字串時可以使用regular expression($regex)的方式,但是該方式的效能並不是很好,Text index可以提供更好的解決辦法
- text index會將一段字串針對每個單詞作切割存到text index的array裡,且會自動過濾掉暫停字句(a, the etc.)
- 所有的大寫字母會被統一轉成小寫字母
1 | //text index創造的方法有些不同,不是設定1或-1而是"text",如果使用1或-1就會變成一般的index |
Combine text index
- 以上面的products例子來說,如果想將title也設成text index會失敗,因為我們只能有一個text index,但是可以結合text index來解決這個問題
- 額外說明如果要將text index給刪除我們必須下db.products.dropIndex(“description_text”),而不是dropIndex(description: “text”),”description_text”可以使用getIndexes來查看
- 不只是可以查找包含哪些字串,還可以找包含特定字串但不包含其他特定字串,可以藉由 - 號來設定
1 | //刪除所有text index後,來創造combine text index |
Create index in the foreground or background
- 上面所示範的createIndex都是在foreground執行,這兩者有些差異
- foreground: index在創造期間collection會被鎖住,不能insert或find等操作,index創造速度較快
- background: 相對於foreground,collection不會被鎖住,但index創造速度較慢
- 假設我們同時連線mongodb並創造index,在index還沒完成之前我們插入某一筆資料,這筆資料要一直等到index完成後才會被插入,大型專案的情況下,index可能需要幾分鐘才會被完成,如果在foreground執行會造成整個應用出問題
1 | db.rating.createIndex({age: 1}, {background: true})//如此一來就可以在background執行 |