Node.js中的Mongoose实用笔记
2015-10-22 14:37
501 查看
mongoose官网 http://mongoosejs.com/
##一、快速通道
###1.1 名词解释
注意:
1.本学习文档采用严格命名方式来区别不同对象,例如:
2.
##1.2 准备工作
1.首先你必须安装MongoDB和NodeJS
2.在项目只能够创建一个数据库连接,如下:
3.打开本机
注意:
成功开启数据库后,就可以执行数据库相应操作,假设以下代码都在回调中处理
4.定义一个
5.将该
6.用
7.我们甚至可以为此
8.
9.如果要执行查询,需要依赖
注意:
1. 具体的如何配置
2.
##二、新手指引
如果您还不清楚
###1. Schema——纯洁的数据库原型
####1.1 什么是Schema
我理解
他仅仅只是数据库模型在程序片段中的一种表现,或者是数据属性模型
####1.2 如何定义Schema
####1.3 什么是Schema.Type
####1.4 Schema.Types
####1.5 关于Buffer
####1.6 关于Mixed
混合类型因为没有特定约束,因此可以任意修改,一旦修改了原型,则必须调用
####1.7 关于ObjectId
主键,一种特殊而且非常重要的类型,每个
该类型的值由系统自己生成,从某种意义上几乎不会重复,生成过程比较复杂,有兴趣的朋友可以查看源码。
####1.8 关于Array
####1.9 附言
###2. Schema的扩展
####2.1 实例方法
有的时候,我们创造的
下面例子比快速通道的例子更加高级,可以进行高级扩展:
使用如下:
####2.2 静态方法
静态方法在
####2.3 索引
索引或者复合索引能让搜索更加高效,默认索引就是主键索引
####2.4 虚拟属性
如果每次想使用全名就得这样
显然这是很麻烦的,我们可以定义
那么就能用
####2.5 配置项
在使用
或者使用
可供配置项有:
#####2.5.1 safe——安全属性(默认安全)
一般可做如下配置:
当然我们也可以这样
#####2.5.2 strict——严格配置(默认启用)
确保
如果取消严格选项,
该选项也可以在构造实例时使用,例如:
注意:
#####2.5.3 shardKey
需要
#####2.5.4 capped——上限设置
如果有数据库的批量操作,该属性能限制一次操作的量,例如:
当然该参数也可是JSON对象,包含size、max、autiIndexId属性
#####2.5.5 versionKey——版本锁
版本锁是
此时存入数据库的版本锁就不是
具体怎么存入都是由
除非你知道你在做什么,并且你知道这样做的后果
#####2.5.6 autoIndex——自动索引
该内容将在索引章节单独讲解
###3. Documents
注意:
Schema的strict严格配置
####3.1 查询
查询内容过多,专题讲解
####3.2 更新
有许多方式来更新文件,以下是常用的传统方式:
这里,利用
当然这样的更新很麻烦,可以使用
需要注意,
类似的方法还有
####3.3 新增
如果是
两种新增方法区别在于,如果使用
####3.4 删除
和新增一样,删除也有2种方式,但
###4.Sub Docs
如同
简单的说就是一个
如果子文档在更新时出现错误,将直接报在父类文档中,可以这样处理:
####4.1 查询子文档
如果
####4.2 新增、删除、更新
子文档是父文档的一个属性,因此按照属性的操作即可,不同的是在新增父类的时候,子文档是会被先加入进去的。
如果
###5.Model
####5.1 什么是Model
####5.2 如何创建Model
你必须通过
####5.2 操作Model
该模型就能直接拿来操作,具体查看
注意:
你可以使用
###6.Query
查询是数据库中运用最多也是最麻烦的地方,这里对
####6.1 查询的方式
通常有2种查询方式,一种是
#####6.1.1 直接查询
在查询时带有回调函数的,称之为直接查询,查询的条件往往通过
具体的查询参数,请查询
#####6.1.2 链式查询
在查询时候,不带回调,而查询条件通过
这种方式相对直接查询,分的比较明细,如果不带
因为
###7.Validation
数据的存储是需要验证的,不是什么数据都能往数据库里丢或者显示到客户端的,数据的验证需要记住以下规则:
验证始终定义在
验证是一个内部中间件
验证是在一个
验证是异步递归的,如果你的
验证并不关心错误类型,而通过
####7.1 验证器
以下是综合案例:
####7.2 验证失败
如果验证失败,则会返回
一旦验证失败,
###8.Middleware中间件
####8.1 什么是中间件
中间件是一种控制函数,类似插件,能控制流程中的
####8.2 中间件的分类
中间件分为两类
#####8.2.1 Serial串行
串行使用
#####8.2.2 Parallel并行
并行提供更细粒度的操作
####8.3 中间件特点
一旦定义了中间件,就会在全部中间件执行完后执行其他操作,使用中间件可以雾化模型,避免异步操作的层层迭代嵌套
####8.4 使用范畴
复杂的验证
删除有主外关联的
异步默认
某个特定动作触发异步任务,例如触发自定义事件和通知
例如,可以用来做自定义错误处理
node 安装:http://www.infoq.com/cn/articles/nodejs-npm-install-config
Win7下MongoDB安装 :http://www.mkyong.com/mongodb/how-to-install-mongodb-on-windows/
MongoDB 安装步骤总结:
[/code]
1、创建node项目
2、初始化相关组件
3、运行app.js,访问:http://localhost:3000,查看页面,如看到以下页面则说明页面访问成功:
4、此时项目默认使用*.ejs作为页面的模板,为了方便我们吧ejs的后缀修改html
a) 修改app.js中内容:
b) 重命名views下的index.ejs为index.html
5、重新启动服务访问,如果页面可以正常显示则说明修改成功。
6、现在开始写增删改查的详细方法
1)修改路由(app.js)
2)在routes目录下创建model.js文件,内容如下:
3)修改index.js,添加逻辑方法
4)index.html页面
5)add.html页面
6) 修改页面modify.html
到此,增删改查已经完成,启动项目即可。
Mongoose是MongoDB的一个对象模型工具,可以工作于异步环境下。
定义一个模型很容易:
?
回到顶部
推荐通过NPM方式安装:
或者,你可以从Github仓库中获取代码,然后解压:
然后就可以在应用中将mongoose模块包含进来
回到顶部
首先,我们需要定义一个连接。如果你的应用只使用一个数据库,可以使用mongoose.connect,如果需要创建附加数据库连接,使用mongoose.createConnection。
connect和createConnection都能连接mongodb数据库,支持以URI或参数(host,database,port)的形式。
?
连接一旦建立成功,该连接实例的open事件就被触发。如果你使用的是mongoose.connect方式,连接对象为mongoose.connection;否则,mongoose.createConnection返回的是Connection对象。
切记!Mongoose在与数据库真正建立连接之前便缓存了所有的命令,这就意味着你在定义模型、执行查询时不必非要确认与MongoDB数据库的连接是否已经建立。(一回@CSSer注:异步是MongoDB等与传统数据库的重大区别)
回到顶部
模型是通过模式接口(Schema interface)定义的,如:
?
除了定义文档结构和你要存储的数据类型外,模式(Schema)还用于以下定义:
Validators (异步和同步)
Defaults - 默认值
Getters
Setters
Indexes - 索引
Middleware - 中间件
Methods definition - 方法定义
Statics definition - 静态定义
Plugins - 插件
下面的代码向我们展示了这些功能的一部分:
?
你可以查看几乎包含所有模型定义的 示例 。
回到顶部
当通过mongoose.model('ModelName', mySchema)定义了一个模型之后,我们可以通过相同的函数来访问它:
接下来我们可以将模型的实例保存下来:
或者我们也可以从同样的的集合(collection)中找到文档(documents):
也可以调用findOne, findById, update等等方法,更多的细节请阅读 API文档 。
回到顶部
还记得在第一个示例的代码片段中,我们在模式中定义了一个键(key):
这里的Comments是我们已经创建的模式(Schema),这就是说创建嵌入文档跟下面的代码看起来一样简单:
?
用同样的方式删除模式:
?
嵌入文档拥有与模型相同的功能,包括Defaults、validators、middleware等。当发生错误时,它会冒泡到save()错误回调函数,这里错误处理是一个单元。
Mongoose interacts with your embedded documents in arrays atomically, out of the box.
回到顶部
中间件是Mongoose 1.0推出的最激动人心的功能之一,它让我们可以不用再受嵌套回调函数的折磨了。
中间件定义在模式级别(Schema level),当方法初始化时(文档与MongoDB数据初始化后)、保存数据时(文档或嵌入文档保存后)生效。
中间件有两种类型,它们由定义的函数签名确定(即函数接受的参数)。
顺序(Serial)中间件定义如下:
?
当每个中间件调用下一个时,它们按顺序执行。
并行(Parallel)中间件提供更细粒度的控制,其定义如下:
?
Parallel 中间件可以立刻next(),但是只有当所有parallel中间件调用done()之后最后的参数才被执行。
回到顶部
如果任一个中间件调用next或done时抛出了错误,执行流会中断,错误会被作为参数传入回调函数。
例如:
?
回到顶部
可以在 http://mongoosejs.com 找到相关API文档。
1.Mongoose学习参考文档
前言:本学习参考文档仅供参考,如有问题,师请雅正##一、快速通道
###1.1 名词解释
Schema: 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力
Model: 由
Schema发布生成的模型,具有抽象属性和行为的数据库操作对
Entity: 由
Model创建的实体,他的操作也会影响数据库
注意:
1.本学习文档采用严格命名方式来区别不同对象,例如:
var PersonSchema; //Person的文本属性 var PersonModel; //Person的数据库模型 var PersonEntity; //Person实体
2.
Schema、
Model、
Entity的关系请牢记,
Schema生成
Model,
Model创造
Entity,
Model和
Entity都可对数据库操作造成影响,但
Model比
Entity更具操作性。
##1.2 准备工作
1.首先你必须安装MongoDB和NodeJS
2.在项目只能够创建一个数据库连接,如下:
var mongoose = require('mongoose'); //引用mongoose模块 var db = mongoose.createConnection('localhost','test'); //创建一个数据库连接
3.打开本机
localhost的
test数据库时,我们可以监测是否有异常
db.on('error',console.error.bind(console,'连接错误:')); db.once('open',function(){ //一次打开记录 });
注意:
成功开启数据库后,就可以执行数据库相应操作,假设以下代码都在回调中处理
4.定义一个
Schema
var PersonSchema = new mongoose.Schema({ name:String //定义一个属性name,类型为String });
5.将该
Schema发布为
Model
var PersonModel = db.model('Person',PersonSchema); //如果该Model已经发布,则可以直接通过名字索引到,如下: //var PersonModel = db.model('Person'); //如果没有发布,上一段代码将会异常
6.用
Model创建
Entity
var personEntity = new PersonModel({name:'Krouky'}); //打印这个实体的名字看看 console.log(personEntity.name); //Krouky
7.我们甚至可以为此
Schema创建方法
//为Schema模型追加speak方法 PersonSchema.methos.speak = function(){ console.log('我的名字叫'+this.name); } var PersonModel = db.model('Person',PersonSchema); var personEntity = new PersonModel({name:'Krouky'}); personEntity.speak();//我的名字叫Krouky
8.
Entity是具有具体的数据库操作
CRUD的
personEntity.save(); //执行完成后,数据库就有该数据了
9.如果要执行查询,需要依赖
Model,当然
Entity也是可以做到的
PersonModel.find(function(err,persons){ //查询到的所有person });
注意:
1. 具体的如何配置
Schema、
Model以及
Model和
Entity的相关操作,我们会在后面进行
2.
Model和
Entity都有能影响数据库的操作,但仍有区别,后面我们也会做解释
##二、新手指引
如果您还不清楚
Mongoose是如何工作的,请参看第一章快速通道快速浏览他的用法吧
###1. Schema——纯洁的数据库原型
####1.1 什么是Schema
我理解
Schema仅仅只是一断代码,他书写完成后程序依然无法使用,更无法通往数据库端
他仅仅只是数据库模型在程序片段中的一种表现,或者是数据属性模型
####1.2 如何定义Schema
var BlogSchema = new Schema({ title:String, author:String //new Schema()中传入一个JSON对象,该对象形如 xxx:yyyy , /xxx是一个字符串,定义了属性,yyy是一个Schema.Type,定义了属性类型 });
####1.3 什么是Schema.Type
Schema.Type是由
Mongoose内定的一些数据类型,基本数据类型都在其中,他也内置了一些
Mongoose特有的
Schema.Type。当然,你也可以自定义
Schema.Type,只有满足
Schema.Type的类型才能定义在
Schema内。
####1.4 Schema.Types
NodeJS中的基本数据类型都属于
Schema.Type,另外
Mongoose还定义了自己的类型
//举例: var ExampleSchema = new Schema({ name:String, binary:Buffer, living:Boolean, updated:Date, age:Number, mixed:Schema.Types.Mixed, //该混合类型等同于nested _id:Schema.Types.ObjectId, //主键 _fk:Schema.Types.ObjectId, //外键 array:[], arrOfString:[String], arrOfNumber:[Number], arrOfDate:[Date], arrOfBuffer:[Buffer], arrOfBoolean:[Boolean], arrOfMixed:[Schema.Types.Mixed], arrOfObjectId:[Schema.Types.ObjectId] nested:{ stuff:String, } });
####1.5 关于Buffer
Buffer和
ArrayBuffer是
Nodejs两种隐藏的对象,相关内容请查看
NodeJS-API
####1.6 关于Mixed
Schema.Types.Mixed是
Mongoose定义个混合类型,该混合类型如果未定义具体形式。因此,如果定义具体内容,就直接使用
{}来定义,以下两句等价
var AnySchema = new Schema({any:{}}); var AnySchema = new Schema({any:Schema.Types.Mixed});
混合类型因为没有特定约束,因此可以任意修改,一旦修改了原型,则必须调用
markModified()
person.anything = {x:[3,4,{y:'change'}]} person.markModified('anything');//传入anything,表示该属性类型发生变化 person.save();
####1.7 关于ObjectId
主键,一种特殊而且非常重要的类型,每个
Schema都会默认配置这个属性,属性名为
_id,除非自己定义,方可覆盖
var mongoose = require('mongoose'); var ObjectId = mongoose.Schema.Types.ObjectId; var StudentSchema = new Schema({}); //默认会有_id:ObjectId var TeacherSchema = new Schema({id:ObjectId});//只有id:ObjectId
该类型的值由系统自己生成,从某种意义上几乎不会重复,生成过程比较复杂,有兴趣的朋友可以查看源码。
####1.8 关于Array
Array在
JavaScript编程语言中并不是数组,而是集合,因此里面可以存入不同的值,以下代码等价:
var ExampleSchema1 = new Schema({array:[]}); var ExampleSchema2 = new Schema({array:Array}); var ExampleSchema3 = new Schema({array:[Schema.Types.Mixed]}); var ExampleSchema4 = new Schema({array:[{}]});
####1.9 附言
Schema不仅定义了
文档结构和
使用性能,还可以有
扩展插件、
实例方法、
静态方法、
复合索引、
文档生命周期钩子
Schema可以定义插件,并且插件具有良好的可拔插性,请有兴趣的读者继续往后阅读或者查阅官方资料。
###2. Schema的扩展
####2.1 实例方法
有的时候,我们创造的
Schema不仅要为后面的
Model和
Entity提供公共的属性,还要提供公共的方法。
下面例子比快速通道的例子更加高级,可以进行高级扩展:
var PersonSchema = new Schema({name:String,type:String}); //查询类似数据 PersonSchema.methods.findSimilarTypes = function(cb){ return this.model('Person').find({type:this.type},cb); }
使用如下:
var PersonModel = mongoose.model('Person',PersonSchema); var krouky = new PersonSchema({name:'krouky',type:'前端工程师'}); krouky.findSimilarTypes(function(err,persons){ //persons中就能查询到其他前端工程师 });
####2.2 静态方法
静态方法在
Model层就能使用,如下:
PersonSchema.statics.findByName = function(name,cb){ this.find({name:new RegExp(name,'i'),cb}); } var PersonModel = mongoose.model('Person',PersonSchema); PersonModel.findByName('krouky',function(err,persons){ //找到所有名字叫krouky的人 });
####2.3 索引
索引或者复合索引能让搜索更加高效,默认索引就是主键索引
ObjectId,属性名为
_id,索引会作为一个专题来讲解
####2.4 虚拟属性
Schema中如果定义了虚拟属性,那么该属性将不写入数据库,例如:
var PersonSchema = new Schema({ name:{ first:String, last:String } }); var PersonModel = mongoose.model('Person',PersonSchema); var krouky = new PersonModel({ name:{first:'krouky',last:'han'} });
如果每次想使用全名就得这样
console.log(krouky.name.first + ' ' + krouky.name.last);
显然这是很麻烦的,我们可以定义
虚拟属性:
PersonSchema.virtual('name.full').get(function(){ return this.name.first + ' ' + this.name.last; });
那么就能用
krouky.name.full来调用全名了,反之如果知道full,也可以反解
first和
last属性
PersonSchema.virtual('name.full').set(function(name){ var split = name.split(' '); this.name.first = split[0]; this.name.last = split[1]; }); var PersonModel = mongoose.model('Person',PersonSchema); var krouky = new PersonModel({}); krouky.name.full = 'krouky han';//会被自动分解 console.log(krouky.name.first);//krouky
####2.5 配置项
在使用
new Schema(config)时,我们可以追加一个参数
options来配置
Schema的配置,形如:
var ExampleSchema = new Schema(config,options);
或者使用
var ExampleSchema = new Schema(config); ExampleSchema.set(option,value);
可供配置项有:
safe、
strict、
capped、
versionKey、
autoIndex
#####2.5.1 safe——安全属性(默认安全)
一般可做如下配置:
new Schema({...},{safe:true});
当然我们也可以这样
new Schema({...},{safe:{j:1,w:2,wtimeout:10000}});
j表示做1份日志,
w表示做2个副本(尚不明确),超时时间10秒
#####2.5.2 strict——严格配置(默认启用)
确保
Entity的值存入数据库前会被自动验证,如果你没有充足的理由,请不要停用,例子:
var ThingSchema = new Schema({a:String}); var ThingModel = db.model('Thing',SchemaSchema); var thing = new Thing({iAmNotInTheThingSchema:true}); thing.save();//iAmNotInTheThingSchema这个属性将无法被存储
如果取消严格选项,
iAmNotInTheThingSchema将会被存入数据库
该选项也可以在构造实例时使用,例如:
var ThingModel = db.model('Thing'); var thing1 = new ThingModel(doc,true); //启用严格 var thing2 = new ThingModel(doc,false); //禁用严格
注意:
strict也可以设置为
throw,表示出现问题将会抛出错误
#####2.5.3 shardKey
需要
mongodb做分布式,才会使用该属性
#####2.5.4 capped——上限设置
如果有数据库的批量操作,该属性能限制一次操作的量,例如:
new Schema({...},{capped:1024}); //一次操作上线1024条数据
当然该参数也可是JSON对象,包含size、max、autiIndexId属性
new Schema({...},{capped:{size:1024,max:100,autoIndexId:true}});
#####2.5.5 versionKey——版本锁
版本锁是
Mongoose默认配置(__v属性)的,如果你想自己定制,如下:
new Schema({...},{versionKey:'__someElse'});
此时存入数据库的版本锁就不是
__v属性,而是
__someElse,相当于是给版本锁取名字。
具体怎么存入都是由
Mongoose和
MongoDB自己决定,当然,这个属性你也可以去除
new Schema({...},{versionKey:false});
除非你知道你在做什么,并且你知道这样做的后果
#####2.5.6 autoIndex——自动索引
该内容将在索引章节单独讲解
###3. Documents
Document是与
MongoDB文档一一对应的模型,
Document可等同于
Entity,具有属性和操作性
注意:
Document的`CRUD都必须经过严格验证的,参看2.5.2
Schema的strict严格配置
####3.1 查询
查询内容过多,专题讲解
####3.2 更新
有许多方式来更新文件,以下是常用的传统方式:
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操作,操作的数据仅一条
####3.3 新增
如果是
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追加了默认严格属性,但也不必要增加操作的报错
####3.4 删除
和新增一样,删除也有2种方式,但
Entity和
Model都使用
remove方法
###4.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 } }); //其实就是匿名混合模式
###5.Model
####5.1 什么是Model
Model模型,是经过
Schema构造来的,除了
Schema定义的数据库骨架以外,还具有数据库行为模型,他相当于管理数据库属性、行为的类
####5.2 如何创建Model
你必须通过
Schema来创建,如下:
//先创建Schema var TankSchema = new Schema({ name:'String', size:'String' }); //通过Schema创建Model var TankModel = mongoose.model('Tank',TankSchema);
####5.2 操作Model
该模型就能直接拿来操作,具体查看
API,例如:
var tank = {'something',size:'small'}; TankModel.create(tank);
注意:
你可以使用
Model来创建
Entity,
Entity实体是一个特有
Model具体对象,但是他并不具备
Model的方法,只能用自己的方法。
//通过Model创建Entity var tankEntity = new TankModel('someother','size:big'); tankEntity.save();
###6.Query
查询是数据库中运用最多也是最麻烦的地方,这里对
Query解读的并不完善,仅仅是自己的一点领悟而已。
####6.1 查询的方式
通常有2种查询方式,一种是
直接查询,一种是
链式查询(2种查询都是自己命名的)
#####6.1.1 直接查询
在查询时带有回调函数的,称之为直接查询,查询的条件往往通过
API来设定,例如:
PersonModel.findOne({'name.last':'dragon'},'some select',function(err,person){ //如果err==null,则person就能取到数据 });
具体的查询参数,请查询
API
#####6.1.2 链式查询
在查询时候,不带回调,而查询条件通过
API函数来制定,例如:
var query = PersonModel.findOne({'name.last':'dragon'}); query.select('some select'); query.exec(function(err,pserson){ //如果err==null,则person就能取到数据 });
这种方式相对直接查询,分的比较明细,如果不带
callback,则返回
query,
query没有执行的预编译查询语句,该
query对象执行的方法都将返回自己,只有在执行
exec方法时才执行查询,而且必须有回调。
因为
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);
###7.Validation
数据的存储是需要验证的,不是什么数据都能往数据库里丢或者显示到客户端的,数据的验证需要记住以下规则:
验证始终定义在
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属性
###8.Middleware中间件
####8.1 什么是中间件
中间件是一种控制函数,类似插件,能控制流程中的
init、validate
、save
、remove`方法
####8.2 中间件的分类
中间件分为两类
#####8.2.1 Serial串行
串行使用
pre方法,执行下一个方法使用
next调用
var schema = new Schema(...); schema.pre('save',function(next){ //做点什么 next(); });
#####8.2.2 Parallel并行
并行提供更细粒度的操作
var schema = new Schema(...); schema.pre('save',function(next,done){ //下一个要执行的中间件并行执行 next(); doAsync(done); });
####8.3 中间件特点
一旦定义了中间件,就会在全部中间件执行完后执行其他操作,使用中间件可以雾化模型,避免异步操作的层层迭代嵌套
####8.4 使用范畴
复杂的验证
删除有主外关联的
doc
异步默认
某个特定动作触发异步任务,例如触发自定义事件和通知
例如,可以用来做自定义错误处理
schema.pre('save',function(next){ var err = new Eerror('some err'); next(err); }); entity.save(function(err){ console.log(err.message); //some err });
2.node.js + express(ejs) + mongodb(mongoose) 增删改实例
node 和 mongodb安装不做为本文讲解,想了解的朋友可以通过一下链接配置环境:node 安装:http://www.infoq.com/cn/articles/nodejs-npm-install-config
Win7下MongoDB安装 :http://www.mkyong.com/mongodb/how-to-install-mongodb-on-windows/
MongoDB 安装步骤总结:
1、解压目录到d盘 mongodb
2、安装目录的下新建文件[code]mongo.config文件
[/code]
##store data here dbpath=D:\mongodb\data ##all output go here logpath=D:\mongodb\log\mongo.log ##log read and write operations diaglog=3
3、d:\mongodb\bin>mongod --config D:\mongodb\mongo.config
4、d:\mongodb\bin> mongod --config D:\mongodb\mongo.config --install net start MongoDB net stop MongoDB mongod --remove
1、创建node项目
express -e demo
2、初始化相关组件
npm install// 初始化ejsnpm install ejs// 初始化express组件npm install express// 初始化mongdb组合的组件 npm install mongoosenpm install express-mongoose// node 默认启动后修改文件,页面不会立即体现,通过该组件可以使修改的文件实时在页面体现出来npm install supervisor
3、运行app.js,访问:http://localhost:3000,查看页面,如看到以下页面则说明页面访问成功:
4、此时项目默认使用*.ejs作为页面的模板,为了方便我们吧ejs的后缀修改html
a) 修改app.js中内容:
var ejs = require('ejs'); //增加app.engine('html',ejs.__express); //增加//将app.set('view engine', 'ejs'); 修改为app.set('view engine', 'html');
b) 重命名views下的index.ejs为index.html
5、重新启动服务访问,如果页面可以正常显示则说明修改成功。
6、现在开始写增删改查的详细方法
1)修改路由(app.js)
app.get('/add.html',routes.add); //跳转到添加页面 app.post('/add.html', routes.create);//添加记录 app.get('/del.html',routes.delById);//删除 app.get('/modify.html', routes.toModify); //跳转到修改页面 app.post('/modify.html', routes.modify);//修改数据
2)在routes目录下创建model.js文件,内容如下:
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var demoSchema = new Schema({ uid : String, title:String, content:String, createTime : { type: Date, default: Date.now } }); exports.Demo = mongoose.model('Demo',demoSchema); //此处Demo会自动对应数据库中的demos表,不知道是不是可以修改,反正我是这个认为的。
3)修改index.js,添加逻辑方法
/* * GET home page. */ var mongoose = require('mongoose'); var model = require('./model'); var Demo = model.Demo; mongoose.connect('mongodb://localhost/monkey'); exports.index = function(req, res){ //查询所有数据,保存到demos中,在页面循环输出 Demo.find(function(err,docs){ res.render('index', { title:'Express Demo Example', demos:docs }); }); }; //跳转到添加页面 exports.add = function(req, res) { console.log('----here'); res.render('add.html', {title :'添加 demo list'}); }; //创建新纪录 exports.create = function(req, res){ var demo = new Demo({ uid : req.body.uid, title: req.body.title, content : req.body.content }); console.log('create----'); demo.save(function(err,doc){ console.log(doc); res.redirect('/'); }); }; // 根据id删除相应的记录 exports.delById = function(req, res) { var id = req.query.id; console.log('id = ' + id); if(id && '' != id) { console.log('----delete id = ' + id); Demo.findByIdAndRemove(id, function(err, docs) { console.log('delete-----'+ docs); res.redirect('/'); }); } }; // 查询对应修改记录,并跳转到修改页面 exports.toModify = function(req, res) { var id = req.query.id; console.log('id = ' + id); if(id && '' != id) { console.log('----delete id = ' + id); Demo.findById(id, function(err, docs){ console.log('-------findById()------' + docs); res.render('modify.html',{title:'修改ToDos',demo:docs}); }); }; }; //修改相应的值 exports.modify = function(req, res) { var demo = { uid : req.body.uid, title: req.body.title, content : req.body.content }; var id = req.body.id; //因为是post提交,所以不用query获取id if(id && '' != id) { console.log('----update id = ' + id + "," + demo); Demo.findByIdAndUpdate(id, demo,function(err, docs) { console.log('update-----'+ docs); res.redirect('/'); }); } };
4)index.html页面
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> <style type="text/css"> table { border:1px solid green;} table thead tr th{ border:1px solid green;} table tbody tr td{ border:1px solid green;} </style> </head> <body> <p> <a href="http://houzhiqingjava.blog.163.com/blog/add.html">增加</a> </p> <h1>DEMO List</h1> <table> <thead> <tr> <th>id</th> <th>uid</th> <th>title</th> <th>content</th> <th>createTime</th> <th>操作</th> </tr> </thead> <tbody> <% demos.forEach(function( demo ){ %> <tr> <td><%=demo._id%></td> <td><%= demo.uid %></td> <td><%= demo.title %></td> <td><%= demo.content %></td> <td><%=demo.createTime%></td> <td><a href="http://houzhiqingjava.blog.163.com/blog/del.html?id=<%=demo._id%>">Delete</a> | <a href="http://houzhiqingjava.blog.163.com/blog/modify.html?id=<%=demo._id%>">Update</a></td> </tr> <% }); %> </tbody> </table> </body> </html>
5)add.html页面
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> <style type="text/css"> </style> </head> <body> <h1> 增加 </h1> <form method="post"> uid : <input type="text" name="uid"/><br/> title:<input type="text" name="title"/><br/> content:<textarea name="content"></textarea><br/> <input type="submit" value="submit"/> <input type="reset" value="reset"/> </form> </body> </html>
6) 修改页面modify.html
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1> 修改 </h1> <form method="post"> uid : <input type="text" name="uid" value="<%=demo.uid%>"/><br/> title:<input type="text" name="title" value="<%=demo.title%>"/><br/> content:<textarea name="content"><%=demo.content%></textarea><br/> <input type="hidden" name="id" value="<%=demo._id%>"/> <input type="submit" value="submit"/> <input type="reset" value="reset"/> </form> </body> </html>
到此,增删改查已经完成,启动项目即可。
3.Mongoose--让Node.js更好的操作MongoDB数据库
Mongoose是什么
Mongoose是MongoDB的一个对象模型工具,可以工作于异步环境下。定义一个模型很容易:
?
安装
推荐通过NPM方式安装:$ npm install mongoose
或者,你可以从Github仓库中获取代码,然后解压:
$ git clone git@github.com:LearnBoost/mongoose.git support/mongoose/
// 将模块添加至NodeJS可以找到的环境路径中require.paths.unshift('support/mongoose/lib');
然后就可以在应用中将mongoose模块包含进来
require('mongoose');
回到顶部
连接到MongoDB数据库
首先,我们需要定义一个连接。如果你的应用只使用一个数据库,可以使用mongoose.connect,如果需要创建附加数据库连接,使用mongoose.createConnection。connect和createConnection都能连接mongodb数据库,支持以URI或参数(host,database,port)的形式。
?
切记!Mongoose在与数据库真正建立连接之前便缓存了所有的命令,这就意味着你在定义模型、执行查询时不必非要确认与MongoDB数据库的连接是否已经建立。(一回@CSSer注:异步是MongoDB等与传统数据库的重大区别)
回到顶部
定义模型
模型是通过模式接口(Schema interface)定义的,如:?
Validators (异步和同步)
Defaults - 默认值
Getters
Setters
Indexes - 索引
Middleware - 中间件
Methods definition - 方法定义
Statics definition - 静态定义
Plugins - 插件
下面的代码向我们展示了这些功能的一部分:
?
回到顶部
访问模型
当通过mongoose.model('ModelName', mySchema)定义了一个模型之后,我们可以通过相同的函数来访问它:var myModel = mongoose.model('ModelName');
接下来我们可以将模型的实例保存下来:
var instance = new myModel();instance.my.key = 'csser';instance.save(function (err) { //});
或者我们也可以从同样的的集合(collection)中找到文档(documents):
myModel.find({}, function (err, docs) { // docs.forEach});
也可以调用findOne, findById, update等等方法,更多的细节请阅读 API文档 。
回到顶部
嵌入文档
还记得在第一个示例的代码片段中,我们在模式中定义了一个键(key):comments: [Comments]
这里的Comments是我们已经创建的模式(Schema),这就是说创建嵌入文档跟下面的代码看起来一样简单:
?
?
Mongoose interacts with your embedded documents in arrays atomically, out of the box.
回到顶部
中间件
中间件是Mongoose 1.0推出的最激动人心的功能之一,它让我们可以不用再受嵌套回调函数的折磨了。中间件定义在模式级别(Schema level),当方法初始化时(文档与MongoDB数据初始化后)、保存数据时(文档或嵌入文档保存后)生效。
中间件有两种类型,它们由定义的函数签名确定(即函数接受的参数)。
顺序(Serial)中间件定义如下:
?
并行(Parallel)中间件提供更细粒度的控制,其定义如下:
?
回到顶部
错误处理
如果任一个中间件调用next或done时抛出了错误,执行流会中断,错误会被作为参数传入回调函数。例如:
?
API 文档
可以在 http://mongoosejs.com 找到相关API文档。
相关文章推荐
- Zookeeper可视化工具。 ZK 安装 node-zk-browser。2015.10.22亲测可用
- 用node-webkit把web应用打包成桌面应用
- node express 学习2
- Node.JS 学习路线图
- 使用forever运行nodejs应用
- NodeJS概述
- Nodejs和一个简单的web页面消息推送服务
- 浅谈node.js中async异步编程
- Node学习记录
- LeetCode:Delete Node in a Linked List
- Webpack 实现 Node.js 代码热替换
- leetcode: Swap Nodes in Pairs
- LeetCode Populating Next Right Pointers in Each Node & Populating Next Right Pointers in Each Node I
- Node.js巧妙实现Web应用代码热更新
- Event Sourcing - ENode(一)
- [LeetCode] Delete Node in a Linked List
- Node.js巧妙实现Web应用代码热更新
- Webpack 实现 Node.js 代码热替换
- 浅谈node.js中async异步编程
- 第 三 十 七 天:增 加 inode 和 挂 载 ios 镜 像