mongoose操作mongodb
http://mongoosejs.com/docs/api.html#index-js
mongoose是nodejs環(huán)境下操作mongodb的模塊封裝,使用mongoose之后,實(shí)際上只需要在mongodb中創(chuàng)建好數(shù)據(jù)庫(kù)與用戶(hù),集合的定義、創(chuàng)建、操作等直接使用mongoose即可。
- 一、連接
- 二、重要概念
- 三、基本操作
- 1、Schema
- 2、Model
- 3、實(shí)例化document
- 4、保存數(shù)據(jù)
- 5、文檔查詢(xún)
- 6、文檔更新
- 7、文檔刪除
- 8、自定義方法
- 9、虛擬屬性
- 10、前置與后置鉤子
一、連接
let mongoose = require('mongoose');//連接mongodb //非auth模式 //mongoose.connect('mongodb://localhost:27017/mall'); //auth模式 /** mongodb 為協(xié)議 第一個(gè)mall: 連接數(shù)據(jù)庫(kù)的用戶(hù)名 123456: 用戶(hù)的密碼 localhost: mongodb地址 27017: mongodb端口號(hào) 第二個(gè)mall: 數(shù)據(jù)庫(kù)名字 **/ mongoose.connect('mongodb://mall:123456@localhost:27017/mall');//連接失敗 mongoose.connection.on('connected',() => {console.log('mongodb connected!'); });//連接成功 mongoose.connection.on('error',(err) => {console.log('mongodb connect fail:'+err); });//連接斷開(kāi) mongoose.connection.on('disconnected',() => {console.log('mongodb connect disconnected!'); });// connection的事件列表可查看:http://mongoosejs.com/docs/api.html#connection_Connection // 或 ./node_modules/mongoose/lib/connection.js#Connection()// 關(guān)閉的兩種方式 // mongoose.connection.close(); 等同于 db.close(); // mongoose.disconnect();二、重要概念
Mongooose中,有三個(gè)比較重要的概念,Schema、Model、Document。Schema生成Model,Model實(shí)例化成為Document。
Schema用于定義數(shù)據(jù)庫(kù)的結(jié)構(gòu)。類(lèi)似創(chuàng)建表時(shí)的數(shù)據(jù)定義(不僅僅可以定義文檔的結(jié)構(gòu)和屬性,還可以定義文檔的實(shí)例方法、靜態(tài)模型方法、復(fù)合索引等),每個(gè)Schema會(huì)映射到mongodb中的一個(gè)collection,Schema不具備操作數(shù)據(jù)庫(kù)的能力。
Model是由Schema編譯而成的構(gòu)造器,具有抽象屬性和行為,可以對(duì)數(shù)據(jù)庫(kù)進(jìn)行增刪查改。Model的每一個(gè)實(shí)例(instance)就是一個(gè)文檔document。
Document是new Model后創(chuàng)建的實(shí)例,它的操作會(huì)影響數(shù)據(jù)庫(kù)。
三、基本操作
1、Schema
Schema是對(duì)mongodb中某個(gè)集合的結(jié)構(gòu)描述,它定義一個(gè)集合中應(yīng)該包含哪些數(shù)據(jù)項(xiàng),每個(gè)數(shù)據(jù)項(xiàng)的數(shù)據(jù)類(lèi)型與檢查約束。可以想象成一個(gè)抽象類(lèi),只有定義,沒(méi)有實(shí)現(xiàn)。就是規(guī)定好某個(gè)集合的框架應(yīng)該是怎么樣的。也可以理解是mysql中的表結(jié)構(gòu)。
可以為數(shù)據(jù)項(xiàng)指定的數(shù)據(jù)類(lèi)型有8種:
創(chuàng)建Schema
const mongoose = require('mongoose') //獲取Schema const Schema = mongoose.Schema;//聲明一個(gè)Schema實(shí)例,實(shí)際就是創(chuàng)建一個(gè)集合的結(jié)構(gòu)框架 let animalSchema = new Schema({title: String, //類(lèi)型可以是首字母大寫(xiě)age: 'number', //類(lèi)型也可以寫(xiě)成字符串全小寫(xiě)food: [{ //數(shù)組name: String}] });//如果需要為animalSchema增加屬性,可以使用add animalSchema.add({sex: {type: String,enum: ['male', 'female'] //規(guī)定,字段值為枚舉約束,只能填male/female} });創(chuàng)建Schema時(shí)可以有一系列的約束條件,類(lèi)似mysql中字段的非空、唯一等約束。
基本語(yǔ)法是:
常用的約束有:
required: 數(shù)據(jù)必須填寫(xiě) default: 默認(rèn)值 min: 最小值(只適用于數(shù)字) max: 最大值(只適用于數(shù)字) match: 正則匹配(只適用于字符串) enum: 枚舉匹配(只適用于字符串) validate: 自定義匹配其中validate是自定義約束
例如要自定義檢查長(zhǎng)度
2、Model
model是根據(jù)Schema定義的結(jié)構(gòu)框架生成一個(gè)映射mongodb的數(shù)據(jù)模型,可以把它理解是一個(gè)類(lèi),是根據(jù)Schema創(chuàng)建的一個(gè)類(lèi),他的數(shù)據(jù)結(jié)構(gòu)是依據(jù)Schema生成的,這個(gè)類(lèi)用來(lái)生成document實(shí)例。
//通過(guò)schema構(gòu)建model,第一個(gè)參數(shù)是給model起個(gè)名字,第二個(gè)參數(shù)是構(gòu)造這個(gè)model所依據(jù)的schema let animalModel = mongoose.model('animalModel', animalSchema);3、實(shí)例化document
document是相當(dāng)于是mongodb中的每一條數(shù)據(jù)了,它是通過(guò)new一個(gè)model得到的。
// 通過(guò)實(shí)例化model得到document,document的數(shù)據(jù)項(xiàng)需根據(jù)schema定義的結(jié)構(gòu)來(lái)寫(xiě)// 通過(guò)實(shí)例化model得到document,document的數(shù)據(jù)項(xiàng)需根據(jù)schema定義的結(jié)構(gòu)來(lái)寫(xiě)let cat = new animalModel({title: '貓咪',food: [{name: '魚(yú)'},{name: '火腿'}],sex: 'male'});到這一步,mongodb中還沒(méi)有animalMode集合以及數(shù)據(jù)。
4、保存數(shù)據(jù)
- 1、save()
在mongoose中保存數(shù)據(jù),實(shí)際就是保存某個(gè)document,document通過(guò)model實(shí)例化來(lái)創(chuàng)建。document的save()方法可以將document映射保存到mongodb中,save()方法中可以傳遞一個(gè)回調(diào)函數(shù)作為參數(shù)。因?yàn)槭窃赿ocument上調(diào)用,自然一次只保存一條文檔。
方法原型:
保存cat:
//保存document,映射成為一條記錄 cat.save((err, catObj) => {if (err) {console.log('保存失敗');console.info(err);} else {console.info(catObj);} });保存后打印:
{ title: '貓咪',food: [ { name: '魚(yú)', _id: 5a96555e95f678530054f918 },{ name: '火腿', _id: 5a96555e95f678530054f917 } ],sex: 'male',_id: 5a96555e95f678530054f919,__v: 0 }保存后這個(gè)回調(diào)中的第二個(gè)參數(shù)就被填充好值,其中的主鍵_id也在其中,__v是mongoose用來(lái)管理數(shù)據(jù)版本號(hào)的標(biāo)識(shí),自己不用動(dòng)。
這時(shí)看mongodb中已有集合,集合中已經(jīng)有一條數(shù)據(jù)(文檔)了。
注意:
1、從上圖可以看出來(lái),集合的名字是根據(jù)mongoose.model('animalModel', animalSchema)中第一個(gè)參數(shù)來(lái)的,變成小寫(xiě)且復(fù)數(shù)形式,如果結(jié)尾是數(shù)字則不變。比如這個(gè)地方如果第一個(gè)參數(shù)給的是’animal’,則生成的集合名稱(chēng)就應(yīng)該是’animals’。
2、在mongoose中操作mongodb,因?yàn)橛辛藄chema、model、ducument的概念,就不是像mysql一樣,事先創(chuàng)建好表結(jié)構(gòu),而是根據(jù)數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),在代碼中聲明好schema,再生成model、創(chuàng)建document,進(jìn)行數(shù)據(jù)操作。
- 2、create()
和save()不一樣的是,create()不是在document上調(diào)用的,而是在model上調(diào)用的,并且可以一次保存多條文檔。
接著用上面的animalModel,這次不去new它了,也就是不去操作document了,直接用mongoose.model出來(lái)的model。同時(shí)保存兩條狗。
打印的結(jié)果:
保存成功 { title: '1號(hào)狗狗',food: [ { name: '骨頭', _id: 5a96607b5ce650532494f010 },{ name: '肉肉', _id: 5a96607b5ce650532494f00f } ],sex: 'male',_id: 5a96607b5ce650532494f011,__v: 0 } { title: '2號(hào)狗狗',food: [ { name: '骨頭', _id: 5a96607b5ce650532494f013 },{ name: '肉肉', _id: 5a96607b5ce650532494f012 } ],sex: 'male',_id: 5a96607b5ce650532494f014,__v: 0 }再看看mongodb中:
注意:這里也驗(yàn)證了一個(gè)問(wèn)題,就是如果model對(duì)應(yīng)的集合已經(jīng)存在于mongodb中了,則會(huì)直接往這個(gè)集合中添加文檔,如果還沒(méi)有,則會(huì)創(chuàng)建集合。所以在上面保存貓咪的時(shí)候,同時(shí)創(chuàng)建了集合與添加貓咪文檔,而這里的狗狗,直接添加文檔。
- 3、insertMany()
insertMany()也是model的方法,一次插入多條數(shù)據(jù),注意回調(diào)中的返回值是所有結(jié)果對(duì)象的數(shù)組。
打印結(jié)果:
保存成功 [ { title: '1號(hào)猴猴',food: [ [Object], [Object] ],sex: 'male',_id: 5a967c3f44d6d753b6cd6354,__v: 0 },{ title: '2號(hào)猴猴',food: [ [Object], [Object] ],sex: 'male',_id: 5a967c3f44d6d753b6cd6357,__v: 0 } ]5、文檔查詢(xún)
mongodb中的數(shù)據(jù)查詢(xún)就是查文檔。
- 1、find()
find()是用在model上的,這一點(diǎn)很容易理解,既然是查詢(xún)數(shù)據(jù),那么當(dāng)前肯定是不明確要得到的文檔結(jié)果是怎樣的,方法也就不會(huì)在document上。
find()方法在官方文檔中的描述是:
官方示例find的用法非常清晰了,find方法最簡(jiǎn)的參數(shù)是只給一個(gè)回調(diào)函數(shù),表示查詢(xún)所有結(jié)果,相當(dāng)于是sql中的select * from 表名。
find()方法返回值是query對(duì)象,query對(duì)象有exec和一系列方法,exec()可以執(zhí)行準(zhǔn)備好的find()查詢(xún),回調(diào)得到結(jié)果。
回到animal示例中:
查詢(xún)animalmodels集合中所有的文檔:
find()的完整參數(shù)列表是:
Model.find([查詢(xún)條件], [返回字段列表], [設(shè)置選項(xiàng)], [回調(diào)函數(shù)]);
對(duì)于第一個(gè)參數(shù),比如age: { $gte: 18 },表示要查詢(xún)年齡大于等于18的文檔。常用的條件操作符有:
有一個(gè)特殊的操作符$where,可以靈活的設(shè)置查詢(xún)條件,$where后可以直接寫(xiě)任何的js作為查詢(xún)條件,但實(shí)際上完全可以使用model.where()方法來(lái)代替,where()會(huì)更加方便。
{$where:"this.x == this.y"} {$where:function(){return obj.x !== obj.y; }}這些操作符在find的第一個(gè)參數(shù)中使用,query中也有一一對(duì)一個(gè)的方法。
對(duì)于第二個(gè)參數(shù),可以設(shè)置要返回的字段列表,{name:1,_id:0}表示返回name字段,不返回_id字段。
對(duì)于第三個(gè)參數(shù),官方示例中用到了一個(gè)skip,還有l(wèi)imit、sort。
除了find()外,還有findById()、findOne(),與find的用法都是一樣的。只不過(guò)findById的第一個(gè)參數(shù)是準(zhǔn)確的id值。這兩個(gè)方法都只返回一條文檔。
- 2、where()
官網(wǎng)上這么介紹的
官方示例:
User.find({age: {$gte: 21, $lte: 65}}, callback); 等同于 User.where('age').gte(21).lte(65).exec(callback);因?yàn)閣here返回的是query對(duì)象,所以可以鏈?zhǔn)秸{(diào)用:
User .where('age').gte(21).lte(65) .where('name', /^b/i) ... etc所以在實(shí)際的使用過(guò)程中,用query對(duì)象來(lái)進(jìn)行查詢(xún)是比較方便的,用find()的返回值query鏈?zhǔn)秸{(diào)用其他方法,或者用where()的返回值query鏈?zhǔn)秸{(diào)用其他方法,最后用exec(callback)得到結(jié)果,這樣比較清晰。query有一系列的方法,異步官方文檔。
- 3、query常用方法
關(guān)于分頁(yè)與排序,query的三個(gè)方法:
query.limit(20); //只返回前20個(gè)內(nèi)容
query.skip(2); //跳過(guò)多少條,實(shí)際就是開(kāi)始的索引
query.sort({name:1,age:-1}); //1升序,2降序
6、文檔更新
- 1、update()
Model.update(conditions, doc, [options], [callback])
第一個(gè)參數(shù)conditions為查詢(xún)條件,第二個(gè)參數(shù)doc為需要修改的數(shù)據(jù),第三個(gè)參數(shù)options為控制選項(xiàng),第四個(gè)參數(shù)是回調(diào)函數(shù)。
options有以下選項(xiàng):
把名字叫貓咪的文檔的名字更新成貓貓:
animalModel.update({title :{$eq: '貓咪'}}, {title: '貓貓'}, (err, rs) => {if (err) {console.log('更新失敗');console.info(err);} else {console.log('更新成功');console.info(rs);}});如果不設(shè)置multi的話(huà),即使有多條符合條件的文檔,也只更新一條。
如果設(shè)置options里的upsert參數(shù)為true,若沒(méi)有符合查詢(xún)條件的文檔,mongo將會(huì)綜合第一第二個(gè)參數(shù)向集合插入一個(gè)新的文檔。
2、updateMany()
updateMany()與update()的區(qū)別是更新多個(gè)文檔,即使設(shè)置{multi:false}也更新多個(gè)。3、updateOne()
updateOne()只更新找到的第一條數(shù)據(jù),即使設(shè)置{multi:true}只更新一個(gè)。4、數(shù)組更新
update第二個(gè)參數(shù),實(shí)際可以寫(xiě)成{$set: {title: '貓貓'}},$set操作符表示設(shè)值,如果要更新的是數(shù)組,可以有以下操作符:
給貓?zhí)砑右粋€(gè)食物:
animalModel.update({title :{$eq: '喵星人'}},{$addToSet:{food: {name: '蛋黃派'}}},(err,rs) =>{if (err) {console.info("保存失敗");console.info(err);} else {console.info("保存成功");console.info(rs);}});結(jié)果:
保存成功 { n: 1, nModified: 1, ok: 1 }- 5、其他
關(guān)于其他更新方法還有findOneAndUpdate()、findByIdAndUpdate()。
通過(guò)查詢(xún)+保存操作也可以實(shí)現(xiàn)更新,查詢(xún)的結(jié)果實(shí)際是一個(gè)document對(duì)象,調(diào)用save方法,猜想是通過(guò)_id與_v來(lái)進(jìn)行的比對(duì),將直接更新本條記錄。
7、文檔刪除
- 1、remove()
remove方法存在兩個(gè)位置,一個(gè)是model上,一個(gè)是ducument上,model上就是根據(jù)條件去刪除集合內(nèi)某些文檔,文檔上就是刪除自己。
示例:
//model刪除animalModel.remove({title :{$eq: '喵星人'}},(err,rs) =>{if (err) {console.info("刪除失敗");console.info(err);} else {console.info("刪除成功");console.info(rs);}});//document刪除animalModel.where('title').eq('2號(hào)猴猴').exec((err, rs) => {if (err) {console.info(err);} else {console.info(rs);rs.forEach(function(item,index,array){item.remove((err,rs) =>{if (err) {console.info("刪除失敗");console.info(err);} else {console.info("刪除成功");console.info(rs);}});});}});- 2、findOneAndRemove()
model的remove()會(huì)刪除符合條件的所有數(shù)據(jù),如果只刪除符合條件的第一條數(shù)據(jù),可以使用model的findOneAndRemove()方法
- 3、findByIdAndRemove()
通過(guò)id刪除文檔
- 4、其他
所有刪除方法的回調(diào)函數(shù)都不能省略,否則不成功。要么寫(xiě)在方法的回調(diào)函數(shù)參數(shù)中,如果不寫(xiě)回調(diào)函數(shù)參數(shù),則可以在最后調(diào)用.exec()。
8、自定義方法
mongoose中可以利用已有的api,自定義自己的方法來(lái)處理特定的業(yè)務(wù)需要。
- 1、實(shí)例方法
通過(guò)new model得到的document實(shí)例有很多增刪改查的方法,如上面的save()方法用來(lái)保存文檔,我們可以給實(shí)例增加自定義方法來(lái)實(shí)現(xiàn)特定的業(yè)務(wù)邏輯,增加之后就可以像調(diào)用save一樣來(lái)調(diào)用自己的方法,給document增加自定義方法是通過(guò)擴(kuò)展Schema的methods屬性來(lái)完成的。
為animalSchema擴(kuò)展document實(shí)例方法:
//為document實(shí)例添加eat方法,返回所有喜歡吃的食物 animalSchema.methods.eat = function () {let foodList = this.food;let foodStr = '';foodList.forEach(function (item, index, arr) {if (index == 0) {foodStr += item.name;} else {foodStr += ","+item.name;}});return foodStr; };document實(shí)例調(diào)用:
animalModel.findById('5a96607b5ce650532494f011').exec((err, rs) => {if (err) {console.info("查詢(xún)失敗");console.info(err);} else {console.info("查詢(xún)成功");console.info(rs);let eatStr = rs.eat();console.info(eatStr); //骨頭,肉肉}});- 2、靜態(tài)方法
靜態(tài)方法,是不需要實(shí)例化得到document,而是直接在model上調(diào)用的方法。
所以,實(shí)例方法,是操作文檔(mongodb中的某一條記錄)數(shù)據(jù)用的,靜態(tài)方法是操作集合用的。
為animalSchema添加靜態(tài)方法:
//為model添加findDogs方法 animalSchema.statics.findDogs = function (backfun){this.find({title: /狗/}).exec(backfun); };model調(diào)用:
animalModel.findDogs((err, rs) => {if (err) {console.info("查詢(xún)失敗");console.info(err);} else {console.info("查詢(xún)成功");console.info(rs); //打印出兩條狗的document}});-3、查詢(xún)方法
擴(kuò)展schema對(duì)象的query屬性,給model添加查詢(xún)方法,主要為了擴(kuò)展query的個(gè)性化方法需要。
query調(diào)用:
let rsQuery = animalModel.find();rsQuery.findMonkey((err, rs) => {if (err) {console.info("查詢(xún)失敗");console.info(err);} else {console.info("查詢(xún)成功");console.info(rs); //打印出一只猴子的document}});9、虛擬屬性
schema上除了可以設(shè)置方法以外,還可以設(shè)置虛擬屬性,就像vue的getter一樣,實(shí)際數(shù)據(jù)庫(kù)中沒(méi)有這個(gè)屬性,但可以通過(guò)虛擬屬性機(jī)制自定義一個(gè)經(jīng)過(guò)處理的值,雖然用方法同樣能實(shí)現(xiàn),但虛擬屬性效率更高。
//添加虛擬屬性nameAndSex animalSchema.virtual('info').get(function () {return this.title + "," +this.sex; });調(diào)用:
animalModel.find({title: /狗/}).exec((err, rs) => {if (err) {console.info("查詢(xún)失敗");console.info(err);} else {console.info("查詢(xún)成功");rs.forEach( function (item, index, arr) {console.info(item.info);});}});打印結(jié)果:
1號(hào)狗狗,male 2號(hào)狗狗,male10、前置與后置鉤子
通過(guò)schema可以為指定的某些方法添加前置鉤子函數(shù)與后置鉤子函數(shù)。前置鉤子函數(shù)在指定方法開(kāi)始前調(diào)用,后置鉤子函數(shù)不是在指定方法結(jié)束后操作,二是在指定方法開(kāi)始前的最后一步操作。
前置鉤子是pre()
后置鉤子是post()
調(diào)用find()方法
animalModel.find({title: /狗/}).exec((err, rs) => {if (err) {console.info("查詢(xún)失敗");console.info(err);} else {console.info("查詢(xún)成功");console.info(rs);}});得到打印結(jié)果:
前置操作1 前置操作2 后置操作1 后置操作2 查詢(xún)成功 [ { food: [ [Object], [Object] ],_id: 5a96607b5ce650532494f011,title: '1號(hào)狗狗',sex: 'male',__v: 0 },{ food: [ [Object], [Object] ],_id: 5a96607b5ce650532494f014,title: '2號(hào)狗狗',sex: 'male',__v: 0 } ]注意,前置鉤子中有一個(gè)next函數(shù),如果方法內(nèi)不調(diào)用next(),被設(shè)置前置鉤子的方法(比如find())執(zhí)行到鉤子時(shí)不會(huì)繼續(xù)向下執(zhí)行。
可以添加前后鉤子的方法有:
init validate save remove count find findOne findOneAndRemove findOneAndUpdate insertMany update以上是mongoose的常用基本操作,還有索引、聚合等等操作需要積累補(bǔ)充。
總結(jié)
以上是生活随笔為你收集整理的mongoose操作mongodb的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: nodejs的启动方式
- 下一篇: mac下iterm配色、半透明与样式设置