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是按特殊到一般的順序排列的)