細嚼慢嚥 Mongoose 5

此文已由做者黃鍇受權網易雲社區發佈。
html

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。前端

前言

因爲Mongoose一直沒有中文文檔,加上如今市面上充斥着太多「快速上手」,不少中文文檔都只盲目介紹了Mongoose的API用法(可是如今升級後,不少API發生了變化),沒有作詳細介紹,致使不少人用了半天的Mongoose,仍是對其只知其一;不知其二(好比我!),所以寫了這篇文章,從如下三個角度聊聊Mongoose。web

  • 是什麼(what?)mongodb

  • 爲何?(why?)數據庫

  • 怎麼用(how?)編程

    • 快速上手api

    • 再深一點(這纔是細嚼慢嚥的關鍵哦)數組

本文閱讀可快,可慢全憑喜愛。但本着貪多嚼不爛的想法,仍是但願客觀慢慢品嚐。promise

是什麼(what)?


首先,必需要知道Mongoose是什麼緩存

Mongoose(狐獴)就是圖上這個小可愛。(學名:Suricata suricatta),頭尾長42-60釐米,是一種小型的哺乳動物……

好像有點跑題。固然,今天介紹的Mongoose不是真的小動物,而是一款在Node.js環境下對MongoDB API 進行便捷操做的對象模型工具(因此理論上,我默認你是須要大概懂MongoDB數據庫纔會來用這個的)。

要特別強調的是,我這裏說它是對象模型工具,說明Mongoose的操做是以對象爲單位的。

同時,因爲Mongoose升級後(差很少)全面支持Promise,能夠很方便的對接async/await語法。所以接下來的個人代碼都會默認使用Promise形式。

爲何(Why)?

咱們知道,MongoDB官網提供了一個Node.js環境下的MongoAPI,那爲何我還須要使用Mongoose呢?固然是由於它提供了一些官方不能提供的功能,如它官網介紹的:

Let's face it, writing MongoDB validation, casting and business logic boilerplate is a drag.

能夠看出,Mongoose主要是減輕咱們在寫數據驗證,業務邏輯模板的負擔。同時,因爲Mongoose一直強調它是一個MongoDB對象建模工具,所以在這裏就必需要提出Mongoose的核心,也是Mongoose困擾人已久的一個東西——模式和模型。

模式?模型?

對於Mongoose的新人來講,最頭疼的就是它裏面有關模式(Schema),模型(Model)的概念。Mongoose的一切都始於一個模式。這點和MongoDB基於集合(Collection)的思路可能不太一致,由於自己MongoDB是無模式的,也正是由於它的無模型,致使其使用起來極具靈活性。

那Mongoose爲何要定義一個模型?Why Define a Mongoose Schema? 給出瞭解釋:

have you ever wondered how to pull these random documents out and figure out which properties were saved to document? The truth is that for 90% of your web applications you’ll be using model classes and you’ll have a standard set of fields you’ll be expecting to be saved to your database

模式有模式的好處,生活中絕大部分的數據是遵循必定的模式的,模式能讓咱們規範數據的表現形式,不至於太爲所欲爲。同時模式給咱們提供了不少數據驗證的便利(這就像咱們討論JavaScript弱類型和TypeScript強類型是同樣同樣的)。

同時,因爲Mongoose使用的依然MongoDB而非關係型數據庫,所以它沒有徹底失去其天生的靈活性,它依然開放了讓你能夠定義任意字段的能力。

// Defining a schema on a Mongoose model// that allows storage of arbitrary fields// Mongoose.schema(schemaObject, options)var User = Mongoose.schema({
  username: String,
  password: String}, {
  strict: false});複製代碼

注意到上面代碼的第二個字段,你把strict設置爲false,你就能在這個模型中加入任意字段了(可是仍是不建議這樣作,由於這意味着你須要本身去作數據的驗證)。

若是你實在不習慣使用 模式,可使用MongoDB官方的API。

與其說Mongoose定義模式是畫蛇添足的,不如說,它在靈活性的基礎上,提供了更安全的模型化操做對象的方式。

我我的感受:使用Mongoose你更有操做對象的感受。

模式 VS 模型

咱們已經知道了模式是有其優點的,那爲何我還須要模型?這兩個有什麼區別?

官方的定義:Each schema defines the shape of the documents within that collection.

模式

模式它是一個 抽象的概念。其實能夠把模式考慮成設計規範 ,定義了設計圖紙應該如何畫,默認單位是多少,若是沒有該規範,那每一個人畫的設計圖紙就是各式各樣,無法真正投入建造。


模型

模型相對於模式是具體的概念,能夠把模型考慮成設計圖紙 :按照設計規範畫出一個圖紙,任何人根據該規範就能夠把房子造出來。

實例

而模型的實例纔是真正的咱們住的房子,在MongoDB裏也就是文檔(document)


畫蛇添足?

你可能會說,何須畫蛇添足,把模式和模型合起來不就行了嘛?不少人喜歡這樣寫:

const studentModel = Mongoose.model('Student', new Mongoose.Schema({ name: String, sex: String});複製代碼

這樣寫也能夠,可是這個前提是你肯定你的模式只在這一個模型中使用。之因此有個模式這個抽象概念,就像設計規範, 它歷來不是說只能在一張圖紙中使用,你能夠利用這個規範繪製出不少符合規範的圖紙。

有時候你可能會有這樣的需求,基本同樣的數據結構,可是我須要建多個不一樣集合,或者稍微修改一下模式,新建一個新的集合,這時候把模式獨立出來,你就會發現省事不少了。

const personSchema = new Mongoose.Schema({ name: String, sex: String});const studentModel = Mongoose.model('Student', personSchema);const otherStudentModel = Mongoose.model('OtherStudent', personSchema);const teacherSchema = personSchema.clone();
teacherSchema.add({ teach: String});const teacherModel = Mongoose.model('Teacher', teacherSchema);複製代碼

舉例

再來舉一個例子:假設,咱們創建了一個寵物貓咪樂園,咱們須要去招集一批寵物喵成爲咱們的會員。可是呢,咱們的宣傳員他沒見過喵(多麼神奇的生物)。因而,咱們絞盡腦汁,合計出一個規範:全部的寵物貓應該知足三個屬性name,type, meow。也就是說,寵物喵必需要有名字,必須有一個品種,同時它必需要會「喵喵」叫!

const catSchema = new Mongoose.Schema({ name: String, type: String,meow: Boolean});複製代碼

好了,如今咱們把規範告訴他,可是他說他記性很差。怕忘記,所以咱們制定了一個叫《寵物貓咪指南1.0》的手冊

const CatGuide = Mongoose.model('Animal', catSchema);複製代碼

好了,宣傳員開心的出去了,開始處處撩貓去了,把全部它認爲是貓的生物都記錄下來:

const kitty1 = new CatGuide({ name: 'mimi', type: 'American Shorthair', meow: true });const kitty2 = new CatGuide({ name: 'kaddy', type: 'American Shorthair', meow: true });const doggy = new CatGuide({ name: 'doggy', type: 'Dog!!', meow: false });
……複製代碼

(可是這個不靠譜的,拿着手冊也能找錯,找了一隻穿貓咪衣服的狗!!?)


所以,咱們只把那兩隻貓保存到數據庫裏。

kitty1.save().then(() => console.log('meow'));
kitty2.save().then(() => console.log('meow'));複製代碼

知道了他們之間的關係,剩下的就好辦了。就是編程了,編程能解決的事情都是簡單的事情。

怎麼用(How)?

快速開始

Mongoose的使用流程很簡單:

鏈接 ——> 定義模式 ——> 生成Model ——> 生成Model實例複製代碼

其中,鏈接,Model,實例是實際跟數據庫打交道的,他們之間的關係以下:

經過一個很簡單的例子,咱們就能夠上手操做MongoDB,而後配合官方API,熟練的人能夠直接跳過了,可是若是你想了解更多細節,能夠繼續看下去,看看每一個操做具體還能夠怎麼配置。

// step 1 引入Mongooseconst Mongoose = require('Mongoose');// setp2 創建鏈接Mongoose.connect('MongoDB://localhost/test');// step3 創建模式const catSchema = new Mongoose.Schema({ name: String, type: String });// step4 生成模型const Cat = Mongoose.model('Cat', catSchema);// step5.1 模型直接操做documentCat.create({ name: 'mimi', type: 'American Shorthair' }).then(...)// or
                                                              // step5.2 生成實例,操做documentconst kitty = new Cat({ name: 'mimi', type: 'American Shorthair' });
kitty.save().then(() => console.log('meow'));複製代碼

鏈接

咱們都知道,使用數據庫的第一步是須要連上數據庫,Mongoose提供了一個很簡單的方式鏈接:connect。

Mongoose.connect(url, [options]);複製代碼

注意,如今使用url須要加上 { useNewUrlParser: true }選項,不然可能會報錯。 Mongoose.connect(url, { useNewUrlParser: true });

其中url參數的完整形式,跟MongoDB鏈接字符串的一致:MongoDB://username:password@host:port/database?options...

默認狀況下,用戶名和密碼能夠省略:

Mongoose.connect('MongoDB://localhost/db1');複製代碼

若是還須要傳遞用戶名、密碼,則可使用以下方式

Mongoose.connect('MongoDB://username:password@host:port/database?options...');複製代碼

connect()方法還接受一個選項對象options,該對象將傳遞給底層驅動程序。這裏所包含的全部選項優先於鏈接字符串中傳遞的選項:

  • bufferCommands : 這個選項會關閉Mongoose的緩存機制 ,再深一點章節會說。

  • user/pass :用戶名和密碼

  • autoIndex :一般MongoDB會自動生成索引,可是有時候若是你不須要,能夠把這個選項設置爲false

  • dbName :制定數據庫名,通常是沒辦法在鏈接字符串中指定的情形( MongoDB+srv 語法鏈接到 MongoDB Atlas

還有一些選項是用來配置Mongoose的,例如,我以前說的useNewUrlParser ,還有 pooleSize, bufferMaxEntries,connectTimeoutMS, socketTimeoutMS…… 這些都是比較高級的配置,通常人用不到。若是真的須要用到,能夠翻官方文檔

模式

咱們知道,模式其的是規範的做用,所以定義模式主要是定義咱們數據的表現形式以及作一些格式約定,方便自動校驗。例如,我建立了一個學生的模式,它規定了name和age字段的類型:

const studentSchema = new Mongoose.Schema({
  name: String,
  age: Number,
});複製代碼

基本類型

模式有如下10種基本類型,若是隻使用基本類型,就輸入如下字段就行

  • String | Number | Date | Buffer | Boolean | Schema.Types.Mixed | Schema.Types.ObjectId | Array | Schema.Types.Decimal128 | Map(新加入的ES6的Map)

注意:1. 若是是Array,可使用[]代替,你能夠能夠規定Array內部數據類型,例如數字數組: [Number]。2. 不少人困惑爲何沒有Object類型,若是你想定義對象,使用Schema.Types.Mixed就好了。Mixed類型支持多種類型混合,好比數組套對象,對象套數組……

額外約束

可是,只規定了基本類型還不夠,我想作一些額外的約束,好比姓名長度,年齡大小。所以每種類型還有不少配置項,若是要使用額外的約束,須要使用Object形式,同時,類型經過type引入:

const studentSchema = new Mongoose.Schema({
  name: { type: String, minlength: 2 },
  age: { type: Number, min: 12, max: 16},
});複製代碼

這裏只介紹一些比較經常使用的,更多使用說明參考官方手冊

全部類型可用:

  • required: boolean or function, 很好理解,若是爲真就表示該字段必需要存在

  • default: Any or function, 設置默認值

  • validate: function, 增長一個校驗函數

字符串 String:

  • lowercase/upppercase: boolean, 是否作大小寫處理

  • trim: boolean, 是否去除首尾空格

  • match: RegExp, 只有知足特定正則的值才能被接受

  • enum: Array, 只有數組內的值才被接受

  • minlength/ maxLength: Number, 最小、大長度

數字 Number / 日期 Date

  • min / max: Number / Date (根據類型),必須知足的最大值/最小值

注意: Date使用的是內置的Date,可是你使用setMonth等內置的方法,不會被Mongoose不會識別到,所以使用doc.save()不會有效果。你必須設置doc.markModified(pathToYourDate)。

const Assignment = Mongoose.model('Assignment', { name: String, dueDate: Date });const a = new Assignment({ name: 'test', dueDate: Date.now() });
a.save().then(callback);
Assignment.findOne({ name: 'test' }, 'name dueDate', (err, doc) => {
doc.dueDate.setMonth(3);
doc.save(callback); // THIS DOES NOT SAVE YOUR CHANGEdoc.markModified('dueDate');
doc.save(callback); // works});複製代碼

模型

有了模式以後,就能夠生成模型了,生成模型很簡單,就使用model函數就好了。

// step3 創建模式const catSchema = new Mongoose.Schema({ name: String, type: String });// step4 生成模型const Cat = Mongoose.model('Cat', catSchema);複製代碼

注意,雖然全部的例子都是Mongoose.model,可是實際上model是Connect原型的方法:Connection.prototype.model(),即一個鏈接實例纔有model方法。(具體的能夠看後面的 再深一點章節)

其中,model第一個參數就是collection名稱(Mongoose會自動 複數化,想修更名稱也請看後面章節),同時model會把Schema裏的內容複製一份

官方原文是說複製一份

The .model() function makes a copy of schema. Make sure that you've added everything you want to schema before calling .model()!

可是我實際測試,感受仍是引用,在調用model方法以後,經過add(), remove()方法修改Schema依然會影響以前建立的model。

const catSchema = new Mongoose.Schema({ name: String, type: String });const Cat = Mongoose.model('Cat', catSchema);
catSchema.remove('type');   // 在建立model後修改Schema會影響以前的modelconst kitty = new Cat({ name: 'mimi2', type: 'American Shorthair' });
kitty.save().then(() => console.log('meow'));  // type字段不會存儲到數據庫複製代碼

實例

根據以前的圖能夠知道,model的實例就是文檔,有一些比較方便的操做方法,更多方法參考文檔

get(path,opt) : 獲取文檔的某個字段,這個函數比較好的是作數據處理,第2個參數能夠將該字段轉換成其餘類型:«Schema|String|Number|Buffer|*»

doc.get('age') // 47

  // dynamic casting to a string
  doc.get('age', String) // "47"複製代碼

save():顧名思義保存文檔到數據庫

var Tank = Mongoose.model('Tank', yourSchema);var small = new Tank({ size: 'small' });
small.save(function (err) {  if (err) return handleError(err);  // saved!});複製代碼

set(path, value, opt) : 設置文檔的某個字段,第三個參數依然能夠修改字段類型,同時,能夠設置{ strict: false }添加任意字段(回顧 爲何章節)

// 簡單用法doc.set(path, value)// 對象形式用法,修改多個字段doc.set({
    path  : value
  , path2 : {
       path  : value
    }
})// 把value經過Number進行轉換doc.set(path, value, Number)// 填加任意字段doc.set(path, value, { strict: false });複製代碼

CRUD操做

因爲對文檔的操做一般是在"集合"這個維度,所以大部分的操做都是在model上完成的,除了一些單文檔的操做能夠在文檔這個維度,經過實例完成

新增文檔[s]

文檔保存有三種方法,除了以前介紹的save方法,還可使用模型的下面兩種方法。

  • model.create(object) : 其實就是new model.save()的簡寫

  • model.insertMany([object]) : 能夠批量增長,從內存寫到數據庫比較實用

區別就像實例的save是貓主人本身跑過來報名。create,insertMany就是咱們工做人員替用戶直接錄入。

const catSchema = new Mongoose.Schema({ name: String, type: String });const Cat = Mongoose.model('Cat', catSchema);
Cat.create({ name: 'mimi', type: 'American Shorthair' }).then(...)  // 插入單條文檔Cat.insertMany([{ name: 'mimi', type: 'American Shorthair' }...]).then(...)  // 插入多條文檔複製代碼

查找文檔

Mongoose的查刪改的使用方法基本大同小異,這裏重點介紹一下查詢,剩下的更新和刪除主要介紹方法,不會具體介紹怎麼使用

跟查詢有關的方法有…一堆(ByID那一批我排除了,實際用途不大,不少時候咱們都不知道文檔的_id):

大部分都大同小意,而後findOne跟find的差異就是,一個是查詢全部知足條件的文檔,一個是隻返回第一個,所以我這裏只講一個find的用法:

Modle.find.(conditions, [projection], [options], [callback] )複製代碼
  • conditions «Object» : 查詢條件(徹底跟MongoDB手冊一致)

  • [projection] «Object|String» : 選取字段,和下面的select方法徹底一致(我推薦使用鏈式調用)

  • [options] «Object» : 查詢配置(在再深一點章節再介紹)

  • [callback] «Function» : 推薦使用Promise寫發,不寫callback

通常來講,查找MongoDB中的某個文檔,是須要一個查詢條件(condition)的,這個查詢條件能夠簡單能夠複雜,因爲徹底跟MongoDB手冊一致,本文畢竟不是《MongoDB入門》這裏就不作過多敘述,若是後續有時間會把整理的MongoDB手冊發上來, 下面的例子也會說明一些。

這裏重點說一下Mongoose自身提供的一些比較使用的方法,這裏舉官方一個例子來講明一下:

一般一個標準的MongoDB的查詢大概是這樣(其中Person是一個model),Mongoose徹底支持這種查詢方式:

Person.
  find({    // 這裏寫查詢條件
    occupation: /host/,   // 查詢條件能夠用正則
    'name.last': 'Ghost',   //  限定name.last 
    age: { $gt: 17, $lt: 66 },   // 限定年齡範圍
    likes: { $in: ['vaporizing', 'talking'] }   // 限定喜愛必需要在這個數組中
  }).
  limit(10).    // 限定取出的文檔條數
  sort({ occupation: -1 }).  // 針對某個字段排序,1表示正序,-1表示逆序
  select({ name: 1, occupation: 1 }).  // 選擇取出的字段,1表示取出,0表示不取出,若是不須要id,須要顯式寫{_id : 0}
  exec(callback);  // 這裏使用then也行,徹底支持Promise,可是查詢不是Promise,這個要注意,後面會說複製代碼

可是Mongoose把 經常使用的方法都提取出來了,例如:where,gt,lt,in……(完整API文檔),所以更推薦使用下面這種方式:

// Using query builderPerson.
  find({ occupation: /host/ }).
  where('name.last').equals('Ghost').
  where('age').gt(17).lt(66).
  where('likes').in(['vaporizing', 'talking']).
  limit(10).
  sort('-occupation').
  select('name occupation').
  exec(callback);複製代碼

要強調一下,查詢支持promise一些語法,可是它不是promise。這個坑必需要記得!! 千萬不要將callback形式和promise混用!

例如,下面的query能夠執行三次!並不會由於一次then或者callback結束。 說明then只是一個執行方法,查詢並非真正的Promise。

const q = MyModel.updateMany({}, { isDeleted: true }, function() {  console.log('Update 1');
});
q.then(() => console.log('Update 2'));
q.then(() => console.log('Update 3'));複製代碼

若是要統計查詢的文檔數據,可使用countDocuments(),用法和MongoDB的count()一致

Person.find(...)
.count()
.then() // 這裏得到的是一個數字複製代碼

注意: Mongoose的count()已經被廢棄,須要統計個數得用countDocuments

更新文檔

更新文檔的方法有下面三個,condition的使用方法和find的方法一徹底一致(包括where那些查詢方法均可以使用),只是要注意, update只是更新字段(符合Schema),可是replace是徹底的用新doc來替換。

刪除文檔

刪除文檔主要是兩個方法,使用方法徹底和查詢一致。

再深一點

細品鏈接

緩存機制

根據上面的例子,你會發現一個奇怪的現象, 咱們並無判斷鏈接是否成功就進行了操做?這是由於Mongoose內部緩存了函數調用。

Mongoose lets you start using your models immediately, without waiting for Mongoose to establish a connection to MongoDB

這很cool,很方便,咱們不用去關心鏈接成功再去增長文檔,不然你必需要這樣:

Mongoose
    .connect('MongoDB://localhost/test')
    .then(() => {        const catSchema = new Mongoose.Schema({ name: String, type: String });        const Cat = Mongoose.model('Cat', catSchema);        const kitty = new Cat({ name: 'mimi', type: 'American Shorthair' });
        kitty.save().then(() => console.log('meow'));
    })
    .catch(err => console.log(err));複製代碼

可是這樣會帶來一個問題就是你若是不創建鏈接,進行操做也不會收到任何提示。

var MyModel = Mongoose.model('Test', new Schema({ name: String }));// Will just hang until Mongoose successfully connectsMyModel.findOne(function(error, result) { /* ... */ });

setTimeout(function() {
  Mongoose.connect('MongoDB://localhost:27017/myapp');
}, 60000);複製代碼

因此必定要養成良好的喜歡,在作查詢,刪除等操做的時候,判斷一下是否已經鏈接。判斷鏈接可使用下面提的 connection對象的鏈接狀態碼

connection對象

說實話,剛開始使用Mongoose的時候我老是充滿疑惑,一開始我認爲Mongoose應該是一個類,它表明一次鏈接,經過new的方式能夠建立一個數據庫的鏈接。可是Mongoose不是,它直接經過connect方法就建立了一個鏈接,就能直接操做數據庫上的集合,那說明這個Mongoose只是一個實例(的確也是)。那若是Mongoose只是一個實例,它就等於那一個鏈接嗎?那爲何Mongoose上面沒有操做數據庫的方法?並且我要如何建立多個鏈接呢?一個個問題在腦海中迴盪,腦子裏早已一團漿糊,因而,仍是回到了官方文檔找答案。

其實,每次執行Mongoose.connect,實際就是建立了一個Connection對象(這個對象能夠經過Mongoose.connection得到)這個對象纔是真正對應的一個數據庫的鏈接。所以,全部的數據庫操做其實都是在connect對象中,例如:createCollection()dropCollection(), dropDatabase() ……

這個connection對象同時能夠監聽幾個經常使用事件:connected,disconnected,error。

const connection = Mongoose.connection;
connection.on('connected', () => {  console.log('Mongoose connection open');
  connection.createCollection('test').then(() => console.log('add collection')),
});複製代碼

鏈接狀態碼

爲了方便查看對象的狀態,connection也提供了一個readyState屬性,能夠方便判斷當前鏈接狀態,經過Mongoose.STATES能獲取readyState的全部取值:

{ '0': 'disconnected',
  '1': 'connected',
  '2': 'connecting',
  '3': 'disconnecting',
  '99': 'uninitialized',
  disconnected: 0,
  connected: 1,
  connecting: 2,
  disconnecting: 3,
  uninitialized: 99 }複製代碼

因此,理論上你能夠這樣判斷鏈接是否創建

if (Mongoose.STATES[connection.readyState] === 'connected') ....複製代碼

多個鏈接

同時,若是你想建立多個鏈接,你可使用Mongoose.createConnection()的方式建立一個新的鏈接,參數什麼的和connect一致,剩下的操做一致。例如如下這個官方例子:

var Mongoose = require('Mongoose');var db = Mongoose.createConnection(..);
db.model('Venue', new Schema(..));var Ticket = db.model('Ticket', new Schema(..));var Venue = db.model('Venue');複製代碼

細品模式

虛屬性

虛屬性(Virtuals)是Mongoose提供的一個頗有用的功能,它能夠方便咱們存儲一些不須要存儲在數據庫中的數據。例如,我存儲一個用戶的姓名,只須要存名和姓:

const personSchema = new Schema({
    name: {
      first: String,
      last: String
    }
  });  const Person = Mongoose.model('Person', personSchema);  const voidsky = new Person({
    name: { first: 'void', last: 'sky' }
  });複製代碼

可是我在讀出的時候,須要打印用戶的全名,固然咱們能夠這樣作:

console.log(voidsky.name.first + ' ' + voidsky.name.last); // void sky複製代碼

可是虛屬性提供了一個更方便的方式:

personSchema.virtual('fullName').get(function () {  return this.name.first + ' ' + this.name.last;
});console.log(voidsky.fullName);複製代碼

若是你用過一些前端框架,你能夠把它看成計算屬性。可是這個屬性不會被持久化到MongoDB中,虛函數有get和set的方法,熟悉setter和getter的就應該知道,同樣同樣的。

虛屬性還提供了一個頗有用的Aliases字段,它能夠把Schema的某個屬性徹底跟一個虛屬性進行鏈接(set和get的綁定),你操做這兩個屬性的效果徹底一致,這個好處是你能夠在存儲的時候節約空間,可是獲取數據的時候,使用語義化的描述 。只是Aliases定義的屬性不會被持久化到數據庫。這裏看官方案例:

const personSchema = new Schema({
  n: {
    type: String,
    alias: 'name'   // 注意這裏,等於建立了一個name的虛屬性,綁定到n
  }
});const Person = Mongoose.model(personSchema);const person = new Person({ name: 'Val' });console.log(person); // { n: 'Val' }console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }console.log(person.name); // "Val"person.name = 'Not Val';console.log(person); // { n: 'Not Val' }複製代碼

子模式

schema是能夠嵌套的,若是咱們須要定義一個複雜的結構,可使用嵌套的方式(一般子模式會配置成{ _id: false },具體能夠參考下面的模式配置)

const childSchema = new Schema({
  n: {
    type: String,
    alias: 'name'
  }
}, { _id: false });const parentSchema = new Schema({  // If in a child schema, alias doesn't need to include the full nested path c: childSchema, name: { f: { type: String, // Alias needs to include the full nested path if declared inline alias: 'name.first' } } });複製代碼

模式配置

schema是有不少配置的,你能夠在定義Schema的時候配置,或者使用set方法。

new Schema({..}, options);// orconst schema = new Schema({..});
schema.set(option, value);複製代碼

相關配置能夠參考文檔,這裏舉例幾個可能會用到的:

  • colleciton 這個配置能夠修改collection的名字(默認是使用model的複數化名稱,見下面)

const dataSchema = new Schema({..}, { collection: 'data' });const dataModel = Mongoose.model('othername', dataSchema);複製代碼

模式方法

(未完待續)

額外推薦:可視化客戶端

固然,你能夠經過命令行查看結果,可是更多人仍是習慣使用圖形化的界面,這裏推薦一個MongoDB的可視化客戶端:robot 3T,這就是原來大名鼎鼎的RoboMongo。全平臺都支持。要是以前我還真不想推薦它,由於在mac上顯示實在難受,不支持retina屏幕。

可是如今,已經好了不少


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點擊




相關文章:
【推薦】 Spring Cloud使用總結

相關文章
相關標籤/搜索