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 相关文章推荐
E3 tree 1.6在Firefox下显示问题的修复方法
Jan 30 Javascript
使用原生js封装webapp滑动效果(惯性滑动、滑动回弹)
May 06 Javascript
使用jsonp完美解决跨域问题
Nov 27 Javascript
node.js中的fs.readlinkSync方法使用说明
Dec 17 Javascript
动态加载js的方法汇总
Feb 13 Javascript
通过点击jqgrid表格弹出需要的表格数据
Dec 02 Javascript
jQuery自动完成插件completer附源码下载
Jan 04 Javascript
javascript自动恢复文本框点击清除后的默认文本
Jan 12 Javascript
基于Node.js的WebSocket通信实现
Mar 11 Javascript
浅谈Vue SSR 的 Cookies 问题
Nov 20 Javascript
jQuery实现鼠标响应式透明度渐变动画效果示例
Feb 13 jQuery
使用Vue实现移动端左滑删除效果附源码
May 16 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
关于Intype一些小问题的解决办法
2008/03/28 PHP
使用dump函数,给php加断点测试
2013/06/25 PHP
php日志函数error_log用法实例分析
2019/09/23 PHP
php中加密解密DES类的简单使用方法示例
2020/03/26 PHP
5个最佳的Javascript日期处理类库分享
2012/04/15 Javascript
js验证是否为数字的总结
2013/04/14 Javascript
js实现图片和链接文字同步切换特效的方法
2015/02/20 Javascript
Bootstrap实现弹性搜索框
2016/07/11 Javascript
用js实现博客打赏功能
2016/10/24 Javascript
概述一个页面从输入URL到页面加载完的过程
2016/12/16 Javascript
收藏AngularJS中最重要的核心功能
2017/07/09 Javascript
VsCode插件整理(小结)
2017/09/14 Javascript
微信小程序自定义多选事件的实现代码
2018/05/17 Javascript
layui添加动态菜单与选项卡 AJAX请求的例子
2019/09/25 Javascript
Python函数可变参数定义及其参数传递方式实例详解
2015/05/25 Python
python批量修改文件编码格式的方法
2018/05/31 Python
如何基于python测量代码运行时间
2019/12/25 Python
python标准库os库的函数介绍
2020/02/12 Python
pycharm实现在虚拟环境中引入别人的项目
2020/03/09 Python
Python3通过chmod修改目录或文件权限的方法示例
2020/06/08 Python
Python 下载Bing壁纸的示例
2020/09/29 Python
浅谈Python __init__.py的作用
2020/10/28 Python
最新奶茶店创业计划书范文
2014/02/08 职场文书
淘宝好评语大全
2014/05/05 职场文书
学校总务处领导干部个人对照检查材料思想汇报
2014/10/06 职场文书
列车乘务员工作不细心检讨书
2014/10/07 职场文书
考研英语复习计划
2015/01/19 职场文书
学生会个人总结范文
2015/02/15 职场文书
2015年银行员工工作总结
2015/04/24 职场文书
2015年禁毒工作总结
2015/04/30 职场文书
一道JS算法面试题——冒泡、选择排序
2021/04/21 Javascript
Python3 多线程(连接池)操作MySQL插入数据
2021/06/09 Python
MySQL 8.0 驱动与阿里druid版本兼容问题解决
2021/07/01 MySQL
Nginx进程调度问题详解
2021/09/25 Servers
在vue中import()语法不能传入变量的问题及解决
2022/04/01 Vue.js
Python开发五子棋小游戏
2022/05/02 Python