跳至主要内容

MongoDB 簡介

Understanding Databases, Collections & Documents

MongoDB Server > Databases > Collections > Documents

在 MongoDB 的伺服器中,每個使用者可以建立多個 Database,每個 Database 裡可以有多個 Collection,
每個 Collection 裡可以有多個 Document。

  • Collection 好比 RDBMS 的 Table
  • Document 好比 RDBMS 的 Record

Creating Databases & Collections

mongosh 指令複習

  • $ show dbs
  • $ use DB_NAME
  • $ db.COLLECTION_NAME.insertOne({ YOUR_DOCUMENT })

Understanding JSON Data

以下範例相當於一個 JSON 檔內儲存兩筆 MongoDB Document,亦即一對花括號僅容納一筆 Document。
由 line 5 ~ 7 可知,Document 可以儲存多種資料型別。

JSON Data 範例
[
{
"departureAirport": "MUC",
"arrivalAirport": "SFO",
"aircraft": "Airbus A380",
"distance": 12000,
"intercontinental": true
},
{
"departureAirport": "LHR",
"arrivalAirport": "TXL",
"aircraft": "Airbus A320",
"distance": 950,
"intercontinental": false
}
]

Comparing JSON & BSON

在 MongoDB 的 Collection 當中成功插入 Document 後,該筆 Document 會自動附帶結構如下的鍵值對。
其中 ObjectId 是 BSON 格式獨特的資料型別。

"_id": ObjectId("4j5i0gja;k...")

雖然 MongoDB 會自動產生 Document ID,但開發者仍可手動輸入 ID,但該鍵值對的鍵名稱必須是「_id」, 其值則可以是。

手動輸入 Document ID
db.collection_name.insertOne({insert:"manual", _id: "RX-78-2"})
db.collection_name.insertOne({_id: 3.141, sign: "pi"})

在 RDBMS 資料庫中,同一 Table 的 Record 必須擁有相同的資料結構(Schema)
在 MongoDB,同一 Collection 的 Document 可以擁有不同的資料結構。

Schemaless
[
{
_id: ObjectId("646f0fd133e3ab77e3a6f3da"),
departureAirport: 'MUC',
arrivalAirport: 'SFO',
aircraft: 'Airbus A380',
distance: 12000,
intercontinental: true
},
{
_id: ObjectId("646f13c633e3ab77e3a6f3db"),
name: 'try',
schema: 'free',
value: 123,
validate: false
}
]

Create, Read, Update, Delete (CRUD) & MongoDB

Create

  • insertOne(data, options)
  • insertMany(data, options)

Read

  • find(filter, options)
  • findOne(filter, options)

update

  • updateOne(filter, data, options)
  • updateMany(filter, data, options)
  • replaceOne(filter, data, options)

update

  • deleteOne(filter, options)
  • deleteMany(filter, options)

Finding, Inserting, Deleting & Updating Elements

  • updateOne(filter, data, options)
    更新符合 filter 的 Document,在 Document 中 新增或更改 data 中的鍵值對。
db.collection_name.updateOne({key1: value1}, {$set: {key2: value2}})
  • updateMany(filter, data, options)
    更新符合 filter 的 Document,在 Document 中 新增或更改 data 中的鍵值對。
    當 filter 指定為 {} 時,表示以 Collection 內的所有 Document 為更新對象。
db.collection_name.updateMany({}, {$set: {key1: value1}})
  • deleteOne(filter, options)
    刪除符合 filter 的單筆 Document
db.collection_name.deleteOne({key1: value1})
  • deleteMany(filter, options)
    刪除符合 filter 的所有 Document
    當 filter 指定為 {} 時,表示刪除 Collection 內的所有 Document
db.collection_name.deleteMany({key1: value1})

Understanding "insertMany()"

  • insertMany(data, options)
    在 Collection 中插入 data。在 mongosh 當中操作時,
    可直接以 [{data1}, {data2}, ...] 的格式貼上,但必須在貼上後才輸入閉括號。
db.collection_name.insertMany([{key1: value1}, {key2: value2}, ...])

Diving Deeper Into Finding Data

  • find(filter, options) 從 Collection 中找出符合 filter 的 Document。 可不傳入 fliter,如此可找出 Collection 中的所有 Document。 可搭配運算子($gt, )縮小查詢範圍。
db.collection_name.find()
db.collection_name.find({key1: value1})
db.flights.find({key1: {$gt: value}})
提示

前往官方文件檢視其他運算子: Query and Projection Operators

  • findOne(filter, options) 從 Collection 中找出符合 filter 的第一筆 Document。
db.collection_name.findOne({key1: {$lte: value}})

"update" vs "updateMany()"

  • update(filter, data, options)
    本函式於 2023/5/25 時已棄用。欲覆蓋既有 data,可使用 replaceOne。
    本函式不需搭配 $set 運算子,且傳入函式的 data 會取代原始 data。
  • updateOne(filter, data, options)
    本函式需搭配 $set 運算子,傳入函式的 data 不會取代原始 data。
  • updateMany(filter, data, options)
    本函式需搭配 $set 運算子,傳入函式的 data 不會取代原始 data。

Understanding "find()" & the Cursor Object

find() 並非回傳所有 Document,而是回傳一個 Cursor Object,
倘若 find() 找到超過 20 筆資料,第 21 筆起不會直接顯示於終端機。

  • find().toArray()
    可將 Cursor Object 轉換為陣列,如此即使資料超過 20 筆也會全部顯示。
  • find().froEach()
    不同的語言有不同的語法,詳見官方文件
    將匿名函式傳入 forEach(),可迭代處理 Cursor Object。
db.collection_name.find()
.forEach((value) => {printjson(value._id)})

Understanding Projection

假設有一筆 Document 如下。使用 find() 讀取這筆 Document 時,
若未輸入任何選項將回傳五個鍵值對(不含 _id)。
反之,查詢時加入選項可即可取得指定的鍵值對,此概念稱為 Projection。

  {
"fieldName1": "value1",
"fieldName2": "value2",
"fieldName3": "value3",
"fieldName4": "value4",
"fieldName5": "value5"
}

以下為 Projection 範例,第一個指令會取回 key1 的鍵值對及 _id;
第二個指令僅取回 key1 的鍵值對。可知查詢條件中的值 1 表 true,0 表 false。

$ db.collection_name.find({}, {key1: 1})
$ db.collection_name.find({}, {key1: 1, _id: 0})

Embedded Documents & Arrays - The Theory

  • Embedded Documents
    以巢狀結構儲存資料,最高可達 100 層,容量上限為每筆 Document 16 MB。
  • Arrays Document 可藉由 Array 儲存列表,也可在巢狀結構中嵌入 Array。

Working with Arrays

Document 的鍵值對中,值可以是 Array,而 Array 當中可以儲存各種型別的資料。
以下範例中,Array 儲存了四個元素,分別為字串、整數、布林值、Document

$ db.collection_name.updateOne({key1: value1}, 
{$set: {key2: ["string", 1, true, {}]}})

Accessing Structured Data

// 從 Collection 當中找出一筆符合條件的資料,且僅回傳 key2 的值。
$ db.collection_name.findOne({key1: value1}).key2

// 適用於目標為 Array 時。從 Collection 當中找出一筆符合條件的資料,
且僅回傳 key2 的值的索引值為 1 的元素。
$ db.collection_name.findOne({key1: value1}).key2[1]

// 輸入查詢條件時,冒號前的部分用 " 夾住,表示以嵌入式結構為條件查詢。
$ db.collection_name.find({"key.embeddedKey": "value"})

Time to Practice - The Basics & CRUD Operations

練習題格式
{
"firstName": "Max",
"lastName": "Schwarzmueller",
"age": 29,
"history": [
{"disease": "cold", "treatment": "..."},
{ ... }
]
}
  1. Insert 3 patient records with at least 1 history entry per patient
  2. Update patient data of 1 patient with new age, name and history entry
  3. Find all patients who are older than 30 (or a value of your choice)
  4. Delete all patients who got a cold as a disease

2023/5/25 練習記錄

  1. 先建立新資料庫
$ use patients
  1. 編寫 JSON 資料
[
{
"firstName": "Max",
"lastName": "Schwarzmueller",
"age": 29,
"history": [
{"disease": "cold", "treatment": "treatment2"},
{"disease": "COVID-19", "treatment": "treatment1" }
]
},
{
"firstName": "小明",
"lastName": "王",
"age": 39,
"history": [
{"disease": "cold", "treatment": "treatment2"},
{"disease": "COVID-19", "treatment": "treatment1" },
{"disease": "COVID-32", "treatment": "treatment1" }
]
},
{
"firstName": "怡君",
"lastName": "王",
"age": 33,
"history": [
{"disease": "COVID-19", "treatment": "treatment1" },
{"disease": "COVID-32", "treatment": "treatment2" }
]
}
]
  1. 練習插入三筆記錄
練習用 insertOne 插入
db.patients.insertOne({
... "firstName": "Max",
... "lastName": "Schwarzmueller",
... "age": 29,
... "history": [
... {"disease": "cold", "treatment": "treatment2"},
... {"disease": "COVID-19", "treatment": "treatment1" }
... ]
... })
練習用 insertMany 插入
db.patients.insertMany([
... {
... "firstName": "小明",
... "lastName": "王",
... "age": 39,
... "history": [
... {"disease": "cold", "treatment": "treatment2"},
... {"disease": "COVID-19", "treatment": "treatment1" },
... {"disease": "COVID-32", "treatment": "treatment1" }
... ]
... },
... {
... "firstName": "怡君",
... "lastName": "王",
... "age": 33,
... "history": [
... {"disease": "COVID-19", "treatment": "treatment1" },
... {"disease": "COVID-32", "treatment": "treatment2" }
... ]
... }
... ])
  1. 練習更新一筆記錄
練習用 replaceOne 更新
db.patients.replaceOne({ _id: ObjectId("646f6fda33e3ab77e3a6f3f8") }, { "firstName": "宜君", "lastName": "王", "age": 18, "history": [ { "disease": "COVID-19", "treatment": "treatment1" }, { "disease": "COVID-32", "treatment": "tre
patients> db.patients.replaceOne({_id: ObjectId("646f6fda33e3ab77e3a6f3f8")}, {
... "firstName": "宜君",
... "lastName": "",
... "age": 18,
... "history": [
... {"disease": "COVID-19", "treatment": "treatment1" },
... {"disease": "COVID-32", "treatment": "treatment456" }
... ]
... })
練習用 updateOne 更新
db.patients.updateOne({firstName: "小明"}, {$set: {age: 31}})
  1. 練習用運算子查詢記錄
db.patients.find({age: {$gt: 28}})
  1. 練習刪除記錄
    提示

    輸入 CRUD 指令時,若 filter 的目標鍵儲存值為 Array,仍可視為嵌入式結構,
    用 " 夾住陣列內 document 的屬性。MongoDB 會將陣列內元素取出迭代處理。

db.patient.deleteMany({"history.disease": "cold"})