[MongoDB] 學習筆記(一) - 安裝、基礎CRUD、外部輸入資料、Data Type

Ben Hsu
21 min readDec 19, 2020

這篇是對MongoDB學習過程的筆記,內容是Udemy上的課程加上自己的一些測試。

為什麼選MongoDB作為NoSQL學習的對象,從圖1可以預見。圖1是現在Database受歡迎程度的排序表,可以看到MongoDB在2020年10月排名第5,也是前5名唯一一個文件導向 (Document-oriented database)的資料庫管理系統的資料庫,下一個的相同性質的資料庫已經排到26名了,所以如果要挑一個CP值最高的來碰,絕對就是MongoDB。

可以先看看目錄之中是否有自己想要查看的內容,直接在本文搜尋標題。當節使用的指令會列於該節最後。

Mongo版本:v4.4.1

圖1. DB-Engines Ranking (評分依據包含goolge, Bing搜尋熱度、Stack OverflowLinkedIn的提及次數等)

目錄

  • 資料庫的安裝
  • 基本操作 (for windows)
  • 資料庫、Collection的建立與刪除
  • Document的插入、刪除、查詢、更新、覆蓋
  • 外部輸入資料
  • 常用的Data type
  • 結論

資料庫的安裝

安裝網路上有許多教學,也可以參考下面udemy的連結,mongoDB的安裝在這個教學是可預覽的,這篇筆記的順序也是出自這部教學影片。

但如果你不想安裝,也可以到下面的官網連結,提供了一個測試環境。

基本操作 (for windows)

環境變數設定

  1. 在windows 開始的搜尋視窗搜尋編 '輯環境系統變數',進入圖2左邊視窗
  2. 圖2左。點環境變數(N),會跳出圖2中央視窗
  3. 圖2中。點選最上白框變數中的Path,接著點選編輯(E)會進入圖2右的視窗。
  4. 圖2右。點選新增 (N),加入MongoDB當初的安裝位置。位置形式可以參考我環境變數的第一列。
圖2. 環境變數設定視窗

啟動MongoDB服務

開啟的MongoDB時,要以系統管理員開啟命令提示字元,你可以在開始的搜尋中搜尋cmd命令提示字元,並右鍵以系統管理員身分執行。開啟後,如果有完成系統環境設定,可在command line輸入指令,結果見圖3:

:: CMD, 啟動MongoDB 
net start MongoDB /* 啟動MongoDB */
:: CMD,進入MongoDB
mongo
// mongo, 離開MongoDB//
exit
:: CMD:關閉MongoDB
net stop MongoDB
圖3. MongoDB啟動、進入、離開、關閉執行結果

help、cls指令

help 是mongoDB中的幫助指令,結果見圖4。他列出了一些指令說明如 show dbs ,或者如果想了解method進一步的細節, 如想了解db的使用,可以下db.help() 。而 cls是清理畫面。

圖4. help執行結果

資料庫、Collection的建立與刪除

在MySQL中,結構由大到小依序為database、table、row of data,而對應到Mongo則為database、collection、document。而一個初始的mongo中,會有三個預設的database,admin、config、local,可以使用 show dbs看到。

建立、使用Database與Collections

首先是建立database,建立使用的指令都是 use <db_name>,但是如果在創建的database中沒有collection,那你使用 show dbs 時,並不會看到新建的database。必須要使用 db.createCollection(<db_name>),建立完collection後,再使用 show dbs 時,才可以看到建立的database。執行結果見圖5。

圖5. 建立database與collection

創建了collection後,如果我們要查看collection可以使用 db.getCollectionNames(),並使用 db.<collection_name> 操作collection,例如可以使用db.<collection_name>.drop() 來刪除collection。但是如果建立的collection名稱並不是英文字母開頭,如名字是123,那就必須使用 db.getCollection(<collection_name>) 來操作collection。而如果你要刪除database時,可以使用 db.dropDatabase()。也或者,因為當database沒有collection時,database也會自動刪除,所以當把所有collection刪除時,也能達到相同效果。執行結果見圖6。

查看、刪除Database與Collections

圖5. 查看、刪除database與collection
// 指令列表 //// 顯示現有的資料庫 //
show dbs
// 使用、建立資料庫 //
use <db_name>
// 顯示目前使用的資料庫 //
db.getName()
// 建立collection //
db.createCollection(<collection_name>)
// 顯示collection列表 //
db.getCollectionNames()
// 操作collection //
db.<colletion_name>
db.getCollection(<colletion_name>)
// 刪除colletion //
db.getCollection(<colletion_name>).drop()
// 刪除database //
db.dropDatabase()

Document的插入、刪除、查詢、更新、覆蓋

在mongo中的document都是json格式,格式、基本說明如下:

  1. 由array (中括號 []),以及object(大括號{})所組成
  2. 在object中,可以由field, value成為成對數組,並以冒號(:)區隔
  3. 在value中,可以儲存數字、字串、布林函數、array、object
  4. 每個object中的field不需要相同,使其沒有schema的限制,更為自由

因為練習時常常需要建立json,但時常會有格式不正確的問題,發生錯誤時,我就會透過網站驗證一下。

### json格式範例
[
{
"first_name": "Robin",
"last_name": "Jackman",
"title": "Software Engineer",
"salary": 5500,
"hire_date": "2001-10-12"
},
{
"first_name": "Taylor",
"last_name": "Edward",
"title": "Software Architect",
"salary": 7200,
"hire_date": "2002-09-21"
},
{
"test":[
{
"key": "value"
}
]
}
]

而如果你將json放入mongo之後,他的格式將不會是json,而是被轉換為bson,意義是Binary json目的是提高mongo儲存與掃描的效率。範例可以看圖6,他的field為 _id時,value是 objectID(...) ,這並不是json的合法格式。

圖6. mongo中的bson格式

Document的插入、刪除

插入有兩個指令 db.<collection_name>.insertOne(document),與db.<collection_name>.insertMany(documents)。 函數中放置的document就是json物件,而顧名思義 insertOne是插入一個objec的json物件、insertMany則可以插入一個array。執行結果可以見圖7左。

刪除也有兩個指令db.<collection_name>.deleteOne(filter)db.<collection_name>.deleteMany(filter)。可以透過給定條件filter,篩選出document,來進行刪除。其中 deleteOne是刪除一個物件,但如果篩選出現兩個以上的結果時,則會刪除第一個document;deleteMany則是刪除多個物件。而filter則是也是json格式,例如 {"a":1}。執行結果可以見圖7右。

圖7. 插入(左)、刪除(右)執行結果

filter 可以使用條件式的方式書寫,如我要查詢field為a中,大於0的數值,則 filter{"a": {$gt:0}},其他operators可以參考下面網址。

// 插入程式碼 //
db.employee.drop()
db.employee.insertOne({
"first_name": "Robin",
"last_name": "Jackman",
})
db.employee.insertOne([{
"first_name": "Robin",
"last_name": "Jackman",
},
{'a':1, 'b':2
}])
db.employee.insertMany([{
"first_name": "Robin",
"last_name": "Jackman",
},
{'a':1, 'b':2
}])
db.employee.find().pretty()
// 刪除程式碼 //
db.employee.drop()
db.employee.insertMany(
[
{
"a":1,
"b":2
},
{
"a":1,
"b":2
},
{
"a":1,
"b":2
}
])
db.employee.find()
db.employee.deleteOne({"a":1})
db.employee.find()
db.employee.deleteMany({"a":1})
db.employee.find()
db.employee.deleteMany({"a":{"$gt":0}})

Document的查詢、更新

與前面相似,查詢、更新分別也有針對一個與多個document操作的函數。一次查詢一個document的函數 db.<collection_name>.findOne(filter, projection),如果設定的條件找到多的結果,則回傳第一個;而一次查詢多個的函數為 db.<collection_name>.find(filter, projection) 。如果filter為空,則回傳所有結果;projection則為選擇需要的field,如果field為空,則回傳所有篩選結果。

projection 的寫法為{ <field1>: <value>, <field2>: <value> … },如果需要篩選field1,則會寫成 {field: 1}

針對單一ducoment進行更新的函數為db.<collection_name>.updateOne(filter, oupdate_object),而針對多個ducoment進行更新的函數,則為db.<collection_name>.updateMany(filter, oupdate_object) ,而oupdate_object$set(指定field: 更新數值)

所以當我想要將資料庫中,薪水大於1000的員工,都變成500,那就會寫成 db.<collection_name>.updateMany("Salary":{"$gt":1000}, $set("Salary":500)) ,相關的執行結果如圖8.。

圖8. 查詢、更新執行結果
// 建立document//
db.employee.drop()
db.employee.insertMany(
[
{
"Salary":1000,
"_id":0
},
{
"Salary":1000,
"_id":1
},
{
"Salary":1000,
"_id":2
},
{
"Salary":2000,
"_id":3
}
])
// 查詢程式碼 //
db.employee.findOne()
db.employee.findOne({'Salary':1000})
db.employee.find()
db.employee.find({'Salary':1000})
// 更新程式碼 //
db.employee.updateOne({"Salary":{"$gte":1000}}, {$set:{"Salary":500}})
db.employee.find()
db.employee.updateMany({"Salary":{$gte:1000}}, {$set:{"Salary":900}})
db.employee.find()
db.employee.updateOne({"_id":0}, {$set:{"Position":"PM"}})
db.employee.find()
db.employee.updateMany({"_id":{$gte:2}}, {$set:{"Position":"RD"}})
db.employee.find()
db.employee.find({"Salary":{$gte:900}}, {"Position":1})

Document的覆蓋

當你想要將一個document以另一個document進行覆蓋時,有兩個函數可以做到這件事,document進行覆蓋的 db.<collection_name>.replaceOne(filter, document) 與針對所有結果進行覆蓋的db.<collection_name>.update(filter, document/oupdate_object)

其中db.<collection_name>.update 的第二個參數,如果你給的是document ,則效果與db.<collection_name>.replaceOne 相同;而如果是給oupdate_object 則效果與db.<collection_name>.updateOne 相同。

但需要注意的一點是,這裡覆蓋的document,他是將document中的field, value進行替換,但是document的_id是不會變的,而如果你嘗試要更改_id,則會回報錯誤。執行結果如圖9.。

圖9. 替換執行結果
// 替換程式碼 //
db.employee.drop()
db.employee.insertMany(
[
{
"Salary":1000,
"_id":0
},
{
"Salary":1000,
"_id":1
},
{
"Salary":1000,
"_id":2
},
{
"Salary":2000,
"_id":3
}
])
db.employee.replaceOne({"_id":0}, {"Position":"PM"})
db.employee.find()
db.employee.update({"_id":1}, {$set:{"Position":"HR"}})
db.employee.find()
db.employee.update({"_id":2}, {"Position":"RD"})
db.employee.find()
db.employee.update({"_id":3}, {"Position":"RD", '_id':4})
db.employee.find()
db.employee.replaceOne({"_id":3}, {"Position":"SA", '_id':3})
db.employee.find()

外部輸入資料 - mongoimport

當我們想要直接從外部import json檔案進MongoDB時,可以使用mongoimport。這個指令是要在cmd的環境下執行的,不是在mongo的環境。但如果是windows的使用者,有可能會發生無法找到執行檔的問題,此時就要下載相關的toolbox,連結如下:

下載下來解壓縮後,會出現一個bin的資料夾,裡面就可以看到mongoimport的執行檔,如下圖。此時將這些執行檔複製到mongo的bin資料夾(就是上面加入環境變數的路徑),此時再cmd中就能執行 mongoimport 的指令。

使用方法是這樣的,其中 -d指的是資料庫的名字, -c是collection的名字, -jsonArray 是指輸入的檔案格式,-drop 則是指刪除先前創建的collection,重新建立一個新的collection。

mongoimport <json路徑> -d <db_name> -c <collection_name> -jsonArray --drop

詳細的使用方法見 -help,執行結果如下圖。可以看到他連結到local mongdb的路徑,並且drop掉test.test這個collection,最後成功導入2個documents。

常用的Data Type

MongoDB的shell,其實就是javascript的Client,所以裡面是可以寫javascript語法的。所以一些MongoDB的資料格式,會透過javascript創建,再儲存進MongoDB中。如果是以其他語言使用,也會有對應的driver可以處理,支援的drive可以參考下面網站。

https://docs.mongodb.com/drivers/

String、Number、Boolean

如果我們想要看一個value的data type,可以透過先以 var 將物件存下來,再以 typeof 查看。而存取的方式,則可以用 db.<collection_name>.findOne儲存,再呼叫裡面的field。舉例而言 var result = db.employee.findOne({Salary:1000}) 就是將篩選的結果存在result裡面,此時我要看Salary的內容時,就可以使用 result.Salary。執行結果如圖10,像是500numbertruebooleanHRstring,而其他像是array會是object,都會被歸在 object中。

圖10. 資料類型查看
db.employee.drop()
db.employee.insertMany([
{
"Salary":500,
"_id":0
},
{
"Position":"HR",
"Salary":1000,
"Tenure":4.56,
"Stay":true,
"Phone_number":{
home:22222222,
mobile:0911111111
}
}]
)
var result = db.employee.findOne({Salary:1000})
result.Position
typeof result.Position
result.Salary
typeof result.Salary
result.Stay
typeof result.Stay
result._idt
typeof result._idt
result.Phone_number
typeof result.Phone_number

時間數據類型 date

時間類型是挺煩的,很容易因為起算的時區不同而有誤差。常用的function是 Date,而他基本上有三種用法:

  1. 不輸入參數 new Date():顯示現在時間
  2. 輸入數字 new Date(number):顯示自1970-01-01以來過了幾毫秒,如果輸入 new Date(1000*60) 則結果會是 ISODate("1970-01-01T00:01:00Z") 過了一分鐘。
  3. 輸入日期格式文字 new Date(string) :舉例而言,你可以輸入 new Date("2018-5-1"),結果會是 ISODate("2018-05-01T00:00:00Z") 。而可以接受的日期格式可以參考,如果輸入錯誤的格式會出現NA
圖11. 時間類型變數執行結果
new Date()
new Date(0)
new Date(1000*60)
new Date("2018-5-1")
typeof new Date("2018-5-1")
+ new Date("2018-5-1")
typeof + new Date("2018-5-1")
new Date("2018/05/01")
db.employee.insertOne({"Time":new Date("2018/05/01")
})
var result = db.employee.findOne({"Time":new Date("2018/05/01")
})
result._id
typeof result._id
result.Time
typeof result.Time
new Date("2018/05")

結論

MongoDB的設計允許document有不同的schema,以及value允許object、array,使儲存結構更為自由。但是,實務上若真的將schema設計的不一樣,這對於DB後續的使用是非常不方便的,所以我們是利用他自由,但仍要加上規範。意思就是在一個collection中我們還是會使用相同的schema,不然也是很難用。

這篇文章說明了MongoDB的基本特性、安裝、CRUD的做法,以及數據類型。但這裡還沒提到的是一些較複雜的篩選方式,以及詳細連線到driven的方式。後幾篇再整理。

--

--