天天看點

你真的了解mongoose嗎?

引言

繼上篇文章「

Koa2+MongoDB+JWT實戰--Restful API最佳實踐

」後,收到許多小夥伴的回報,表示自己對于

mongoose

不怎麼了解,上手感覺有些難度,看官方文檔又基本都是英文(寶寶心裡苦,但寶寶不說

你真的了解mongoose嗎?

)。

為了讓各位小夥伴快速上手,加深對于 mongoose 的了解,我特地結合之前的項目整理了一下關于 mongoose 的一些基礎知識,這些對于實戰都是很有用的。相信看了這篇文章,一定會對你快速上手,了解使用 mongoose 有不小的幫助。

mongoose 涉及到的概念和子產品還是很多的,大體有下面這些:

你真的了解mongoose嗎?

本篇文章并不會逐個去展開詳細講解,主要是講述在實戰中比較重要的幾個子產品:

模式(schemas)

模式類型(SchemaTypes)

連接配接(Connections)

模型(Models)

聯表(Populate)

模式(schemas)

定義你的 schema

Mongoose

的一切都始于一個

Schema

。每個 schema 映射到 MongoDB 的集合(

collection

)和定義該集合(collection)中的文檔的形式。

const mongoose = require("mongoose");

const { Schema, model } = mongoose;

const userSchema = new Schema(
  {
    __v: { type: Number, select: false },
    name: { type: String, required: true },
    password: { type: String, required: true, select: false },
    avatar_url: { type: String },
    gender: {
      type: String,
      enum: ["male", "female"],
      default: "male",
      required: true
    },
    headline: { type: String },
  },
  { timestamps: true }
);

module.exports = model("User", userSchema);

           

複制

這裡的

__v

versionKey

。該 versionKey 是每個文檔首次建立時,由 mongoose 建立的一個屬性。包含了文檔的内部修訂版。此文檔屬性是可配置的。預設值為

__v

。如果不需要該版本号,在 schema 中添加

{ versionKey: false}

即可。

建立模型

使用我們的 schema 定義,我們需要将我們的

userSchema

轉成我們可以用的模型。也就是

mongoose.model(modelName, schema)

。也就是上面代碼中的:

module.exports = model("User", userSchema);
           

複制

選項(options)

Schemas 有幾個可配置的選項,可以直接傳遞給構造函數或設定:

new Schema({..}, options);

// or

var schema = new Schema({..});
schema.set(option, value);
           

複制

可用選項:

  • autoIndex

  • bufferCommands

  • capped

  • collection

  • id

  • _id

  • minimize

  • read

  • shardKey

  • strict

  • toJSON

  • toObject

  • typeKey

  • validateBeforeSave

  • versionKey

  • skipVersioning

  • timestamps

這裡我隻是列舉了常用的配置項,完整的配置項可檢視官方文檔

https://mongoosejs.com/docs/guide.html#options

這裡我主要說一下

versionKey

timestamps

:

  • versionKey

    (上文有提到) 是 Mongoose 在檔案建立時自動設定的。這個值包含檔案的内部修訂号。versionKey 是一個字元串,代表版本号的屬性名, 預設值為

    __v

  • 如果設定了

    timestamps

    選項, mongoose 會在你的 schema 自動添加

    createdAt

    updatedAt

    字段, 其類型為

    Date

到這裡,已經基本介紹完了

Schema

,接下來看一下

SchemaTypes

模式類型(SchemaTypes)

SchemaTypes

為查詢和其他處理路徑預設值,驗證,getter,setter,字段選擇預設值,以及字元串和數字的特殊字元。在 mongoose 中有效的 SchemaTypes 有:

  • String

  • Number

  • Date

  • Buffer

  • Boolean

  • Mixed

  • ObjectId

  • Array

  • Decimal128

  • Map

看一個簡單的示例:

const answerSchema = new Schema(
  {
    __v: { type: Number, select: false },
    content: { type: String, required: true },
    answerer: {
      type: Schema.Types.ObjectId,
      ref: "User",
      required: true,
      select: false
    },
    questionId: { type: String, required: true },
    voteCount: { type: Number, required: true, default: 0 }
  },
  { timestamps: true }
);
           

複制

所有的 Schema 類型

  • required

    : 布爾值或函數,如果為 true,則為此屬性添加必須的驗證。
  • default

    : 任意類型或函數,為路徑設定一個預設的值。如果值是一個函數,則函數的傳回值用作預設值。
  • select

    : 布爾值 指定 query 的預設

    projections

  • validate

    : 函數,對屬性添加驗證函數。
  • get

    : 函數,使用

    Object.defineProperty()

    定義自定義 getter
  • set

    : 函數,使用

    Object.defineProperty()

    定義自定義 setter
  • alias

    : 字元串,隻對

    mongoose>=4.10.0

    有效。定義一個具有給定名稱的虛拟屬性,該名稱可以擷取/設定這個路徑

索引

你可以用 schema 類型選項聲明 MongoDB 的索引。

  • index

    : 布爾值,是否在屬性中定義一個索引。
  • unique

    : 布爾值,是否在屬性中定義一個唯一索引。
  • sparse

    : 布爾值,是否在屬性中定義一個稀疏索引。
var schema2 = new Schema({
  test: {
    type: String,
    index: true,
    unique: true // 如果指定`unique`為true,則為唯一索引
  }
});
           

複制

字元串

  • lowercase

    : 布爾值,是否在儲存前對此值調用

    toLowerCase()

  • uppercase

    : 布爾值,是否在儲存前對此值調用

    toUpperCase()

  • trim

    : 布爾值,是否在儲存前對此值調用

    trim()

  • match

    : 正則,建立一個驗證器,驗證值是否比對給定的正規表達式
  • enum

    : 數組,建立一個驗證器,驗證值是否是給定數組中的元素

數字

  • min

    : 數字,建立一個驗證器,驗證值是否大于等于給定的最小值
  • max

    : 數字,建立一個驗證器,驗證值是否小于等于給定的最大的值

日期

  • min

    : Date
  • max

    : Date

現在已經介紹完

Schematype

,接下來讓我們看一下

Connections

連接配接(Connections)

我們可以通過利用

mongoose.connect()

方法連接配接 MongoDB 。

mongoose.connect('mongodb://localhost:27017/myapp');
           

複制

這是連接配接運作在本地

myapp

資料庫最小的值(

27017

)。如果連接配接失敗,嘗試用

127.0.0.1

代替

localhost

當然,你可在 uri 中指定更多的參數:

mongoose.connect('mongodb://username:password@host:port/database?options...');
           

複制

操作緩存

意思就是我們不必等待連接配接建立成功就可以使用 models,mongoose 會先緩存 model 操作

let TestModel = mongoose.model('Test', new Schema({ name: String }));
// 連接配接成功前操作會被挂起
TestModel.findOne(function(error, result) { /* ... */ });

setTimeout(function() {
  mongoose.connect('mongodb://localhost/myapp');
}, 60000);
           

複制

如果要禁用緩存,可修改

bufferCommands

配置,也可以全局禁用 bufferCommands

mongoose.set('bufferCommands', false);
           

複制

選項

connect 方法也接收一個 options 對象:

mongoose.connect(uri, options);
           

複制

這裡我列舉幾個在日常使用中比較重要的選項,完整的連接配接選項看這裡

  • bufferCommands

    :這是 mongoose 中一個特殊的選項(不傳遞給 MongoDB 驅動),它可以禁用 mongoose 的

    緩沖機制

  • user/pass

    :身份驗證的使用者名和密碼。這是 mongoose 中特殊的選項,它們可以等同于 MongoDB 驅動中的

    auth.user

    auth.password

    選項。
  • dbName

    :指定連接配接哪個資料庫,并覆寫連接配接字元串中任意的資料庫。
  • useNewUrlParser

    :底層 MongoDB 已經廢棄目前連接配接字元串解析器。因為這是一個重大的改變,添加了 useNewUrlParser 标記如果在使用者遇到 bug 時,允許使用者在新的解析器中傳回舊的解析器。
  • poolSize

    :MongoDB 驅動将為這個連接配接保持的最大 socket 數量。預設情況下,poolSize 是 5。
  • useUnifiedTopology

    :預設情況下為

    false

    。設定為 true 表示選擇使用 MongoDB 驅動程式的新連接配接管理引擎。您應該将此選項設定為 true,除非極少數情況會阻止您保持穩定的連接配接。

示例:

const options = {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  autoIndex: false, // 不建立索引
  reconnectTries: Number.MAX_VALUE, // 總是嘗試重新連接配接
  reconnectInterval: 500, // 每500ms重新連接配接一次
  poolSize: 10, // 維護最多10個socket連接配接
  // 如果沒有連接配接立即傳回錯誤,而不是等待重新連接配接
  bufferMaxEntries: 0,
  connectTimeoutMS: 10000, // 10s後放棄重新連接配接
  socketTimeoutMS: 45000, // 在45s不活躍後關閉sockets
  family: 4 // 用IPv4, 跳過IPv6
};
mongoose.connect(uri, options);
           

複制

回調

connect()

函數也接收一個回調參數,其傳回一個 promise。

mongoose.connect(uri, options, function(error) {
  // 檢查錯誤,初始化連接配接。回調沒有第二個參數。
});

// 或者用promise
mongoose.connect(uri, options).then(
  () => { /** ready to use. The `mongoose.connect()` promise resolves to undefined. */ },
  err => { /** handle initial connection error */ }
);
           

複制

說完

Connections

,下面讓我們來看一個重點

Models

模型(Models)

Models

是從

Schema

編譯來的構造函數。它們的執行個體就代表着可以從資料庫儲存和讀取的

documents

。從資料庫建立和讀取 document 的所有操作都是通過

model

進行的。

const mongoose = require("mongoose");

const { Schema, model } = mongoose;

const answerSchema = new Schema(
  {
    __v: { type: Number, select: false },
    content: { type: String, required: true },
  },
  { timestamps: true }
);

module.exports = model("Answer", answerSchema);

           

複制

定義好 model 之後,就可以進行一些增删改查操作了

建立

如果是

Entity

,使用

save

方法;如果是

Model

,使用

create

方法或

insertMany

方法。

// save([options], [options.safe], [options.validateBeforeSave], [fn])
let Person = mongoose.model("User", userSchema);
let person1 = new Person({ name: '森林' });
person1.save()

// 使用save()方法,需要先執行個體化為文檔,再使用save()方法儲存文檔。而create()方法,則直接在模型Model上操作,并且可以同時新增多個文檔
// Model.create(doc(s), [callback])
Person.create({ name: '森林' }, callback)

// Model.insertMany(doc(s), [options], [callback])
Person.insertMany([{ name: '森林' }, { name: '之晨' }], function(err, docs) {

})
           

複制

說到這裡,我們先要補充說明一下 mongoose 裡面的三個概念:

schema

model

entity

:

  • schema

    : 一種以檔案形式存儲的資料庫模型骨架,不具備資料庫的操作能力
  • model

    : 由 schema 釋出生成的模型,具有抽象屬性和行為的資料庫操作對
  • entity

    : 由 Model 建立的實體,他的操作也會影響資料庫
Schema、Model、Entity 的關系請牢記:

Schema生成Model,Model創造Entity

,Model 和 Entity 都可對資料庫操作造成影響,但 Model 比 Entity 更具操作性。

查詢

對于 Mongoosecha 的查找文檔很容易,它支援豐富的查詢 MongoDB 文法。包括

find

findById

findOne

等。

find()

第一個參數表示查詢條件,第二個參數用于控制傳回的字段,第三個參數用于配置查詢參數,第四個參數是回調函數,回調函數的形式為

function(err,docs){}

Model.find(conditions, [projection], [options], [callback])
           

複制

下面讓我們依次看下 find()的各個參數在實際場景中的應用:

  • conditionsModel.find({})Model.find({name:'森林'})對比相關操作符符号描述eq與指定的值相等ne與指定的值不相等gt大于指定的值gte大于等于指定的值lt小于指定的值lte小于等于指定的值in與查詢數組中指定的值中的任何一個比對nin與查詢數組中指定的值中的任何一個都不比對Model.find({ age: { in: [18, 24]} })傳回 age 字段等于 18 或者 24 的所有 document。邏輯相關操作符符号描述and滿足數組中指定的所有條件nor不滿足數組中指定的所有條件or滿足數組中指定的條件的其中一個not反轉查詢,傳回不滿足指定條件的文檔// 傳回 age 字段大于 24 或者 age 字段不存在的文檔Model.find( { age: { not: { lte: 24 }}})字段相關操作符符号描述exists比對存在指定字段的文檔type傳回字段屬于指定類型的文檔數組字段的查找符号描述all比對包含查詢數組中指定的所有條件的數組字段elemMatch比對數組字段中的某個值滿足 elemMatch 中指定的所有條件size比對數組字段的 length 與指定的大小一樣的 document// 使用 all 查找同時存在 18 和 20 的 documentModel.find({ age: {
    • 使用操作符
    • 精确查找
    • 查找全部
  • projection

    指定要包含或排除哪些

    document

    字段(也稱為查詢“

    投影

    ”),必須同時指定包含或同時指定排除,不能混合指定,

    _id

    除外。

    在 mongoose 中有兩種指定方式,

    字元串指定

    對象形式指定

    字元串指定時在排除的字段前加 - 号,隻寫字段名的是包含。

    Model.find({},'age');

    Model.find({},'-name');

    對象形式指定時,

    1

    是包含, 是排除。

    Model.find({}, { age: 1 });

    Model.find({}, { name: 0 });

  • options

    // 三種方式實作

    Model.find(filter,null,options)

    Model.find(filter).setOptions(options)

    Model.find(filter).<option>(xxx)

    options 選項見官方文檔 Query.prototype.setOptions()。

    這裡我們隻列舉常用的:

    // sort 兩種方式指定排序

    Model.find().sort('age -name'); // 字元串有 - 代表 descending 降序

    Model.find().sort({age:'asc', name:-1});

    sort

    limit

    同時使用時,調用的順序并不重要,傳回的資料都是先排序後限制數量。

    // 效果一樣

    Model.find().limit(2).sort('age');

    Model.find().sort('age').limit(2);

    • sort

      : 按照排序規則根據所給的字段進行排序,值可以是 asc, desc, ascending, descending, 1, 和 -1。
    • limit

      : 指定傳回結果的最大數量
    • skip

      : 指定要跳過的文檔數量
    • lean

      : 傳回普通的 js 對象,而不是

      Mongoose Documents

      。建議不需要 mongoose 特殊處理就返給前端的資料都最好使用該方法轉成普通 js 對象。
  • callback

    Mongoose 中所有傳入

    callback

    的查詢,其格式都是

    callback(error, result)

    這種形式。如果出錯,則 error 是出錯資訊,result 是 null;如果查詢成功,則 error 是 null, result 是查詢結果,查詢結果的結構形式是根據查詢方法的不同而有不同形式的。

    find()

    方法的查詢結果是數組,即使沒查詢到内容,也會傳回 [] 空數組。

findById

Model.findById(id,[projection],[options],[callback])
           

複制

Model.findById(id)

相當于

Model.findOne({ _id: id })

看一下官方對于

findOne

findById

的對比:

不同之處在于處理 id 為

undefined

時的情況。

findOne({ _id: undefined })

相當于

findOne({})

,傳回任意一條資料。而

findById(undefined)

相當于

findOne({ _id: null })

,傳回

null

查詢結果:

  • 傳回資料的格式是

    {}

    對象形式。
  • id 為

    undefined

    null

    ,result 傳回

    null

  • 沒符合查詢條件的資料,result 傳回

    null

findOne

該方法傳回查找到的所有執行個體的第一個

Model.findOne(conditions, [projection], [options], [callback])
           

複制

如果查詢條件是

_id

,建議使用

findById()

查詢結果:

  • 傳回資料的格式是

    {}

    對象形式。
  • 有多個資料滿足查詢條件的,隻傳回第一條。
  • 查詢條件 conditions 為 {}、 null 或 undefined,将任意傳回一條資料。
  • 沒有符合查詢條件的資料,result 傳回 null。

更新

每個模型都有自己的更新方法,用于修改資料庫中的文檔,不将它們傳回到您的應用程式。常用的有

findOneAndUpdate()

findByIdAndUpdate()

update()

updateMany()

等。

findOneAndUpdate()

Model.findOneAndUpdate(filter, update, [options], [callback])
           

複制

  • filter

    查詢語句,和

    find()

    一樣。

    filter 為

    {}

    ,則隻更新第一條資料。
  • update{operator: { field: value, ... }, ... }必須使用 update 操作符。如果沒有操作符或操作符不是 update 操作符,統一被視為 set 操作(mongoose 特有)字段相關操作符符号描述set設定字段值currentDate設定字段值為目前時間,可以是 Date 或時間戳格式。min隻有當指定值小于目前字段值時更新max隻有當指定值大于目前字段值時更新inc将字段值增加指定數量,指定數量可以是負數,代表減少。mul将字段值乘以指定數量unset删除指定字段,數組中的值删後改為 null。數組字段相關操作符符号描述充當占位符,用來表示比對查詢條件的數組字段中的第一個元素 {operator:{ "arrayField.addToSet向數組字段中添加之前不存在的元素 { addToSet: {arrayField: value, ... }},value 是數組時可與 each 組合使用。push向數組字段的末尾添加元素 { push: { arrayField: value, ... } },value 是數組時可與 each 等修飾符組合使用pop移除數組字段中的第一個或最後一個元素 { pop: {arrayField: -1(first) / 1(last), ... } }pull移除數組字段中與查詢條件比對的所有元素 { pull: {arrayField: value / condition, ... } }pullAll從數組中删除所有比對的值 { pullAll: { arrayField: [value1, value2 ... ], ... } }修飾符符号描述each修飾 push 和 addToSet 操作符,以便為數組字段添加多個元素。position修飾 push 操作符以指定要添加的元素在數組中的位置。slice修飾 push 操作符以限制更新後的數組的大小。sort修飾
    • 在指定的位置添加元素以更新數組字段
    • 按照指定的規則排序
    • 限制數組大小
    • 存儲數組
  • options

    • lean: true 傳回普通的 js 對象,而不是

      Mongoose Documents

    • new: 布爾值,

      true

      傳回更新後的資料,

      false

      (預設)傳回更新前的資料。
    • fields/select:指定傳回的字段。
    • sort:如果查詢條件找到多個文檔,則設定排序順序以選擇要更新哪個文檔。
    • maxTimeMS:為查詢設定時間限制。
    • upsert:布爾值,如果對象不存在,則建立它。預設值為

      false

    • omitUndefined:布爾值,如果為

      true

      ,則在更新之前删除值為

      undefined

      的屬性。
    • rawResult:如果為

      true

      ,則傳回來自 MongoDB 的原生結果。
  • callback

    • 沒找到資料傳回

      null

    • 更新成功傳回更新前的該條資料(

      {}

      形式)
    • options

      {new:true}

      ,更新成功傳回更新後的該條資料(

      {}

      形式)
    • 沒有查詢條件,即

      filter

      為空,則更新第一條資料

findByIdAndUpdate()

Model.findByIdAndUpdate(id, update, options, callback)
           

複制

Model.findByIdAndUpdate(id, update)

相當于

Model.findOneAndUpdate({ _id: id }, update)

result 查詢結果:

  • 傳回資料的格式是

    {}

    對象形式。
  • id 為

    undefined

    null

    ,result 傳回

    null

  • 沒符合查詢條件的資料,result 傳回

    null

update()

Model.update(filter, update, options, callback)
           

複制

  • options

    • multi: 預設

      false

      ,隻更新第一條資料;為

      true

      時,符合查詢條件的多條文檔都會更新。
    • overwrite:預設為 false,即 update 參數如果沒有操作符或操作符不是 update 操作符,将會預設添加 set;如果為 true,則不添加 set,視為覆寫原有文檔。

updateMany()

Model.updateMany(filter, update, options, callback)
           

複制

更新符合查詢條件的所有文檔,相當于

Model.update(filter, update, { multi: true }, callback)

删除

删除常用的有

findOneAndDelete()

findByIdAndDelete()

deleteMany()

findByIdAndRemove()

等。

findOneAndDelete()

Model.findOneAndDelete(filter, options, callback)
           

複制

  • filter

    查詢語句和

    find()

    一樣
  • options

    • sort:如果查詢條件找到多個文檔,則設定排序順序以選擇要删除哪個文檔。
    • select/projection:指定傳回的字段。
    • rawResult:如果為

      true

      ,則傳回來自

      MongoDB

      的原生結果。
  • callback

    • 沒有符合

      filter

      的資料時,傳回

      null

    • filter

      為空或

      {}

      時,删除第一條資料。
    • 删除成功傳回

      {}

      形式的原資料。

findByIdAndDelete()

Model.findByIdAndDelete(id, options, callback)
           

複制

Model.findByIdAndDelete(id)

相當于

Model.findOneAndDelete({ _id: id })

  • callback

    • 沒有符合

      id

      的資料時,傳回

      null

    • id

      為空或

      undefined

      時,傳回

      null

    • 删除成功傳回

      {}

      形式的原資料。

deleteMany()

Model.deleteMany(filter, options, callback)
           

複制

  • filter

    删除所有符合

    filter

    條件的文檔。

deleteOne()

Model.deleteOne(filter, options, callback)
           

複制

  • filter

    删除符合

    filter

    條件的第一條文檔。

findOneAndRemove()

Model.findOneAndRemove(filter, options, callback)
           

複制

用法與

findOneAndDelete()

一樣,一個小小的差別是

findOneAndRemove()

會調用 MongoDB 原生的

findAndModify()

指令,而不是

findOneAndDelete()

指令。

建議使用

findOneAndDelete()

方法。

findByIdAndRemove()

Model.findByIdAndRemove(id, options, callback)
           

複制

Model.findByIdAndRemove(id)

相當于

Model.findOneAndRemove({ _id: id })

remove()

Model.remove(filter, options, callback)
           

複制

從集合中删除所有比對

filter

條件的文檔。要删除第一個比對條件的文檔,可将

single

選項設定為

true

看完

Models

,最後讓我們來看下在實戰中比較有用的

Populate

聯表(Populate)

Mongoose 的

populate()

可以

連表查詢

,即在另外的集合中引用其文檔。

Populate()

可以自動替換

document

中的指定字段,替換内容從其他

collection

中擷取。

refs

建立

Model

的時候,可給該

Model

中關聯存儲其它集合

_id

的字段設定

ref

選項。

ref

選項告訴

Mongoose

在使用

populate()

填充的時候使用哪個

Model

const mongoose = require("mongoose");

const { Schema, model } = mongoose;

const answerSchema = new Schema(
  {
    __v: { type: Number, select: false },
    content: { type: String, required: true },
    answerer: {
      type: Schema.Types.ObjectId,
      ref: "User",
      required: true,
      select: false
    },
    questionId: { type: String, required: true },
    voteCount: { type: Number, required: true, default: 0 }
  },
  { timestamps: true }
);

module.exports = model("Answer", answerSchema);


           

複制

上例中

Answer

model 的

answerer

字段設為 ObjectId 數組。ref 選項告訴 Mongoose 在填充的時候使用

User

model。所有儲存在

answerer

中的

_id

都必須是

User

model 中

document

_id

ObjectId

Number

String

以及

Buffer

都可以作為

refs

使用。但是最好還是使用

ObjectId

在建立文檔時,儲存

refs

字段與儲存普通屬性一樣,把

_id

的值賦給它就好了。

const Answer = require("../models/answers");

async create(ctx) {
  ctx.verifyParams({
    content: { type: "string", required: true }
  });
  const answerer = ctx.state.user._id;
  const { questionId } = ctx.params;
  const answer = await new Answer({
    ...ctx.request.body,
    answerer,
    questionId
  }).save();
  ctx.body = answer;
}
           

複制

populate(path,select)

填充document

const Answer = require("../models/answers");

const answer = await Answer.findById(ctx.params.id)
      .select(selectFields)
      .populate("answerer");
           

複制

被填充的

answerer

字段已經不是原來的

_id

,而是被指定的

document

代替。這個

document

由另一條

query

從資料庫傳回。

傳回字段選擇

如果隻需要填充

document

中一部分字段,可給

populate()

傳入第二個參數,參數形式即

傳回字段字元串

,同 Query.prototype.select()。

const answer = await Answer.findById(ctx.params.id)
      .select(selectFields)
      .populate("answerer", "name -_id");
           

複制

populate 多個字段

const populateStr =
      fields &&
      fields
        .split(";")
        .filter(f => f)
        .map(f => {
          if (f === "employments") {
            return "employments.company employments.job";
          }
          if (f === "educations") {
            return "educations.school educations.major";
          }
          return f;
        })
        .join(" ");
const user = await User.findById(ctx.params.id)
      .select(selectFields)
      .populate(populateStr);
           

複制

最後

到這裡本篇文章也就結束了,這裡主要是結合我平時的項目(

https://github.com/Jack-cool/rest_node_api

)中對于

mongoose

的使用做的簡單的總結。希望能給你帶來幫助

你真的了解mongoose嗎?