---
# System prepended metadata

title: MongoDB 學習筆記

---

# MongoDB 學習筆記
![image](https://hackmd.io/_uploads/HJq1ncDyC.png)

## 前言
因為工作上遇到某個專案資料儲存相關的問題，我們專案需求使用 PostgreSQL，但這個資料特性是欄位長度不固定，因此起初的做法是將每一筆資料切片成多段（以欄位、值的方式切分）存進資料庫，來解決長度不一的問題；但實作後發現效率實在太差，才開始考慮使用 NoSQL 的資料庫比較一下差異。

* [安裝 MongoDB](#安裝-MongoDB)
* [安裝 MongoDB for Ubuntu 22.04](https://hackmd.io/@e9gZC-1CQNOzcD2pMY6apA/BybNPRlxA)
* [安裝管理工具](#安裝管理工具)
* [資料庫基本操作（CRUD）](#資料庫基本操作（CRUD）)

## 安裝 MongoDB
> ### MongoDB 產品簡介
> MongoDB 是一個物件導向的 NoSQL 資料庫，他們家有兩個主要產品線：
> * **MongoDB Atlas**
> * **MongoDB Server**。
> 
> MongoDB Atlas 提供雲端資料庫平台服務（按需付費），而 MongoDB Server 提供使用者下載至本地端自行部署的服務，其中又分為 Community Server 和 Enterprise Server 兩種版本，這次我們選擇使用 Community Server 免費開源的版本。
> 
> 若對於雲端部署有興趣的可以參考這篇文章 [MongoDB Atlas 教學](https://mengchiehliu.github.io/posts/pymongo/)


1. 到 [MongoDB 官網的 Community Server](https://www.mongodb.com/try/download/community) 找到最新版本下載
![image](https://hackmd.io/_uploads/rJnBD-u10.png)

2. 將下載檔案解壓縮後，拖曳至專案目錄下，並將資料夾名稱改名為`mongodb`，方便後續操作它
![image](https://hackmd.io/_uploads/B17OvZOJC.png)

3. 在專案目錄下建立`mongodb-data`資料夾，用來存放資料
![image](https://hackmd.io/_uploads/rJRcPZuJ0.png)

4. 輸入指令開啟資料庫`mongodb/bin/mongod --dbpath ./mongodb-data`
初次開啟時會跳出此警告，接著我們去系統設定調整一下
![image](https://hackmd.io/_uploads/B1xOjvbu1A.png)

5. 打開隱私權與安全性，找到被阻擋的訊息，點選強制允許即可
![image](https://hackmd.io/_uploads/BJynDWuyR.png)

6. 出現`Waiting for connections port 27017`，表示資料庫服務已啟用
![image](https://hackmd.io/_uploads/BJVnvbuJA.png)

## 安裝管理工具
資料庫啟用之後，我們需要安裝管理工具來連線操作資料庫，官網提供兩種選項，擇一安裝即可

1. **[MongoDB Shell (CLI)](https://www.mongodb.com/try/download/shell)**
![image](https://hackmd.io/_uploads/Hy5JwiDyR.png)

2. **[MongoDB Compass (GUI)](https://www.mongodb.com/try/download/compass)**
![image](https://hackmd.io/_uploads/rkHk_jPJC.png)
> 上述兩種工具安裝後直接執行即可，需注意的是要確認資料庫服務已開啟
## 資料庫基本操作（CRUD）
首先我們要先了解在 MongoDB 資料庫裡最基本的**組成概念**、**資料型別**

**組成概念**
Database 包含了多個 Collections，而 Collection 由多個 Documents 所組成。
![image](https://hackmd.io/_uploads/H1RMwRPyA.png)

>這張圖以 SQL vs MongoDB 來比較兩者的對應關係。
![image](https://hackmd.io/_uploads/S1cgOCw1C.png)


**資料型別**
MongoDB 所儲存的格式是Binary JSON 通稱為 BSON，都是以 JSON 的格式呈現，但儲存的資料型別還是有差異的，以下列表取自[ MongoDB Docs](https://www.mongodb.com/docs/manual/reference/bson-types/) ，點進去可以看到詳細說明。

| Type               | Number | Alias      | Notes           |
|--------------------|--------|------------|-----------------|
| Double             | 1      | "double"   |                 |
| String             | 2      | "string"   |                 |
| Object             | 3      | "object"   |                 |
| Array              | 4      | "array"    |                 |
| Binary data        | 5      | "binData"  |                 |
| Undefined          | 6      | "undefined"| Deprecated.     |
| ObjectId           | 7      | "objectId" |                 |
| Boolean            | 8      | "bool"     |                 |
| Date               | 9      | "date"     |                 |
| Null               | 10     | "null"     |                 |
| Regular Expression | 11     | "regex"    |                 |
| DBPointer          | 12     | "dbPointer"| Deprecated.     |
| JavaScript         | 13     | "javascript"|                |
| Symbol             | 14     | "symbol"   | Deprecated.     |
| 32-bit integer     | 16     | "int"      |                 |
| Timestamp          | 17     | "timestamp"|                 |
| 64-bit integer     | 18     | "long"     |                 |
| Decimal128         | 19     | "decimal"  |                 |
| Min key            | -1     | "minKey"   |                 |
| Max key            | 127    | "maxKey"   |                 |


### 基本指令
* 列出所有 database
`show dbs`

* 切換至指定的 database (若不存在則創建新的 database)
`use <database>`

* 建立 collection
`db.createCollection("rawdata")`

* 列出當前 database 所有的 collection
`db.getCollectionNames()`

* 顯示當前的 database
`db.getName()`

* 刪除當前的 database
`db.fropDatabase()`

* 刪除指定的 collection
`db.`

### 新增指令
* 新增單筆資料
![image](https://hackmd.io/_uploads/Hkq5VnvkR.png)
```mongodb=
# 新增單筆資料
# db.rawdata.insertOne(document, options)
db.rawdata.insertOne({
    "identity/LineItemId": "q4rf7k5pgrrbbudrcjzpkdfhwpprovpgwklgdnw7xy5shpny25ha",
    "identity/TimeInterval": "2024-01-01T00:00:00Z/2024-02-01T00:00:00Z",
    "lineItem/UsageAccountId": "934075583300",
})

# 新增成功的話會回傳以下資訊
{
  acknowledged: true,
  insertedId: ObjectId('660a346f81e20dbb5ca2948f')
}
```

* 新增多筆資料
![image](https://hackmd.io/_uploads/Hkfc8hwJ0.png)
```mongodb=
# 新增多筆資料
# db.rawdata.insertMany(documents, options)
db.rawdata.insertMany([
    {
        "name": "wilson",
        "age": 28,
    },
    {
        "name": "tony",
        "age": 46,
    },
])

# 新增成功的話回傳以下資訊
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('660a365681e20dbb5ca29490'),
    '1': ObjectId('660a365681e20dbb5ca29491')
  }
}
```

### 查詢指令
* 查詢單筆資料
```mongodb=
# 查詢單筆資料
db.rawdata.findOne({"name": "wilson"})

# 查詢成功的話回傳第一筆符合項目，否則回傳 null
```
![image](https://hackmd.io/_uploads/SyMdp6P1R.png)

* 查詢多筆資料
```mongodb=
# 查詢多筆資料
db.rawdata.find({"name": "wilson"})

# 查詢成功的話會回傳所有符合的項目，否則回傳 null
```
![image](https://hackmd.io/_uploads/SkkzFk_1C.png)
### 進階查詢指令
這邊整理了一些常用的進階查詢 Query 範例，參考自[ MongoDB 官方文檔](https://www.mongodb.com/docs/manual/reference/operator/query-comparison/)，介紹各類型運算子的定義及用法，有興趣的朋友可以點進去閱讀一下

| Name | Description                                                         | Note    |
| ---- | ------------------------------------------------------------------- | --- |
| $eq  | Matches values that are equal to a specified value.                 | 等於    |
| $gt  | Matches values that are greater than a specified value.             | 大於    |
| $gte | Matches values that are greater than or equal to a specified value. | 大於等於    |
| $in  | Matches any of the values specified in an array.                    |符合其中一個    |
| $lt  | Matches values that are less than a specified value.                |小於     |
| $lte | Matches values that are less than or equal to a specified value.    | 小於等於    |
| $ne  | Matches all values that are not equal to a specified value.         |不等於     |
| $nin | Matches none of the values specified in an array.                   |不符合其中一個     |

* 查詢 name 符合 "wil" 字段的所有項目（正則表達式）
`db.rawdata.find({name: { $regex: /wil/}})`
![image](https://hackmd.io/_uploads/r1B6EldkC.png)

* 查詢 age 小於 30 的所有項目
`db.rawdata.find({age: { $lt: 30}})`
![image](https://hackmd.io/_uploads/BJS6Beu1R.png)

* 查詢 age 小於 30 的所有項目總數
`db.rawdata.find({age: { $lt: 30}}).count()`
![image](https://hackmd.io/_uploads/Hye2ebOkR.png)

> **Tips**
> `$eq`, `$in`這兩個蠻容易搞混，這邊我是這樣理解，他倆比較像是“且”跟“或”的關係
> 
> 1. 這邊使用`$eq`篩選 name 符合 wilson 且符合 tony 的documents，故沒有回傳符合的結果
> ![image](https://hackmd.io/_uploads/H1UDHgYJ0.png)
>
> 2. 而這邊使用`$in`篩選 name 符合 wilson 或符合 tony 的 documents，因此回傳了兩筆結果
>  ![image](https://hackmd.io/_uploads/BJCVBgtyR.png)


### 修改指令
TODO

### 刪除指令
同樣也能運用上述的運算子來增加條件
* 刪除單筆資料（將 age 是 28 的刪除）
`db.rawdata.deleteOne({age: "28"})`
![image](https://hackmd.io/_uploads/BJPEGWuyC.png)

* 刪除多筆資料（正則表達式，將 name 是 m 開頭的刪除）
`db.rawdata.deleteMany({name: { $regex: /^m/}})`
![image](https://hackmd.io/_uploads/HJDBBWOy0.png)


## 參考文章
[Mongodb 教學：從安裝到啟動基本操作教學](https://tw.alphacamp.co/blog/mongodb-intro)
[從入門到精通 MongoDB 鐵人賽](https://ithelp.ithome.com.tw/users/20130448/ironman/3618)