HOWTO · MongoDB
MongoDB 和 Mongoose 的區別
在本初學者課程中,我們將向你展示如何區分 MongoDB 和 Mongoose。本文還將討論每種方法的好處。
本頁內容
MongoDB 是文件資料庫;Mongoose 則是供使用 MongoDB 的 Node.js 應用程式選用的 JavaScript 物件資料建模(ODM)程式庫。兩者並非位於同一層的替代方案。若要從 Node.js 操作 MongoDB,可以直接使用官方的 MongoDB Node.js 驅動程式,也可以使用建構於該驅動程式之上、並提供建模功能的 Mongoose。
MongoDB 與 Mongoose:各自是什麼
MongoDB 會將記錄以 BSON 文件形式儲存在集合中。同一集合內的文件可以有不同欄位,因此應用程式能隨需求調整資料形狀,不必要求每筆記錄都符合固定的關聯式資料表定義。這種彈性不代表資料毫無結構或無法檢查:應用程式可以強制執行規則,而 MongoDB 也支援在資料庫層級設定驗證規則。
Mongoose 是 Node.js 程式庫,可用結構描述描述應用程式資料,並透過模型操作資料。結構描述可以指定預期欄位、型別、預設值和驗證規則。模型則提供方法,用來在 MongoDB 集合中建立、查詢、更新及刪除文件。Mongoose 屬於 ODM,因為它負責在 JavaScript 物件與儲存的文件之間進行對應;它本身不是資料庫。
MongoDB Node.js 驅動程式是另一種選擇。它會將 Node.js 應用程式連接至 MongoDB,並提供資料庫、集合及文件操作。Mongoose 在底層使用這個驅動程式,同時加入自己的結構描述與模型慣例。無論採用哪種方式,最後操作的都是 MongoDB 文件。
MongoDB 與 Mongoose 如何搭配運作
兩者的關係如下:Node.js 應用程式可以直接呼叫 MongoDB Node.js 驅動程式,或呼叫 Mongoose;Mongoose 透過驅動程式與 MongoDB 通訊;MongoDB 負責儲存並回傳文件。無論採用哪種方式,你都需要 MongoDB 部署環境和連線。加入 Mongoose 並不會取代資料庫,也不會免除選擇資料庫與集合的需要。
儲存的文件基本上由欄位和值組成。例如,客戶記錄可能包含姓名和電子郵件地址。在應用程式程式碼中,可以用 JavaScript 物件表示這種資料形狀:
執行以下範例:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
const customer = { name: "Ada", email: "ada@example.com" };
console.log(`${customer.name}: ${customer.email}`);
Ada: ada@example.com
此範例已成功執行,只展示文件的 JavaScript 資料形狀;它並未連線至 MongoDB,也沒有使用任一程式庫。接下來的程式碼片段分別展示兩種 API 用法。以下範例假設已安裝 mongodb 或 mongoose 套件、可連線至 MongoDB 部署環境,且呼叫端會提供連線 URI。這些範例僅供說明,並未使用這些套件或資料庫實際執行。
使用 MongoDB Node.js 驅動程式時,先選取資料庫和集合,再直接呼叫集合操作:
async function insertCustomer(uri) {
const { MongoClient } = require("mongodb");
const client = new MongoClient(uri);
const customer = { name: "Ada", email: "ada@example.com" };
try {
await client.connect();
return await client.db("app").collection("customers").insertOne(customer);
} finally {
await client.close();
}
}
使用 Mongoose 時,先定義結構描述、建立模型,再呼叫模型操作:
async function createCustomer(uri) {
const mongoose = require("mongoose");
const customerSchema = new mongoose.Schema({ name: String, email: String });
const Customer = mongoose.model("Customer", customerSchema);
try {
await mongoose.connect(uri);
return await Customer.create({ name: "Ada", email: "ada@example.com" });
} finally {
await mongoose.disconnect();
}
}
這些範例並未宣稱曾執行資料庫操作。驅動程式範例會明確開啟並關閉用戶端;Mongoose 範例則透過 Mongoose 連線及中斷連線。兩種情況下,呼叫端都必須提供有效的 URI,並處理回傳結果或連線錯誤。
MongoDB 與 Mongoose 的主要差異
兩者的主要差異在於各自的角色,以及應用程式規則所屬的層級:
| 項目 | MongoDB | Mongoose |
|---|---|---|
| 定義 | 儲存及查詢資料的文件資料庫 | 應用程式使用的 Node.js ODM 程式庫 |
| Node.js 存取方式 | 驅動程式提供資料庫和集合操作 | 結構描述與模型在驅動程式之上提供額外的應用程式層 |
| 資料形狀 | 同一集合中的文件可以各不相同 | 結構描述可以描述特定模型預期的資料形狀 |
| 驗證 | 可使用資料庫驗證、應用程式檢查,或兩者並用 | 可在透過 Mongoose 儲存文件前,依結構描述規則進行驗證 |
| 查詢 | 使用驅動程式的集合操作及 MongoDB 查詢語法 | 提供模型與查詢建構 API,但仍透過驅動程式送出操作 |
Mongoose 的結構描述並不能保證伺服器上的每次寫入都遵循其規則。透過其他應用程式、資料庫工具或直接使用驅動程式進行的寫入,不會自動執行 Mongoose 驗證。如果必須確保所有寫入者遵循一致規則,也應考慮 MongoDB 本身的驗證功能,以及應用程式端的檢查。
Mongoose 也可以依據結構描述中的型別轉換值,並套用應用程式設定的預設值或中介軟體。這些功能提供實用的慣例,但屬於程式庫行為,而非 MongoDB 本身的特性。集合仍然需要命名:Mongoose 會將模型對應至集合,預設使用自己的命名慣例,也可以在需要時明確指定集合。
何時使用 MongoDB 驅動程式或 Mongoose
若你想直接使用 MongoDB 的資料庫與集合 API、減少相依套件,或處理形狀正在變動或差異很大的文件,可以直接使用 MongoDB Node.js 驅動程式。驅動程式也會明確呈現資料庫操作,適合資料存取層已提供驗證與共用慣例的服務。
若應用程式能受益於具名模型、集中管理的結構描述、應用程式層級驗證、型別轉換、預設值或中介軟體,可以選擇 Mongoose。當多個路由或服務需要操作相同類型的文件時,這些功能有助於整理程式碼;不過,開發人員也必須理解並維護這些慣例。
對任何專案而言,兩種選擇都不一定比較簡單或快速。請考量文件形狀有多穩定、驗證應在哪裡執行、團隊想直接使用多少 MongoDB API,以及專案是否需要 Mongoose 這個額外層。你也可以在部分資料存取路徑使用驅動程式、其他路徑使用 Mongoose,前提是團隊清楚訂定共用規則與集合的管理責任。
優點與取捨
MongoDB 的彈性文件模型,讓應用程式能在符合領域需求時,儲存欄位各異的記錄。直接使用驅動程式可省去 ODM 層,讓程式碼明確操作集合及 MongoDB 功能。相對地,應用程式團隊必須自行決定資料驗證、預設值及物件轉換為文件的慣例,並持續一致地套用。
Mongoose 透過結構描述與模型集中管理許多慣例。驗證、型別轉換、預設值、中介軟體及查詢輔助功能,可能減少重複的應用程式程式碼。相對地,開發人員需要增加一項相依套件,並留意 Mongoose 特有的行為,包括驗證何時執行,以及模型設定如何對應至集合。結構描述可以提升應用程式內的一致性,但無法自行約束透過 Mongoose 以外途徑進行的寫入。
無論採用哪種方式,資料庫索引、存取控制、部署及維運做法都很重要。ODM 不會自動讓資料模型變得正確,而使用驅動程式也不會妨礙應用程式定義清楚的結構描述。真正值得考量的是每項規則應由哪個層級負責,以及如何確保規則套用至所有相關寫入。
為你的應用程式選擇合適方案
在這兩種設計中,MongoDB 都是資料庫。對 Node.js 應用程式而言,實際上通常是在 MongoDB Node.js 驅動程式與 Mongoose 之間做選擇,而不是把 MongoDB 和 Mongoose 當成互相競爭的資料庫。
若資料存取層需要直接控制集合操作並彈性處理文件,請選擇驅動程式。若以結構描述為基礎的模型及應用程式層級行為,能解決實際的程式碼組織或驗證需求,則可選擇 Mongoose。決定前,請先確認驗證必須在哪裡強制執行、是否有不同寫入者操作相同集合,以及團隊是否能接受維護 ODM 慣例。只要各自的責任清楚,兩種方案都能有效運作。