Mongoose学习全面理解(推荐)


Posted in Javascript onJanuary 21, 2017

一、创建schemas

创建schemas的方式:

var userSchema = new mongoose.Schema({
   name: String,
   email: String,
   createdOn: Date
 });

schemas中的数据类型有以下几种:

  •  String
  •  Number
  •  Date
  •  Boolean
  •  Buffer
  •  ObjectId
  •  Mixed
  •  Array

特别需要说明一下ObjectId类型和Mixed类型以及Array类型,在schemas中声明这几种类型的方式如下:

//ObjectId就类似于唯一键值
projectSchema.add({
  owner: mongoose.Schema.Types.ObjectId
});
//混合类型,顾名思义,就是说里面可以放置任意类型的数据,有两种方式创建该类型数据
//方式一:直接赋予一个空的字面量对象
vardjSchema= new mongoose.Schema({
  mixedUp: {}
});
//方式二:根据Schemas.Types中值来赋予
vardjSchema= new mongoose.Schema({
  mixedUp: Schema.Types.Mixed
});
//Array类型数据有两种创建方式,一种是简单数组创建:
var userSchema = new mongoose.Schema({
  name: String,
  emailAddresses: [String]
});
//第二种方式就是复杂类型数据数组,例如我们可以再数组中添加不同类型的schemas:
var emailSchema = new mongoose.Schema({
  email: String,
  verified: Boolean
});
var userSchema = new mongoose.Schema({
  name: String,
  emailAddresses: [emailSchema]
});
//注意:如果定义一个空的数据的话,则会创建为一个混合类型数据的数组:
var emailSchema = new mongoose.Schema({
  email: String,
  verified: Boolean
});
var userSchema = new mongoose.Schema({
  name: String,
  emailAddresses: [emailSchema]
});

我们可以给schema创建静态方法,这个静态方法将来会用在Model中,创建该静态方法需要在创建完成schema之后,在Model编译之前:

projectSchema.statics.findByUserID = function (userid, callback) {
  this.find({ createdBy: userid }, '_id projectName', {sort: 'modifiedOn'}, callback);
 };

在其对应的模型创建完成并编译后,我们就可以像下面这样来调用该静态方法了:

Model.findByUserID(userid,callback);

该静态方法会返回一个JSON格式的数据,这在我们使用AJAX技术来加载网页数据的时候会比较方便,就像下面这样:

//路由规则:app.get('/project/byuser/:userid', project.byUser);
exports.byUser = function (req, res) {
  console.log("Getting user projects");
  if (req.params.userid){
    Project.findByUserID(req.params.userid,function (err, projects) {
      if(!err){
        console.log(projects);
        res.json(projects);
      }else{
        console.log(err);
        res.json({"status":"error", "error":"Error finding projects"});
      }
    });
  }else{
    console.log("No user id supplied");
    res.json({"status":"error", "error":"No user id supplied"});
  }
};

二、创建Model

创建Model很简单:

Mongoose.Model('User', userSchema);

参数一为Model的名字,参数二为生成Model所需要的schema,Model就像是schema所编译而成的一样。

mongoose连接数据库是有两种方式的:

//方式一:
var dbURI = 'mongodb://localhost/mydatabase';
mongoose.connect(dbURI);
//方式二:
var dbURI = 'mongodb://localhost/myadmindatabase';
var adminConnection = mongoose.createConnection(dbURI);
//如果需要声明端口号:
var dbURI = 'mongodb://localhost:27018/mydatabase';
//如果需要定义用户名和密码:
var dbURI = 'mongodb://username:password@localhost/mydatabase';
//也可以像下面这样传一个对象类型的参数:
var dbURI = 'mongodb://localhost/mydatabase';
var dbOptions = {'user':'db_username','pass':'db_password'};
mongoose.connect(dbURI, dbOptions);

根据连接数据库的方式,我们可以得到第二种创建Model的方式,就是使用数据库连接的引用名来创建:

adminConnection.model( 'User', userSchema );

默认情况下mongoose会根据我们传入的Model名字来生成collection名字,在上面的代码中就会生成名为users(全为小写字母)的collection(集合);

有两种方法能让我们自定义collection的名字。

//方式一,在创建schema的时候定义collection的名字:
var userSchema = new mongoose.Schema({
  name: String,
  email: {type: String, unique:true}
},
{
  collection: 'myuserlist'
});
//方式二,在创建Model的时候定义collection的名字:
mongoose.model( 'User', userSchema, 'myuserlist' );

创建Model实例:

var user = new User({ name: 'Simon' });

user就是模型User的一个实例,它具有mongoose中模型所具有的一些方法,例如保存实例:

user.save(function (err) {
   if (err) return handleError(err);
 });

模型也具有一些常用的增删查改的方法:

User.findOne({'name' : 'Sally', function(err,user) {
  if(!err){
    console.log(user);
  }
});
User.find({}, function(err, users) {
  if(!err){
    console.log(users);
  }
});

可以使用链式方式使用这些方法,例如:

var newUser = new User({
  name: 'Simon Holmes',
  email: 'simon@theholmesoffice.com',
  lastLogin : Date.now()
}).save( function( err ){
  if(!err){
    console.log('User saved!');
  }
});

上面的代码创建了一个模型实例,然后进行保存。我们有一个更为简介的方式来完成这项工作,就是使用Model.create()方法:

User.create({
  name: 'Simon Holmes',
  email: 'simon@theholmesoffice.com',
  lastLogin : Date.now()
}, function( err, user ){
  if(!err){
    console.log('User saved!');
    console.log('Saved user name: ' + user.name);
    console.log('_id of saved user: ' + user._id);
  }
});

三、查找数据和读取数据的方法

1.使用QueryBuilder接口来查找数据

先看看下面的代码:

var myQuery = User.find({'name' : 'Simon Holmes'});
myQuery.where('age').gt(18);
myQuery.sort('-lastLogin');
myQuery.select('_id name email');
myQuery.exec(function (err, users){
  if (!err){
    console.log(users); // output array of users found
  }
});

代码中,我们查找名字为"Simon Holmes",并且年龄大于18岁,查找结果根据lastLogin降序排列,只获取其中的_id, name, email三个字段的值,上面的代码只有在调用exec方法后才真正执行数据库的查询。

当然我们可以使用链式的方式来改写上面的代码,代码会更加简洁:

User.find({'name' : 'Simon Holmes'})
.where('age').gt(18)
.sort('-lastLogin')
.select('_id name email')
.exec(function (err, users){
  if (!err){
    console.log(users); // output array of users found
  }
});

上面代码中的第一行创建了一个queryBuilder.通过使用这个queryBuilder,我们就可以执行一些比较复杂的查找工作,在创建完成这个queryBuilder之后,查询操作并没有马上执行,而是待到执行exec方法时才会去执行数据库的查找。

当然也有另外一种方式能够直接查找数据库的,就是直接在查找方法中添加回调函数,使用方式为:

Model.find(conditions, [fields], [options], [callback])

下面举一个简单例子:

User.find({'name', 'simon holmes'}, function(err, user) {});

另一个稍微复杂的例子:

User.find({'name', 'simon holmes'}, 'name email',function(err, user) {
   //console.log('some thing');
 });

另一个更加复杂的例子,包含查询结果的排序:

User.find({'name' : 'Simon Holmes'},
  null, // 如果使用null,则会返回所有的字段值
  {sort : {lastLogin : -1}}, // 降序排序
  function (err, users){
    if (!err){console.log(users);}
  });

列举几个比较实用的查找方法:

Model.find(query);
 Model.findOne(query);//返回查找到的所有实例的第一个
 Model.findById(ObjectID);//根据ObjectId查找到唯一实例

例如:

User.findOne({'email' : req.body.Email},
 '_id name email',
 function(err, user) {
   //todo
 });

 2.更新数据

有三种方式来更新数据:

(1)update(conditions,update,options,callback);

该方法会匹配到所查找的内容进行更新,不会返回数据;

(2)findOneAndUpdate(conditions,update,options,callback);

该方法会根据查找去更新数据库,另外也会返回查找到的并未改变的数据;

(3)findByIdAndUpdate(conditions,update,options,callback);

该方法跟上面的findOneAndUpdate方法功能一样,不过他是根据ID来查找文档并更新的。

三个方法都包含四个参数,一下稍微说明一下几个参数的意思:

  • conditions:查询条件
  • update:更新的数据对象,是一个包含键值对的对象
  • options:是一个声明操作类型的选项,这个参数在下面再详细介绍
  • callback:回调函数

对于options参数,在update方法中和findOneAndUpdate、findByIdAndUpdate两个方法中的可选设置是不同的;

//在update方法中,options的可选设置为:
{
safe:true|false, //声明是否返回错误信息,默认true
upsert:false|true, //声明如果查询不到需要更新的数据项,是否需要新插入一条记录,默认false
multi:false|true, //声明是否可以同时更新多条记录,默认false
strict:true|false //声明更新的数据中是否可以包含在schema定义之外的字段数据,默认true
}
//对于findOneAndUpdate、findByIdAndUpdate这两个方法,他们的options可选设置项为:
{
new:true|false, //声明返回的数据时更新后的该是更新前的,如果为true则返回更新后的,默认true
upsert:false|trure, 
sort:javascriptObject, //如果查询返回多个文档记录,则可以进行排序,在这里是根据传入的javascript object对象进行排序
select:String //这里声明要返回的字段,值是一个字符串
}

下面举个例子:

User.update({_id:user._id},{$set: {lastLogin: Date.now()}},function(){});

3.数据删除

跟更新数据一样,也有三种方法给我们删除数据:

remove();
findOneAndRemove();
findByIdAndRemove();

remove方法有两种使用方式,一种是用在模型上,另一种是用在模型实例上,例如:

User.remove({ name : /Simon/ } , function (err){
  if (!err){
    // 删除名字中包含simon的所有用户
  }
});

User.findOne({ email : 'simon@theholmesoffice.com'},function (err,user){
  if (!err){
    user.remove( function(err){
      // 删除匹配到该邮箱的第一个用户
    });
  }
});

接下来看一下findOneAndRemove方法:

User.findOneAndRemove({name : /Simon/},{sort : 'lastLogin', select : 'name email'},function (err, user){
  if (!err) {
    console.log(user.name + " removed");
    // Simon Holmes removed
  };
});

另外一个findByIdAndRemove方法则是如出一辙的。

User.findByIdAndRemove(req.body._id,function (err, user) {
  if(err){
    console.log(err);
    return;
  }
  console.log("User deleted:", user);
});

四、数据验证

1.mongoose内置数据验证

在mongoose中,数据验证这一层是放在schema中的,mongoose已经帮我们做了很多内置的数据验证,有一些验证是针对某些数据类型的,也有一些是针对所有数据类型的。

能够作用在所有数据类型上的验证有require,意思就是该字段是否是必须的,例如:

email: { type: String, unique: true, required: true }

上面的代码就定义了一个email是必须的schema.

下面再分别介绍一下mongoose内置的一些数据验证类型。

数字类型schemasType,对于Number类型的数据,具有min,max提供用来界定最大最小值:

var teenSchema = new Schema({
   age : {type: Number, min: 13, max:19}
 });

字符串类型SchemasType,对于该类型数据,mongoose提供了两种验证器:

  • match:可使用正则表达式来匹配字符串是否符合该正则表达式的规则
  • enum:枚举出字符串可使用的一些值

分别举例如下:

var weekdaySchema = new Schema({
  day : {type: String, match: /^(mon|tues|wednes|thurs|fri)day$/i}
});

var weekdays = ['monday', 'tuesday', 'wednesday', 'thursday','friday'];
var weekdaySchema = new Schema({
  day : {type: String, enum: weekdays}
});

在我们进行一些数据库的时候,如果有错误,可能会返回一些错误信息,这些信息封装在一个对象中,该对象的数据格式大致如下:

{ 
  message: 'Validation failed',
  name: 'ValidationError',
  errors:{ 
    email:{
      message: 'Validator "required" failed for path email',
      name: 'ValidatorError',
      path: 'email',
      type: 'required' 
    },
    name:{ 
      message: 'Validator "required" failed for path name',
      name: 'ValidatorError',
      path: 'name',
      type: 'required' 
    } 
  } 
}

知道该错误信息的具体格式之后,我们可以从中得出我们想要的信息并反馈到控制台。

if(err){
  Object.keys(err.errors).forEach(function(key) {
    var message = err.errors[key].message;
    console.log('Validation error for "%s": %s', key, message);
  });
}

2.自定义数据验证

最简单的自定义数据验证方式就是定义一个数据验证的函数,并将它传递给schema;

var lengthValidator = function(val) {
  if (val && val.length >= 5){
    return true;
  }
  return false;
};
//usage:
name: {type: String, required: true, validate: lengthValidator }

可以看到,我们只需要在schema中添加validate键值对即可,validate对应的值便是我们自定义的验证方法;

但是该形式的数据验证无法给我们提供完整的错误信息,比如errors信息中返回的type值就会成为undefined;

在此基础上如果希望错误信息中能返回一个错误描述,那我们可以稍微进行一点修改:

//code 1
validate: { validator: lengthValidator, msg: 'Too short' }

//code 2
var weekdaySchema = new Schema({
  day : {type: String, validate: {validator:/^(mon|tues|wednes|thurs|fri)day$/i, msg: 'Not a day' }
});

将validate的值修改为一个对象,并且该对象包含验证器和错误描述。

我们也可以使用另一种方式在写这些验证器,就是将验证器卸载schema外部,例如:

var validateLength = [lengthValidator, 'Too short' ];
 var validateDay = [/^(mon|tues|wednes|thurs|fri)day$/i, 'Not a day' ];
 //usage:
 name: {type: String, required: true, validate: validateLength }
 day : {type: String, validate: validateDay }

眼睛放大,一看再看,确实没错,在validate中我们传入的是一个数组了,而不是原来的对象了。

其实就validateLength这个东东来说,他就是一个简写来的,你也可以改成下面这样:

var validateLength = [
   {validator: lengthValidator, msg: 'Too short'}
 ];

恩,到这里,应该能明白了,将对象改为数组之后,我们便可以传递多个验证器给我们的schema了,的确如此。

var validateUsername = [
   {validator: lengthValidator, msg: 'Too short'} ,
   {validator: /^[a-z]+$/i, msg: 'Letters only'}
 ];

我们还有另外一种方法给我们的schema提供验证器:

userSchema.path('name').validate(lengthValidator, 'Too short');
userSchema.path('name').validate(/^[a-z]+$/i, 'Letters only');

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
使用jQuery解决IE与FireFox下createElement方法的差异
Nov 14 Javascript
JavaScript调用客户端的可执行文件(示例代码)
Nov 28 Javascript
JS实现仿京东淘宝竖排二级导航
Dec 08 Javascript
Avalon中文长字符截取、关键字符隐藏、自定义过滤器
May 18 Javascript
JavaScript数组合并的多种方法
May 22 Javascript
AngularJS框架中的双向数据绑定机制详解【减少需要重复的开发代码量】
Jan 19 Javascript
实例详解BootStrap的动态模态框及静态模态框
Aug 13 Javascript
5分钟快速看懂ES6中的反射与代理
Dec 19 Javascript
Javascript操作select控件代码实例
Feb 14 Javascript
JS严格模式原理与用法实例分析
Apr 27 Javascript
js实现直播点击飘心效果
Aug 19 Javascript
原生JS实现分页
Apr 19 Javascript
ng-options和ng-checked在表单中的高级运用(推荐)
Jan 21 #Javascript
vue实现添加标签demo示例代码
Jan 21 #Javascript
微信小程序 数据交互与渲染实例详解
Jan 21 #Javascript
前端编码规范(3)JavaScript 开发规范
Jan 21 #Javascript
Bootstrap框架安装使用详解
Jan 21 #Javascript
AngularJS ng-repeat指令中使用track by子语句解决重复数据遍历错误问题
Jan 21 #Javascript
微信小程序 九宫格实例代码
Jan 21 #Javascript
You might like
建立动态的WML站点(二)
2006/10/09 PHP
php file_get_contents函数轻松采集html数据
2010/04/22 PHP
php发送短信验证码完成注册功能
2015/11/24 PHP
Yii2结合Workerman的websocket示例详解
2018/09/10 PHP
Jquery.TreeView结合ASP.Net和数据库生成菜单导航条
2010/08/27 Javascript
THREE.JS入门教程(6)创建自己的全景图实现步骤
2013/01/25 Javascript
JavaScript字符串对象replace方法实例(用于字符串替换或正则替换)
2014/10/16 Javascript
不到30行JS代码实现Excel表格的方法
2014/11/15 Javascript
Bootstrap基础学习
2015/06/16 Javascript
JavaScript中字符串拼接的基本方法
2015/07/07 Javascript
js创建对象的方法汇总
2016/01/07 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【凹多边形的分离轴检测算法】
2018/12/13 Javascript
小程序日历控件使用方法详解
2018/12/29 Javascript
JavaScript实现的九种排序算法
2019/03/04 Javascript
vue router 组件的高级应用实例代码
2019/04/08 Javascript
基于iview的router常用控制方式
2019/05/30 Javascript
Vue之beforeEach非登录不能访问的实现(代码亲测)
2019/07/18 Javascript
如何在wxml中直接写js代码(wxs)
2019/11/14 Javascript
bootstrap实现tab选项卡切换
2020/08/09 Javascript
python操作MongoDB基础知识
2013/11/01 Python
老生常谈Python序列化和反序列化
2017/06/28 Python
django admin添加数据自动记录user到表中的实现方法
2018/01/05 Python
Python cookbook(数据结构与算法)从字典中提取子集的方法示例
2018/03/22 Python
Django配置MySQL数据库的完整步骤
2019/09/07 Python
Python数据可视化:箱线图多种库画法
2019/11/06 Python
如何使用python的ctypes调用医保中心的dll动态库下载医保中心的账单
2020/05/24 Python
通过实例简单了解Python sys.argv[]使用方法
2020/08/04 Python
时尚、社区、科技:SEVENSTORE
2019/04/26 全球购物
碧欧泉法国官网:Biotherm法国
2019/10/23 全球购物
Linux如何命名文件--使用文件名时应注意
2012/01/22 面试题
毕业自我评价范文
2013/11/17 职场文书
特教教师先进事迹
2014/05/21 职场文书
建筑施工安全责任书
2014/07/24 职场文书
2014小学数学教研组工作总结
2014/12/06 职场文书
婚礼男方父母答谢词
2015/09/29 职场文书
python中opencv实现图片文本倾斜校正
2021/06/11 Python