Mongoose API總結

Mongoose裏面最重要的三個概念是Schemas,Models和Documents,另外還有驗證,中間件等,前面三個相當重要,這裏像填充Populate鑑別器Discriminators的相關知識就不介紹了,平時基本上不太能用到,具體的能夠查閱官方文檔。javascript

Mongoose的一切始於SchemaSchema定義collection裏的文檔的構成,models是從Schema編譯來的構造函數,它們的實例就表明着能夠從數據庫保存和讀取的documents,從數據庫建立和讀取document的操做都是經過model進行的,而保存時咱們是在model實例上進行的。css

var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);

var small = new Tank({ size: 'small' });
small.save(function (err) {
  if (err) return handleError(err);
  // saved!
})

// or

Tank.create({ size: 'small' }, function (err, small) {
  if (err) return handleError(err);
  // saved!
})
複製代碼

模式Schemas

  1. 如何爲models實例(Documents)定義咱們本身的方法 雖然douments有不少自帶的實例方法,但咱們也能夠自定義咱們本身的方法,這時須要在schema上定義方法。
var animalSchema = new Schema({ name: String, type: String });

  animalSchema.methods.findSimilarTypes = function(cb) {
    return this.model('Animal').find({ type: this.type }, cb);
  };
  
  var Animal = mongoose.model('Animal', animalSchema);
  var dog = new Animal({ type: 'dog' });

  dog.findSimilarTypes(function(err, dogs) {
    console.log(dogs); // woof
  });
複製代碼

也能夠添加靜態方法html

animalSchema.statics.findByName = function(name, cb) {
    return this.find({ name: new RegExp(name, 'i') }, cb);
  };

  var Animal = mongoose.model('Animal', animalSchema);
  Animal.findByName('fido', function(err, animals) {
    console.log(animals);
  });
複製代碼

2.Schemas有不少的配置選項,能夠在構造時或者直接setjava

new Schema({..}, options);

// or

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

模型Models

查詢

查詢文檔能夠用model的find,findById,findOne和where這些靜態方法正則表達式

刪除

remove方法mongodb

更新

model的update方法能夠修改數據庫中的文檔,不過不會把文檔返回數據庫

findOneAndUpdate方法用來更新單獨一條文檔而且返回給應用層api

文檔Documents

檢索

檢索方法較多,暫不闡述數組

更新

更新的方法也比較多promise

  • findById+改變值得操做

    Tank.findById(id, function (err, tank) {
      if (err) return handleError(err);
    
      tank.size = 'large';//或者使用tank.set({ size: 'large' });
      tank.save(function (err, updatedTank) {
        if (err) return handleError(err);
        res.send(updatedTank);
      });
    });
    複製代碼
  • update

    Tank.update({ _id: id }, { $set: { size: 'large' }}, callback);
    複製代碼
  • findByIdAndUpdate 這個方法會返回文檔

    Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true }, function (err, tank) {
      if (err) return handleError(err);
      res.send(tank);
    });
    複製代碼
  • 其餘方法如findAndUpdate/Remove查找並返回最多一個文檔

驗證

Document會在被保存以前驗證

覆蓋

能夠用.set( )覆蓋整個文檔

Tank.findById(id, function (err, tank) {
  if (err) return handleError(err);
  // Now `otherTank` is a copy of `tank`
  otherTank.set(tank);
});
複製代碼

查詢queries

Model 的方法中包含查詢條件參數的( find findById count update )均可以按如下兩種方式執行:

  • 傳入 callback 參數,操做會被當即執行,查詢結果被傳給回調函數( callback )。

  • 不傳 callback 參數,Query 的一個實例(一個 query 對象)被返回,這個 query 提供了構建查詢器的特殊接口。Query 實例有一個 .then() 函數,用法相似 promise。

    傳callback參數的狀況:

var Person = mongoose.model('Person', yourSchema);

// 查詢每一個 last name 是 'Ghost' 的 person, select `name` 和 `occupation` 字段
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) {
  if (err) return handleError(err);
  // Prints "Space Ghost is a talk show host".
  console.log('%s %s is a %s.', person.name.first, person.name.last,
    person.occupation);
});
複製代碼

不傳callback參數的狀況:

// 查詢每一個 last name 是 'Ghost' 的 person
var query = Person.findOne({ 'name.last': 'Ghost' });

// select `name` 和 `occupation` 字段
query.select('name occupation');

// 而後執行查詢
query.exec(function (err, person) {
  if (err) return handleError(err);
  // Prints "Space Ghost is a talk show host."
  console.log('%s %s is a %s.', person.name.first, person.name.last,
    person.occupation);
});
複製代碼

模式類型SchemaTypes

有一下SchemaTypes:

  • String

  • Number

  • Date

  • Buffer

  • Boolean

  • Mixed

  • ObjectId

  • Array

  • Decimal128 能夠聲明schema type爲某一種type,或者賦值一個含有type屬性的對象

    var schema1 = new Schema({
      test: String // `test` is a path of type String
    });
    
    var schema2 = new Schema({
      test: { type: String } // `test` is a path of type string
    });
    複製代碼

    除了type屬性,如下有一些所有type可用的選項和一些限定部分type使用的選項

    所有可用
    • required: 布爾值或函數 若是值爲真,爲此屬性添加 required 驗證器
    • default: 任何值或函數 設置此路徑默認值。若是是函數,函數返回值爲默認值
    • select: 布爾值 指定 query 的默認 projections
    • validate: 函數 adds a validator function for this property
    • get: 函數 使用 Object.defineProperty() 定義自定義 getter
    • set: 函數 使用 Object.defineProperty() 定義自定義 setter
    • alias: 字符串 僅mongoose >= 4.10.0。 爲該字段路徑定義虛擬值 gets/sets
    var numberSchema = new Schema({
      integerOnly: {
        type: Number,
        get: v => Math.round(v),
        set: v => Math.round(v),
        alias: 'i'
      }
    });
    
    var Number = mongoose.model('Number', numberSchema);
    
    var doc = new Number();
    doc.integerOnly = 2.001;
    doc.integerOnly; // 2
    doc.i; // 2
    doc.i = 3.001;
    doc.integerOnly; // 3
    doc.i; // 3
    複製代碼
    索引相關
    • index: 布爾值 是否對這個屬性建立索引
    • unique: 布爾值 是否對這個屬性建立惟一索引
    • sparse: 布爾值 是否對這個屬性建立稀疏索引
var schema2 = new Schema({
  test: {
    type: String,
    index: true,
    unique: true // Unique index. If you specify `unique: true`
    // specifying `index: true` is optional if you do `unique: true`
  }
});
複製代碼
String
  • lowercase: 布爾值 是否在保存前對此值調用 .toLowerCase()
  • uppercase: 布爾值 是否在保存前對此值調用 .toUpperCase()
  • trim: 布爾值 是否在保存前對此值調用 .trim()
  • match: 正則表達式 建立驗證器檢查這個值是否匹配給定正則表達式
  • enum: 數組 建立驗證器檢查這個值是否包含於給定數組
Number
  • min: 數值 建立驗證器檢查屬性是否大於或等於該值
  • max: 數值 建立驗證器檢查屬性是否小於或等於該值
Date
  • min: Date
  • max: Date

驗證validation

  • 驗證定義於 SchemaType
  • 驗證是一個中間件。它默認做爲 pre('save')` 鉤子註冊在 schema 上
  • 你可使用 doc.validate(callback)doc.validateSync() 手動驗證
  • 驗證器不對未定義的值進行驗證,惟一例外是 required 驗證器
  • 驗證是異步遞歸的。當你調用 Model#save,子文檔驗證也會執行,出錯的話 Model#save 回調會接收錯誤
  • 驗證是可定製的
var schema = new Schema({
      name: {
        type: String,
        required: true
      }
    });
    var Cat = db.model('Cat', schema);

    // This cat has no name :(
    var cat = new Cat();
    cat.save(function(error) {
      assert.equal(error.errors['name'].message,
        'Path `name` is required.');

      error = cat.validateSync();
      assert.equal(error.errors['name'].message,
        'Path `name` is required.');
    });
複製代碼

內建validators

Mongoose有一些內建驗證器

自定義驗證器

自定義驗證器經過傳入一個檢驗函數來定義

var userSchema = new Schema({
      phone: {
        type: String,
        validate: {
          validator: function(v) {
            return /\d{3}-\d{3}-\d{4}/.test(v);
          },
          message: '{VALUE} is not a valid phone number!'
        },
        required: [true, 'User phone number required']
      }
    });

    var User = db.model('user', userSchema);
    var user = new User();
    var error;

    user.phone = '555.0123';
    error = user.validateSync();
    assert.equal(error.errors['phone'].message,
      '555.0123 is not a valid phone number!');

    user.phone = '';
    error = user.validateSync();
    assert.equal(error.errors['phone'].message,
      'User phone number required');

    user.phone = '201-555-0123';
    // Validation succeeds! Phone number is defined
    // and fits `DDD-DDD-DDDD`
    error = user.validateSync();
    assert.equal(error, null);
複製代碼

異步自定義驗證器

自定義檢驗器能夠是異步的。若是檢驗函數 返回 promise (像 async 函數), mongoose 將會等待該 promise 完成。 若是你更喜歡使用回調函數,設置 isAsync 選項, mongoose 會將回調函數做爲驗證函數的第二個參數。

var userSchema = new Schema({
      name: {
        type: String,
        // You can also make a validator async by returning a promise. If you
        // return a promise, do **not** specify the `isAsync` option.
        validate: function(v) {
          return new Promise(function(resolve, reject) {
            setTimeout(function() {
              resolve(false);
            }, 5);
          });
        }
      },
      phone: {
        type: String,
        validate: {
          isAsync: true,
          validator: function(v, cb) {
            setTimeout(function() {
              var phoneRegex = /\d{3}-\d{3}-\d{4}/;
              var msg = v + ' is not a valid phone number!';
              // 第一個參數是布爾值,表明驗證結果
              // 第二個參數是報錯信息
              cb(phoneRegex.test(v), msg);
            }, 5);
          },
          // 默認報錯信息會被 `cb()` 第二個參數覆蓋
          message: 'Default error message'
        },
        required: [true, 'User phone number required']
      }
    });

    var User = db.model('User', userSchema);
    var user = new User();
    var error;

    user.phone = '555.0123';
    user.name = 'test';
    user.validate(function(error) {
      assert.ok(error);
      assert.equal(error.errors['phone'].message,
        '555.0123 is not a valid phone number!');
      assert.equal(error.errors['name'].message,
        'Validator failed for path `name` with value `test`');
    });
  
複製代碼

中間件middleware

中間件(pre和post鉤子)是在異步函數執行時函數傳入的控制函數,mongoose中全部的中間件都支持pre和post鉤子。

pre鉤子

pre鉤子分爲串行和並行兩種,串行就是中間件一個接一個的執行,也就是上一個中間件調用next函數的時候,下一個執行。

var schema = new Schema(..);
schema.pre('save', function(next) {
  // do stuff
  next();
});
複製代碼

並行中間件提供細粒度流控制

var schema = new Schema(..);

// 只有第二個參數爲‘true'時才表示並行執行
schema.pre('save', true, function(next, done) {
  // calling next kicks off the next middleware in parallel
  next();
  setTimeout(done, 100);
});
複製代碼

post中間件

post中間件在方法執行後調用

schema.post('init', function(doc) {
  console.log('%s has been initialized from the db', doc._id);
});
schema.post('validate', function(doc) {
  console.log('%s has been validated (but not saved yet)', doc._id);
});
schema.post('save', function(doc) {
  console.log('%s has been saved', doc._id);
});
schema.post('remove', function(doc) {
  console.log('%s has been removed', doc._id);
});
複製代碼

若是在回調函數傳入兩個參數,mongoose會認爲第二個參數是next函數,咱們能夠經過next觸發下一個中間件

// Takes 2 parameters: this is an asynchronous post hook
schema.post('save', function(doc, next) {
  setTimeout(function() {
    console.log('post1');
    // Kick off the second post hook
    next();
  }, 10);
});

// Will not execute until the first middleware calls `next()`
schema.post('save', function(doc, next) {
  console.log('post2');
  next();
});
複製代碼

鏈接Connections

咱們可用mongoose.connect()鏈接,最簡單的如mongoose.connect('mongodb://localhost/myapp');

,固然咱們也能夠在uri中指定多個參數mongoose.connect('mongodb://username:password@host:port/database?options...');

咱們也能夠用mongoose.createConnection()來鏈接,它返回一個新的鏈接,connection對象後面用於建立和檢索models。

const conn = mongoose.createConnection('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', options);
複製代碼

操做緩存

意思就是咱們沒必要等待鏈接創建成功就可使用models,mongoose會先緩存model操做

var MyModel = mongoose.model('Test', new Schema({ name: String }));
// 鏈接成功前操做會被掛起
MyModel.findOne(function(error, result) { /* ... */ });

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

若是要禁用緩存,可修改bufferCommands配置,也能夠全局禁用bufferCommands

mongoose.set('bufferCommands', false);
複製代碼

選項

connect方法能夠接受options參數,具體有哪些參數能夠參考官方文檔

mongoose.connect(uri,options)
複製代碼
const options = {
  useMongoClient: true,
  autoIndex: false, // Don't build indexes
  reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
  reconnectInterval: 500, // Reconnect every 500ms
  poolSize: 10, // Maintain up to 10 socket connections
  // If not connected, return errors immediately rather than waiting for reconnect
  bufferMaxEntries: 0
};
mongoose.connect(uri, options);
複製代碼

回調

connect函數能夠接受回調函數或者返回一個promise

mongoose.connect(uri,options,function(error){
})

mongoose.connect(uri,options).then(
 () => { /** ready to use. The `mongoose.connect()` promise resolves to undefined. */ },
  err => { /** handle initial connection error */ }
)
複製代碼

注意

這裏有個鏈接池的概念,不管是使用 mongoose.connect 或是 mongoose.createConnection 建立的鏈接, 都被歸入默認最大爲 5 的鏈接池,能夠經過 poolSize 選項調整:

// With object options
mongoose.createConnection(uri, { poolSize: 4 });

const uri = 'mongodb://localhost/test?poolSize=4';
mongoose.createConnection(uri);
複製代碼

子文檔Subdocument

子文檔指的是嵌套在另外一個文檔中的文檔,mongoose文檔有兩種不一樣的概念:子文檔數組和單個嵌套子文檔

var childSchema = new Schema({ name: 'string' });

var parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
});
複製代碼

子文檔和普通文檔相似,主要不一樣的是子文檔不能單獨保存,它們會在它們的頂級文檔保存時保存

var Parent = mongoose.model('Parent', parentSchema);
var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
parent.children[0].name = 'Matthew';

// `parent.children[0].save()` 無操做,雖然他觸發了中間件
// 可是**沒有**保存文檔。你須要 save 他的父文檔
parent.save(callback);
複製代碼

經常使用的操做:

  1. Model.find Model.find(query,fields,options,callback)//fields和options都是可選參數 簡單查詢 Model.find({'csser.com':5},function(err,docs){docs是查詢的結果數組}); 只查詢指定鍵的結果 Model.find({},['first','last'],function(err,docs){//docs此時只包含文檔的部分鍵值})

  2. Model.findOne 它只返回單個文檔 Model.findOne({age:5},function(err,doc){//doc是單個文檔});

  3. Model.findById 與findOne相同,但它接收文檔的_id做爲參數,返回單個文檔。_id能夠是字符串或者ObjectID對象 Model.findById(obj._id,function(err,doc){//doc是單個文檔})

  4. Model.count 返回符合條件的文檔數 Model.count(conditions,callback)

  5. Model.remove 刪除符合條件的文檔 Model.remove(conditions,callback)

  6. Model.distinct 查詢符合條件的文檔並返回根據鍵分組的結果

    Model.distinct(field,conditions,callback)

  7. Model.where 當查詢比較複雜的時候就用where Model.where('age').gte(25)

    where('tags').in(['movie','music','art']) .select('name','age','tags') .skip(20) .limit(10) .asc('age') .slaveOk() .hint({age:1,name:1}) .run(callback)

  8. Model.$where

    有時咱們須要在MongoDB中使用JavaScript表達式進行查詢,這時可使用find({ where:javascript})方式,where是一種快捷方式,並支持鏈式調用查詢 Model.$where('this.firstname===this.lastname').exec(callback)

  9. Model.update

    使用update子句更新指定條件的文檔,更新數據在發送到數據庫服務器以前會改變模型的類型,注意update返回的是被修改的文檔數量 var conditions={name:'borne'} ,update={$inc:{visits:1}} ,options={multi:true} Model.update(conditions,update,options,callback)

    注意:爲了向後兼容,全部頂級更新鍵若是不是原子操做命名的,會被統一按$set操做處理,例如:

    var queryb={name:'borne'};

    Model.update(query,{name:'jason borne'},options,callback)

    上面會被這樣發送到數據庫服務器的

    Model.update(query,{$set:{name:'jason borne'}},options,callback)

  10. 查詢API 若是不提供回調函數,全部這些方法都返回Query對象,它們均可以被再次修改,直到調用exec方法 var query=Model.find({});

    query.where('field',5);

    query.limit(5);

    query.skip(100);

相關文章
相關標籤/搜索