Mongoose 笔记

Mongoose 笔记

官网指南

参考链接

名词解释

Schema: 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力

Model: 由Schema发布生成的模型,具有抽象属性和行为的数据库操作对

Entity: 由Model创建的实体,他的操作也会影响数据库

Schema、Model、Entity的关系请牢记,Schema生成Model,Model创造Entity,Model和Entity都可对数据库操作造成影响,但Model比Entity更具操作性。

链接数据库&创建模型


var mongoose = require('mongoose');    //引用mongoose模块

var db = mongoose.createConnection('localhost','test'); //创建一个数据库连接

db.on('error',console.error.bind(console,'连接错误:'));

db.once('open',function(){

//一次打开记录

});

//定义一个Schema

var PersonSchema = new mongoose.Schema({

name:String  //定义一个属性name,类型为String

});

//将该Schema发布为Model

var PersonModel = db.model('Person',PersonSchema);

//如果该Model已经发布,则可以直接通过名字索引到,如下:

//var PersonModel = db.model('Person');

//如果没有发布,上一段代码将会异常

//用Model创建Entity

var personEntity = new PersonModel({name:'Krouky'});

//打印这个实体的名字看看

console.log(personEntity.name); //Krouky

我们甚至可以为此Schema创建方法


//为Schema模型追加speak方法

PersonSchema.methods.speak = function(){

console.log('我的名字叫'+this.name);

}

var PersonModel = db.model('Person',PersonSchema);

var personEntity = new PersonModel({name:'Krouky'});

personEntity.speak();//我的名字叫Krouky

//Entity是具有具体的数据库操作CRUD的

personEntity.save();  //执行完成后,数据库就有该数据了

//如果要执行查询,需要依赖Model,当然Entity也是可以做到的

PersonModel.find(function(err,persons){

//查询到的所有person

});

查询

通常有2种查询方式,一种是直接查询,一种是链式查询(2种查询都是自己命名的)

直接查询

在查询时带有回调函数的,称之为直接查询,查询的条件往往通过API来设定,例如:


PersonModel.findOne({'name.last':'dragon'},'some select',function(err,person){

//如果err==null,则person就能取到数据

});

链式查询

在查询时候,不带回调,而查询条件通过API函数来制定,例如:


var query = PersonModel.findOne({'name.last':'dragon'});

query.select('some select');

query.exec(function(err,pserson){

//如果err==null,则person就能取到数据

});

因为query的操作始终返回自身,我们可以采用更形象的链式写法


Person

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

更新

有许多方式来更新文件,以下是常用的传统方式:


PersonModel.findById(id,function(err,person){

person.name = 'MDragon';

person.save(function(err){});

});

这里,利用Model模型查询到了person对象,该对象属于Entity,可以有save操作,如果使用Model`操作,需注意:


PersonModel.findById(id,function(err,person){

person.name = 'MDragon';

var _id = person._id; //需要取出主键_id

delete person._id;    //再将其删除

PersonModel.update({_id:_id},person,function(err){});

//此时才能用Model操作,否则报错

});

update第一个参数是查询条件,第二个参数是更新的对象,但不能更新主键,这就是为什么要删除主键的原因。

当然这样的更新很麻烦,可以使用$set属性来配置,这样也不用先查询,如果更新的数据比较少,可用性还是很好的:


PersonModel.update({_id:_id},{$set:{name:'MDragon'}},function(err){});

需要注意,Document的CRUD操作都是异步执行,callback第一个参数必须是err,而第二个参数各个方法不一样,update的callback第二个参数是更新的数量,如果要返回更新后的对象,则要使用如下方法


Person.findByIdAndUpdate(_id,{$set:{name:'MDragon'}},function(err,person){

console.log(person.name); //MDragon

});

类似的方法还有findByIdAndRemove,如同名字,只能根据id查询并作update/remove操作,操作的数据仅一条

新增

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


//使用Entity来增加一条数据

var krouky = new PersonModel({name:'krouky'});

krouky.save(callback);

//使用Model来增加一条数据

var MDragon = {name:'MDragon'};

PersonModel.create(MDragon,callback);

两种新增方法区别在于,如果使用Model新增时,传入的对象只能是纯净的JSON对象,不能是由Model创建的实体,原因是:由Model创建的实体krouky虽然打印是只有{name:'krouky'},但是krouky属于Entity,包含有Schema属性和Model数据库行为模型。如果是使用Model创建的对象,传入时一定会将隐藏属性也存入数据库,虽然3.x追加了默认严格属性,但也不必要增加操作的报错

删除

和新增一样,删除也有2种方式,但Entity和Model都使用remove方法

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

});

验证器

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是验证失败的错误信息

}

});

验证失败

如果验证失败,则会返回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属性

Middleware中间件

什么是中间件

中间件是一种控制函数,类似插件,能控制流程中的init、validate、save、remove`方法

中间件的分类

中间件分为两类

Serial串行

串行使用pre方法,执行下一个方法使用next调用


var schema = new Schema(...);

schema.pre('save',function(next){

//做点什么

next();

});

Parallel并行

并行提供更细粒度的操作


var schema = new Schema(...);

schema.pre('save',function(next,done){

//下一个要执行的中间件并行执行

next();

doAsync(done);

});

中间件特点

一旦定义了中间件,就会在全部中间件执行完后执行其他操作,使用中间件可以雾化模型,避免异步操作的层层迭代嵌套

使用范畴

复杂的验证

删除有主外关联的doc

异步默认

某个特定动作触发异步任务,例如触发自定义事件和通知

例如,可以用来做自定义错误处理


schema.pre('save',function(next){

var err = new Eerror('some err');

next(err);

});

entity.save(function(err){

console.log(err.message); //some err

});

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,498评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,668评论 3 406
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,857评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,305评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,308评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,747评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,078评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,080评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,649评论 1 327
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,644评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,760评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,352评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,076评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,490评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,651评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,353评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,828评论 2 367

推荐阅读更多精彩内容

  • Mongodb 配置选项 通常在mongod.conf中 配置文件 设置了配置文件后启动时以自定义的配置文件启动:...
    AkaTBS阅读 1,087评论 0 6
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简介安装SchemaSchema嵌套官网示例真·M...
    kirito_song阅读 357评论 0 18
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,688评论 18 399
  • 这部分主要是开源Java EE框架方面的内容,包括Hibernate、MyBatis、Spring、Spring ...
    杂货铺老板阅读 1,402评论 0 2
  • 整个11月不知道自己是怎么过来的:赶上工作更换岗位,新欢旧爱两头烧;还作死给自己报名了微课:Angie的时间管理特...
    KIS宁阅读 781评论 2 3