nodejs使用mongoose模塊操作mongodb數(shù)據(jù)庫(kù)

npm安裝mongoose祖灰。

npm install mongoose

關(guān)于mongoose
Mongoose是MongoDB的一個(gè)對(duì)象模型工具,是基于node-mongodb-native開發(fā)的MongoDB nodejs驅(qū)動(dòng)侮攀,可以在異步的環(huán)境下執(zhí)行未巫。同時(shí)它也是針對(duì)MongoDB操作的一個(gè)對(duì)象模型庫(kù)扫尺,封裝了MongoDB對(duì)文檔的的一些增刪改查等常用方法约急,讓NodeJS操作Mongodb數(shù)據(jù)庫(kù)變得更加靈活簡(jiǎn)單零远。
Schema : 一種以文件形式存儲(chǔ)的數(shù)據(jù)庫(kù)模型骨架,不具備數(shù)據(jù)庫(kù)的操作能力
Model : 由Schema發(fā)布生成的模型厌蔽,具有抽象屬性和行為的數(shù)據(jù)庫(kù)操作對(duì)
Entity : 由Model創(chuàng)建的實(shí)體遍烦,他的操作也會(huì)影響數(shù)據(jù)庫(kù)
它們之間的關(guān)系是Schema生成Model,Model創(chuàng)造Entity蝴韭,Model和Entity都可對(duì)數(shù)據(jù)庫(kù)操作造成影響崇呵,但Model比Entity更具操作性。Model對(duì)應(yīng)collection,Entity對(duì)應(yīng)docment。


1.增加

如果是Entity玩敏,使用save方法,如果是Model漓柑,使用create方法

// mongoose 鏈接
var mongoose = require('mongoose');
var db       = mongoose.connect('mongodb://127.0.0.1:27017/chm'); 
// 鏈接錯(cuò)誤
db.connection.on('error', function(error) {
    console.log(error);
});

db.connection.on("open", function () { 
console.log("——數(shù)據(jù)庫(kù)連接成功竟宋!——"); 
});

// Schema 結(jié)構(gòu)
var vipSchema = new mongoose.Schema({
    name : {type : String, default : 'java'},
    addr    : {type : String},
    addTime  : {type : Date, default: Date.now},
    age      : {type : Number}
});
//中間件
//save之前觸發(fā)
vipSchema.pre('save', function(next){
    console.log("pre save");
    next();
});
vipSchema.post('save', function (doc) {
    //doc是當(dāng)前插入的文檔
    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!');
    }
    // 關(guān)閉數(shù)據(jù)庫(kù)鏈接
    db.disconnect();
});
//增加記錄 基于model操作
vipModel.create(doc, function(error){
    if(error) {
        console.log(error);
    } else {
        console.log('save ok');
    }
    // 關(guān)閉數(shù)據(jù)庫(kù)鏈接
    db.disconnect();
});

這里有個(gè)問題,當(dāng)執(zhí)行完上面的代碼后粘捎,到數(shù)據(jù)庫(kù)中執(zhí)行db.vip.find();命令你會(huì)發(fā)現(xiàn)查不到剛添加的那條數(shù)據(jù)薇缅,再執(zhí)行show collections你會(huì)發(fā)現(xiàn)多了一個(gè)vips集合,數(shù)據(jù)在這個(gè)集合里面攒磨。這里Mongoose在模型名至數(shù)據(jù)庫(kù)集合名的命名轉(zhuǎn)換上做了文章泳桦,下面會(huì)介紹。

2.查找

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

db.connection.on("error", function (error) { 
console.log("數(shù)據(jù)庫(kù)連接失斆溏帧:" + error); 
}); 
db.connection.on("open", function () { 
console.log("——數(shù)據(jù)庫(kù)連接成功灸撰!——"); 
});

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

// 添加 mongoose 實(shí)例方法
vipSchema.methods.findByName = function(hello, callback) {
    return this.model('vips').find({name: hello}, callback);
}
// 添加 mongoose 靜態(tài)方法,靜態(tài)方法在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);
    }
    //關(guān)閉數(shù)據(jù)庫(kù)鏈接
    db.disconnect();
 });
//基于實(shí)例方法的查詢
var entity = new vipModel({"name":"java"});
entity.findByName("java",function(error, result){
    if(error) {
        console.log(error);
    } else {
        console.log(result);
    }
    //關(guān)閉數(shù)據(jù)庫(kù)鏈接
    db.disconnect();
});

//基于靜態(tài)方法的查詢
vipModel.findbyage(20, function(error, result){
    if(error) {
        console.log(error);
    } else {
        console.log(result);
    }
    //關(guān)閉數(shù)據(jù)庫(kù)鏈接
    db.disconnect();;
});

另一種查詢:查詢時(shí)不帶回調(diào)

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沒有執(zhí)行的預(yù)編譯查詢語(yǔ)句完疫,該query對(duì)象執(zhí)行的方法都將返回自己,只有在執(zhí)行exec方法時(shí)才執(zhí)行查詢债蓝,而且必須有回調(diào)壳鹤。

還有一個(gè)根據(jù)Id查詢

var ObjectId = require('mongodb').ObjectID;
let _id = ObjectId(_id);
this.collection.findOne({_id:_id}, {}, function(err, doc) {
    console.log(err, doc);
});

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!');
    }
    //關(guān)閉數(shù)據(jù)庫(kù)鏈接
    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!');
    }

    //關(guān)閉數(shù)據(jù)庫(kù)鏈接
    db.disconnect();
});

Sub Docs

如同SQL數(shù)據(jù)庫(kù)中2張表有主外關(guān)系饰迹,Mongoose將2個(gè)Document的嵌套叫做Sub-Docs(子文檔)

簡(jiǎn)單的說(shuō)就是一個(gè)Document嵌套另外一個(gè)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的操作都由父類去執(zhí)行

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

parent在執(zhí)行保存時(shí),由于包含children2蹦锋,他是一個(gè)數(shù)據(jù)庫(kù)模型對(duì)象兆沙,因此會(huì)先保存chilren2[0]和chilren2[1]。

如果子文檔在更新時(shí)出現(xiàn)錯(cuò)誤莉掂,將直接報(bào)在父類文檔中葛圃,可以這樣處理:

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的子文檔,可以通過(guò)如下方法查詢到children

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

4.2 新增憎妙、刪除库正、更新

子文檔是父文檔的一個(gè)屬性,因此按照屬性的操作即可厘唾,不同的是在新增父類的時(shí)候褥符,子文檔是會(huì)被先加入進(jìn)去的。

如果ChildrenSchema是臨時(shí)的一個(gè)子文檔抚垃,不作為數(shù)據(jù)庫(kù)映射集合喷楣,可以這樣:

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

數(shù)據(jù)驗(yàn)證

數(shù)據(jù)的存儲(chǔ)是需要驗(yàn)證的,不是什么數(shù)據(jù)都能往數(shù)據(jù)庫(kù)里丟或者顯示到客戶端的鹤树,數(shù)據(jù)的驗(yàn)證需要記住以下規(guī)則:

驗(yàn)證始終定義在SchemaType中
驗(yàn)證是一個(gè)內(nèi)部中間件
驗(yàn)證是在一個(gè)Document被保存時(shí)默認(rèn)啟用的铣焊,除非你關(guān)閉驗(yàn)證
驗(yàn)證是異步遞歸的,如果你的SubDoc驗(yàn)證失敗罕伯,Document也將無(wú)法保存
驗(yàn)證并不關(guān)心錯(cuò)誤類型曲伊,而通過(guò)ValidationError這個(gè)對(duì)象可以訪問
7.1 驗(yàn)證器
required 非空驗(yàn)證
min/max 范圍驗(yàn)證(邊值驗(yàn)證)
enum/match 枚舉驗(yàn)證/匹配驗(yàn)證
validate 自定義驗(yàn)證規(guī)則

以下是綜合案例:

    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是一個(gè)驗(yàn)證函數(shù)追他,err是驗(yàn)證失敗的錯(cuò)誤信息
      }
    });

7.2 驗(yàn)證失敗

如果驗(yàn)證失敗坟募,則會(huì)返回err信息,err是一個(gè)對(duì)象該對(duì)象屬性如下

err.errors                //錯(cuò)誤集合(對(duì)象)
err.errors.color          //錯(cuò)誤屬性(Schema的color屬性)
err.errors.color.message  //錯(cuò)誤屬性信息
err.errors.path             //錯(cuò)誤屬性路徑
err.errors.type             //錯(cuò)誤類型
err.name                //錯(cuò)誤名稱
err.message                 //錯(cuò)誤消息

一旦驗(yàn)證失敗邑狸,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.判斷模型名是否是不可數(shù)的推溃,如果是直接返回模型名昂利;否則進(jìn)行復(fù)數(shù)轉(zhuǎn)化正則匹配届腐;

2.返回復(fù)數(shù)轉(zhuǎn)化正則匹配結(jié)果(一個(gè)復(fù)數(shù)轉(zhuǎn)化正則匹配是一個(gè)數(shù)組,有兩個(gè)對(duì)象蜂奸,[0]正則表達(dá)式犁苏,[1]匹配后處理結(jié)果);

3.如果復(fù)數(shù)轉(zhuǎn)化正則匹配結(jié)果不存在扩所,直接返回模型名围详;否則取匹配結(jié)果第一個(gè),對(duì)模型名進(jìn)行處理祖屏。(需要說(shuō)明的是助赞,rules是按特殊到一般的順序排列的)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市袁勺,隨后出現(xiàn)的幾起案子雹食,更是在濱河造成了極大的恐慌,老刑警劉巖期丰,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件群叶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钝荡,警方通過(guò)查閱死者的電腦和手機(jī)街立,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)埠通,“玉大人赎离,你說(shuō)我怎么就攤上這事《巳瑁” “怎么了梁剔?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舞蔽。 經(jīng)常有香客問我憾朴,道長(zhǎng),這世上最難降的妖魔是什么喷鸽? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮灸拍,結(jié)果婚禮上做祝,老公的妹妹穿的比我還像新娘。我一直安慰自己鸡岗,他們只是感情好混槐,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著轩性,像睡著了一般声登。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天悯嗓,我揣著相機(jī)與錄音件舵,去河邊找鬼。 笑死脯厨,一個(gè)胖子當(dāng)著我的面吹牛铅祸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播合武,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼临梗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了稼跳?” 一聲冷哼從身側(cè)響起盟庞,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汤善,沒想到半個(gè)月后什猖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萎津,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年卸伞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锉屈。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荤傲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颈渊,到底是詐尸還是另有隱情遂黍,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布俊嗽,位于F島的核電站雾家,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绍豁。R本人自食惡果不足惜芯咧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竹揍。 院中可真熱鬧敬飒,春花似錦、人聲如沸芬位。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昧碉。三九已至英染,卻和暖如春揽惹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背四康。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工搪搏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人箭养。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓慕嚷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親毕泌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喝检,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容