Mongoose学习参考文档

Schema不仅定义了文档结构使用性能,还可以有扩展插件实例方法静态方法复合索引文档生命周期钩子

Schema可以定义插件,并且插件具有良好的可拔插性,请有兴趣的读者继续往后阅读或者查阅官方资料。

2. Schema的扩展

2.1 实例方法

有的时候,我们创造的Schema不仅要为后面的ModelEntity提供公共的属性,还要提供公共的方法。

下面例子比快速通道的例子更加高级,可以进行高级扩展:

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 PersonModel({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,也可以反解firstlast属性

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

可供配置项有:safestrictcappedversionKeyautoIndex

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,相当于是给版本锁取名字。

具体怎么存入都是由MongooseMongoDB自己决定,当然,这个属性你也可以去除

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){});

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

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种方式,但EntityModel都使用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 查询子文档

如果childrenparent的子文档,可以通过如下方法查询到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来创建EntityEntity实体是一个特有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,则返回queryquery没有执行的预编译查询语句,该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 验证器
  1. required 非空验证

  2. min/max 范围验证(边值验证)

  3. enum/match 枚举验证/匹配验证

  4. 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 //错误消息

一旦验证失败,ModelEntity都将具有和err一样的errors属性

8.Middleware中间件

8.1 什么是中间件

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

8.2 中间件的分类

中间件分为两类

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

你要问前端开发难不难,我就得说计算机领域里常说的一句话,这句话就是『难的不会,会的不难』,对于不熟悉某领域技术的人来说,因为不了解所以产生神秘感,神秘感就会让人感觉很难,也就是『难的不会』;当学会这项技术之后,知道什么什么技术能做到什么做不到,只是做起来花多少时间的问题而已,没啥难的,所以就是『会的不难』。

我特地针对初学者整理一套前端学习资料,免费分享给大家,戳这里即可免费领取

前端路线图

似插件,能控制流程中的init、validatesaveremove`方法

8.2 中间件的分类

中间件分为两类

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-OZHZ43QW-1713547081999)]

[外链图片转存中…(img-dSMohTsE-1713547082000)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-jxvKLnvR-1713547082000)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-9MKrH7rl-1713547082000)]

最后

你要问前端开发难不难,我就得说计算机领域里常说的一句话,这句话就是『难的不会,会的不难』,对于不熟悉某领域技术的人来说,因为不了解所以产生神秘感,神秘感就会让人感觉很难,也就是『难的不会』;当学会这项技术之后,知道什么什么技术能做到什么做不到,只是做起来花多少时间的问题而已,没啥难的,所以就是『会的不难』。

我特地针对初学者整理一套前端学习资料,免费分享给大家,戳这里即可免费领取

[外链图片转存中…(img-1m4JuEe2-1713547082001)]

vue.js的36个技巧

DuktapeJava 是针对 Android 平台封装的 Duktape  JavaScript 引擎;实现Javascript 和 Java的无缝调用。 初始化DuktapeEngnine   @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); duktapeEngine = new DuktapeEngine(); duktapeEngine.put("activity",this); duktapeEngine.execute(AssetScript.toScript(getBaseContext(), "duk.js")); duktapeEngine.call("activityListener", "onCreate", savedInstanceState); } @Override protected void onDestroy() { if (duktapeEngine != null) { duktapeEngine.destory(); duktapeEngine = null; } super.onDestroy(); }   duk.js javascript 代码示例   importClass("com.furture.react.R") importClass("android.view.View.OnClickListener") importClass("android.widget.Toast") importClass("java.lang.Runnable") var activityListener = {}; activityListener.onCreate = function(){ print("activity onCreate onJavaScript"); activity.setContentView(R.layout.activity_duk) button1 = activity.findViewById(R.id.button1); button1.setOnClickListener(new OnClickListener(function(){ Toast.makeText(activity, "Button1 Clicked", Toast.LENGTH_SHORT).show(); var intent = new Intent(activity, "com.furture.react.activity.DetailActivity"); activity.startActivity(intent); })); button2 = activity.findViewById(R.id.button2); button2.setOnClickListener(new OnClickListener({ onClick:function(){ Toast.makeText(activity, "Button2 Clicked", Toast.LENGTH_SHORT).show(); } })); } activityListener.onStart = function(){ print("activity onStart"); } activityListener.onResume = function(){ print("activity onResume"); } activityListener.onPause = function(){ print("activity onPause"); } activityListener.onStop = function(){ print("activity onStop"); } activityListener.finish = function(){ print("activity finish" num); }   Java 和 Javascript 的无缝调用 javascript 调用java方法示例: importClass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值