Day 2 - MongoDB 的 CRUD 教學


本系列文章也刊登在我的部落格中,如有更新將以部落格為主~

前言

上一篇文章中,我們介紹了 MongoDB 的基礎概念。

本篇文章是系列文的第二篇,要帶大家實際對 MongoDB 進行資料操作~

這篇文章將介紹資料的基本操作,包含:

  • Create:創建資料
  • Read:讀取資料
  • Update:更新資料
  • Delete:刪除資料

Create 創建資料

在 MongoDB 創建資料的指令很單純,常用的有以下兩個:

insertOne

用於創建一筆新的 document。

下面的例子會在名為products的 collection 中創建一筆紀錄了商品項目(item)商品數量(qty)的 document:

db.products.insertOne( { item: "card", qty: 15 } );

insertMany

用於創建多筆新的 documents。

下面的例子會在名為products的 collection 中創建三筆 documents。我們將要創建的多筆 documents 放在 Array 中,mongoDB 會依照 array 中 item 的順序去依序 insert:

db.products.insertMany( [
      { item: "card", qty: 15 },
      { item: "envelope", qty: 20 },
      { item: "stamps" , qty: 30, price: 1000 } // 有 price 資訊的 document
   ] );

還記得 MongoDB 的特性是 Schemaless 的嗎?在創建 document 時,每筆 document 都可以有不同的格式,如上面的stamps有記錄價錢(price)這個資訊,前面兩筆則沒有。

ObjectId

如果你透過 MongoDB shell 輸入上方的指令成功創建資料,會看見 MongoDB 回覆了一個 ObjectId 資訊。這個 ObjectId 是什麼呢?

其實我們每創建一筆資料時,MongoDB 都會主動賦予 document 一個獨特的_id欄位,其值的type(資料格式)就是我們看到的ObjectId。我們通常會拿_id當作該筆資料的主鍵,因為 MongoDB 可以保證同一個 collection 中每筆 document 的_id都是獨一無二的。

我們也可以使用自己定義的_id作為 documet 主鍵。只需要在創建 document 時帶上_id欄位就行,但我們需要保證自己定義的_id不會重複,否則創建操作會失敗。

由於標準JSON中並沒有定義ObjectId這個 type,由此可見 document 是以BSON儲存,而不是常見的 JSON

Read 讀取資料

MongoDB 中可以透過find系列指令查詢資料。

find 與 findOne

單純使用find而不帶參數時,會找到該 collection 中所有資料
如下面的例子,我們會拿到記錄在 products 這個 collection 裡的所有 product:

db.products.find() // 拿回所有資料

一次拿回所有資料雖然方便但效率低落,因此在現實應用中很少這樣做。
我們通常會在find指令中帶上query(查詢條件),用來找出符合我們需求的資料。

下面例子會找出_id = 5的 document,由於我們知道_id是獨一無二的,因此可以預期 MongoDB 只會返回一筆(或零筆)資料:

db.products.find( { _id: 5 } )

使用 find 會找出所有符合條件的 document。
使用 findOne 時,會找出第一筆符合條件的 document。

Operators

MongoDB 提供了許多Operators(操作符)讓搭配 query 使用,讓我們可以更精準描述想要查詢的資料。

如下面例子中我們使用$gt(greater than)操作符,幫助我們查詢出所有 distance(距離)> 1000 的航班資訊:

db.flightData.find( { distance: { $gt: 1000 } })

另一個例子中我們使用$in操作符,找出所有 bloodType(血型)是 A 或 B 的 user

db.users.find( { bloodType: { $in: ['A', 'B'] } } )

更詳細的 Operator 列表可以參考官方文件

Cursor Object

當我們透過 shell 操作find指令時,就算已經加上 query 條件,還是有機會一次拿回過多資料導致效率低落(想像你要搜尋姓氏開頭是 A 的使用者,可能超級超級多)。因為效率考量,使用 shell 的 find 指令時,MongoDB 其實是回傳一個Cursor Object,而不是所有符合條件的資料。

Cursor Object 包含了:

  1. 部分資料:MongoDB 預設會回傳 20 筆資料
  2. 資料指針:指向目前拿到的最後一筆資料。我們可以透過 next 方法向 MongoDB 拿更多資料。
// 透過 find 指令找出所有血型是 A、B 的 user。拿到 mongoDB 回傳的 Cursor object
var myCursor = db.users.find( { bloodType: { $in: ['A', 'B'] } } );

// 把 cursor 中的資料轉換成 array,預設會拿 20 筆
var documentArray = myCursor.toArray();

// 透過 hasNext 檢查看看還有還有更多資料,有的話就透過 cursor 的 next 方法拿,並 print 出來
while (myCursor.hasNext()) {
   printjson(myCursor.next());
}

關於 Cursor 的更多使用方法可以參考官方文件

Update 更新資料

更新跟創建的方式很像。可以選擇只更新一筆資料、也可以一次更新多筆資料。

updateOne

在操作更新時,我們使用 query 告訴 MongoDB「想要更新哪些資料」以及「如何更新他們」。

如下面的例子,我們在名為inventory的 collection 中找尋item = paper的資料,並且把資料的 size 更新為 100:

db.inventory.updateOne(
   { item: "paper" },
   {
     $set: { "size": 100 },
   }
)

由於我們使用updateOne指令,就算符合 query 條件的資料有很多筆,MongoDB 也只會更新符合條件的第一筆 document。

updateMany

同上述的例子,如果指令換成updateMany,則所有符合條件的資料都會被更新。

db.inventory.updateMany(
   { item: "paper" }, // 所有 item 欄位值是 paper 的資料都會被更新
   {
     $set: { "size": 100 },
   }
)

使用updateMany要注意的地方是,如果我們在 query 中沒有放上條件,則 collection 中的所有資料都會被更新,因此要小心使用!

db.inventory.updateMany(
    {}, // 由於 query 為空,所有 document 都會加上 message 這個欄位
    {
        $set: { message: 'Oops' }
    }
)

Delete 刪除資料

deleteOne 與 deleteMany

刪除資料的邏輯跟查詢一樣,我們必須在 query 中描述要被刪除的資料條件。

下述例子會將_id = 5的 document 刪除:

db.products.deleteOne( { _id: 5 } )

相同的,我們可以透過deleteMany一次刪除多筆資料。
下面例子會將血型為 A 的 user 全部刪除:

db.user.deleteMany(
   { bloodType: "A" }, // 所有血型為 A 的 user 都被刪除
)

要注意的是,如果我們在 query 中沒有放上條件,就會把 collection 裡的所有 documents 全部刪除,因此要謹慎使用!

db.inventory.deleteMany({}) // 清空 inventory Collection

總結

本篇文章介紹了 MongoDB 中基本的CRUD指令。
基本上透過這些指令已經能應付大多數的操作。關於每個指令的詳細介紹可以參考官方文件

下篇文章我們將針對更強大的Aggregate操作做介紹。

BitWise 分享

除了參與本次 寫作松 分享軟體開發的技術之外,
我在 Podcast 節目 【 BitWise 一點智慧 】 中也會以輕鬆的角度,跟大家聊聊軟體開發、設計、學習!歡迎大家收聽~~

👉 用Apple收聽
👉 用Spotify收聽
👉 用Google收聽
👉 官方網站

#mongoDB #Backend #Database







Related Posts

Day 3 - MongoDB 的 Aggregation 教學

Day 3 - MongoDB 的 Aggregation 教學

ConQuest計分檔的分割函數

ConQuest計分檔的分割函數

深入學習 lsd-slam - 5

深入學習 lsd-slam - 5






Comments