1. 程式人生 > >nodejs操作mongodb資料庫(mongoose)

nodejs操作mongodb資料庫(mongoose)

準備

在上一篇的基礎上,通過npm安裝mongoose。

關於mongoose

Mongoose是MongoDB的一個物件模型工具,是基於node-mongodb-native開發的MongoDB nodejs驅動,可以在非同步的環境下執行。同時它也是針對MongoDB操作的一個物件模型庫,封裝了MongoDB對文件的的一些增刪改查等常用方法,讓NodeJS操作Mongodb資料庫變得更加靈活簡單。

Schema : 一種以檔案形式儲存的資料庫模型骨架,不具備資料庫的操作能力
Model : 由Schema釋出生成的模型,具有抽象屬性和行為的資料庫操作對
Entity : 由Model建立的實體,他的操作也會影響資料庫

它們之間的關係是Schema生成Model,Model創造Entity,Model和Entity都可對資料庫操作造成影響,但Model比Entity更具操作性。Model對應collection,Entity對應docment。

CRUD操作

1.增加

如果是Entity,使用save方法,如果是Model,使用create方法

// mongoose 連結
var mongoose = require('mongoose');
var db       = mongoose.connect('mongodb://127.0.0.1:27017/chm'); 
// 連結錯誤
db.connection.on('error'
, function(error) { console.log(error); }); db.connection.on("open", function () { console.log("——資料庫連線成功!——"); }); // Schema 結構 var vipSchema = new mongoose.Schema({ name : {type : String, default : 'java'}, addr : {type : String}, addTime : {type : Date, default: Date.now}, age : {type : Number
} }); //中介軟體 //save之前觸發 vipSchema.pre('save', function(next){ console.log("pre save"); next(); }); vipSchema.post('save', function (doc) { //doc是當前插入的文件 console.log(doc); }); // model var vipModel = db.model('vip', vipSchema); // 增加記錄 基於 entity 操作 var doc = {name : 'java', age:20, addr:"shanghai"}; var vipEntity= new vipModel(doc); vipEntity.save(function(error) { if(error) { console.log(error); } else { console.log('saved OK!'); } // 關閉資料庫連結 db.disconnect(); }); //增加記錄 基於model操作 vipModel.create(doc, function(error){ if(error) { console.log(error); } else { console.log('save ok'); } // 關閉資料庫連結 db.disconnect(); });

這裡有個問題,當執行完上面的程式碼後,到資料庫中執行db.vip.find();命令你會發現查不到剛新增的那條資料,再執行show collections你會發現多了一個vips集合,資料在這個集合裡面。這裡Mongoose在模型名至資料庫集合名的命名轉換上做了文章,下面會介紹。
2.查詢

var mongoose = require("mongoose");
var db = mongoose.connect('mongodb://localhost:27017/chm'); 

db.connection.on("error", function (error) { 
console.log("資料庫連線失敗:" + error); 
}); 
db.connection.on("open", function () { 
console.log("——資料庫連線成功!——"); 
});

var Schema = mongoose.Schema;
//模板
var vipSchema = new Schema({
    name:String,
    age:Number,
    addr:String,
    addTime:Date
});

// 新增 mongoose 例項方法
vipSchema.methods.findByName = function(hello, callback) {
    return this.model('vips').find({name: hello}, callback);
}
// 新增 mongoose 靜態方法,靜態方法在Model層就能使用
vipSchema.statics.findbyage = function(age, callback) {
    return this.model('vips').find({age: age}, callback);
}

//模型
var vipModel = mongoose.model('vips', vipSchema);


vipModel.find({name:"java"},function(error, result){
    if(error) {
        console.log(error);
    } else {
        console.log(result);
    }
    //關閉資料庫連結
    db.disconnect();
 });
//基於例項方法的查詢
var entity = new vipModel({"name":"java"});
entity.findByName("java",function(error, result){
    if(error) {
        console.log(error);
    } else {
        console.log(result);
    }
    //關閉資料庫連結
    db.disconnect();
});

//基於靜態方法的查詢
vipModel.findbyage(20, function(error, result){
    if(error) {
        console.log(error);
    } else {
        console.log(result);
    }
    //關閉資料庫連結
    db.disconnect();;
});

另一種查詢:查詢時不帶回調

vipModel
      .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);

如果不帶callback,則返回query,query沒有執行的預編譯查詢語句,該query物件執行的方法都將返回自己,只有在執行exec方法時才執行查詢,而且必須有回撥。

3.修改

//省略上面相同程式碼
......

// 修改記錄
var conditions = {name : 'java'};
var update     = {$set : {age : 27}};
var options    = {upsert : true};
vipModel.update(conditions, update, options, function(error){
    if(error) {
        console.log(error);
    } else {
        console.log('update ok!');
    }
    //關閉資料庫連結
    db.disconnect();
});

4.刪除

刪除有2種方式,Entity和Model都使用remove方法

//省略上面相同程式碼
......

var conditions = {username: 'java'};
vipModel.remove(conditions, function(error){
    if(error) {
        console.log(error);
    } else {
        console.log('delete ok!');
    }

    //關閉資料庫連結
    db.disconnect();
});

Sub Docs

如同SQL資料庫中2張表有主外關係,Mongoose將2個Document的巢狀叫做Sub-Docs(子文件)

簡單的說就是一個Document巢狀另外一個Document或者Documents:

var ChildSchema1 = new Schema({name:String});
var ChildSchema2 = new Schema({name:String});
var ParentSchema = new Schema({
   children1:ChildSchema1,   //巢狀Document
   children2:[ChildSchema2]  //巢狀Documents
});

Sub-Docs享受和Documents一樣的操作,但是Sub-Docs的操作都由父類去執行

var ParentModel = db.model('Parent',parentSchema);
 var parent = new ParentModel({
      children2:[{name:'c1'},{name:'c2'}]
    });
 parent.children2[0].name = 'd';
 parent.save(callback);

parent在執行儲存時,由於包含children2,他是一個數據庫模型物件,因此會先儲存chilren2[0]和chilren2[1]。

如果子文件在更新時出現錯誤,將直接報在父類文件中,可以這樣處理:

ChildrenSchema.pre('save',function(next){
        if('x' === this.name) 
            return next(new Error('#err:not-x'));
            next();
        });
        var parent = new ParentModel({children1:{name:'not-x'}});
        parent.save(function(err){
        console.log(err.message); //#err:not-x
    });

4.1 查詢子文件

如果children是parent的子文件,可以通過如下方法查詢到children

var child = parent.children.id(id);

4.2 新增、刪除、更新

子文件是父文件的一個屬性,因此按照屬性的操作即可,不同的是在新增父類的時候,子文件是會被先加入進去的。

如果ChildrenSchema是臨時的一個子文件,不作為資料庫對映集合,可以這樣:

var ParentSchema = new Schema({
   children:{
      name:String
   }
});
//其實就是匿名混合模式

資料驗證

資料的儲存是需要驗證的,不是什麼資料都能往資料庫裡丟或者顯示到客戶端的,資料的驗證需要記住以下規則:

  • 驗證始終定義在SchemaType中
  • 驗證是一個內部中介軟體
  • 驗證是在一個Document被儲存時預設啟用的,除非你關閉驗證
  • 驗證是非同步遞迴的,如果你的SubDoc驗證失敗,Document也將無法儲存
  • 驗證並不關心錯誤型別,而通過ValidationError這個物件可以訪問
    7.1 驗證器
  • required 非空驗證
  • min/max 範圍驗證(邊值驗證)
  • enum/match 列舉驗證/匹配驗證
  • validate 自定義驗證規則

以下是綜合案例:

    var PersonSchema = new Schema({
      name:{
        type:'String',
        required:true //姓名非空
      },
      age:{
        type:'Nunmer',
        min:18,       //年齡最小18
        max:120     //年齡最大120
      },
      city:{
        type:'String',
        enum:['北京','上海']  //只能是北京、上海人
      },
      other:{
        type:'String',
        validate:[validator,err]  //validator是一個驗證函式,err是驗證失敗的錯誤資訊
      }
    });

7.2 驗證失敗

如果驗證失敗,則會返回err資訊,err是一個物件該物件屬性如下

err.errors                //錯誤集合(物件)
err.errors.color          //錯誤屬性(Schema的color屬性)
err.errors.color.message  //錯誤屬性資訊
err.errors.path             //錯誤屬性路徑
err.errors.type             //錯誤型別
err.name                //錯誤名稱
err.message                 //錯誤訊息

一旦驗證失敗,Model和Entity都將具有和err一樣的errors屬性。

Model至Collection的命名策略

mongoose/lib/util.js模組中如下程式碼片段是集合命名的根源。

function pluralize (str) {
  var rule, found;
  if (!~uncountables.indexOf(str.toLowerCase())){
    found = rules.filter(function(rule){
      return str.match(rule[0]);
    });
    if (found[0]) return str.replace(found[0][0], found[0][1]);
  }
  return str;
};

1.判斷模型名是否是不可數的,如果是直接返回模型名;否則進行復數轉化正則匹配;

2.返回複數轉化正則匹配結果(一個複數轉化正則匹配是一個數組,有兩個物件,[0]正則表示式,[1]匹配後處理結果);

3.如果複數轉化正則匹配結果不存在,直接返回模型名;否則取匹配結果第一個,對模型名進行處理。(需要說明的是,rules是按特殊到一般的順序排列的)