mongodb笔记

Mongodb

配置选项

--quiet # 安静输出
--port arg  # 指定服务端口号,默认端口27017
--bind_ip arg   # 绑定服务IP,若绑定127.0.0.1,则只能本机访问,不指定默认本地所有IP
--logpath arg   # 指定MongoDB日志文件,注意是指定文件不是目录
--logappend # 使用追加的方式写日志
--pidfilepath arg   # PID File 的完整路径,如果没有设置,则没有PID文件
--keyFile arg   # 集群的私钥的完整路径,只对于Replica Set 架构有效
--unixSocketPrefix arg  # UNIX域套接字替代目录,(默认为 /tmp)
--fork  # 以守护进程的方式运行MongoDB,创建服务器进程
--auth  # 启用验证
--cpu   # 定期显示CPU的CPU利用率和iowait
--dbpath arg    # 指定数据库路径
--diaglog arg   # diaglog选项 0=off 1=W 2=R 3=both 7=W+some reads
--directoryperdb    # 设置每个数据库将被保存在一个单独的目录
--journal   # 启用日志选项,MongoDB的数据操作将会写入到journal文件夹的文件里
--journalOptions arg    # 启用日志诊断选项
--ipv6  # 启用IPv6选项
--jsonp # 允许JSONP形式通过HTTP访问(有安全影响)
--maxConns arg  # 最大同时连接数 默认2000
--noauth    # 不启用验证
--nohttpinterface   # 关闭http接口,默认关闭27018端口访问
--noprealloc    # 禁用数据文件预分配(往往影响性能)
--noscripting   # 禁用脚本引擎
--notablescan   # 不允许表扫描
--nounixsocket  # 禁用Unix套接字监听
--nssize arg (=16)  # 设置信数据库.ns文件大小(MB)
--objcheck  # 在收到客户数据,检查的有效性,
--profile arg   # 档案参数 0=off 1=slow, 2=all
--quota # 限制每个数据库的文件数,设置默认为8
--quotaFiles arg    # number of files allower per db, requires --quota
--rest  # 开启简单的rest API
--repair    # 修复所有数据库run repair on all dbs
--repairpath arg    # 修复库生成的文件的目录,默认为目录名称dbpath
--slowms arg (=100) # value of slow for profile and console log
--smallfiles    # 使用较小的默认文件
--syncdelay arg (=60)   # 数据写入磁盘的时间秒数(0=never,不推荐)
--sysinfo   # 打印一些诊断系统信息
--upgrade   # 如果需要升级数据库

通常在mongod.conf中

dbpath = /data/db
logpath = /data/mongodb/mongodb.log
logappend = true
port = 27017
fork = true
auth = true

配置文件

设置了配置文件后启动时以自定义的配置文件启动:mongod --config mongodb.conf

错误提示

若提示DeprecationWarning: Mongoose: mpromise则在连接数据库前加一句

mongoose.Promise = global.Promise;  

shell命令

mongodb监控工具

  • 控制台输入mongostat可以实时输出系统状态
  • 还可以通过mongodb的官方监控工具MMS来监控(收费)

对数据库的操作

  1. db.serverStatus()显示数据库服务器运行状态。
  2. 切换/创建数据库:use yourDB; 当创建一个集合(table)的时候会自动创建当前数据库
  3. 查询所有数据库:show dbs;
  4. 查看当前数据库下所有的表:show collections
  5. 删除当前使用数据库:db.dropDatabase();
  6. 从指定主机上克隆数据库:db.cloneDatabase(“127.0.0.1”);
    将指定机器上的数据库的数据克隆到当前数据库
  7. 从指定的机器上复制指定数据库数据到某个数据库:db.copyDatabase("mydb", "temp", "127.0.0.1");
    将本机的mydb的数据复制到temp数据库中
  8. 修复当前数据库:db.repairDatabase();
  9. 查看当前使用的数据库:db.getName();
    db; db和getName方法是一样的效果,都可以查询当前使用的数据库
  10. 显示当前db状态:db.stats();
  11. 当前db版本:db.version();
  12. 查看当前db的链接机器地址:db.getMongo();

对表的操作:foo是数据库内的一张表

db.foo.find(...).count() 
db.foo.find(...).limit(n) 根据条件查找数据并返回指定记录数 
db.foo.find(...).skip(n) 
db.foo.find(...).sort(...) 查找排序 
db.foo.findOne([query]) 根据条件查询只查询一条数据 
db.foo.getDB() get DB object associated with collection  返回表所属的库 
db.foo.getIndexes() 显示表的所有索引 
db.foo.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } ) 根据条件分组 
db.foo.mapReduce( mapFunction , reduceFunction , <optional params> ) 
db.foo.remove(query) 根据条件删除数据 
db.foo.renameCollection( newName ) renames the collection  重命名表 
db.foo.save(obj) 保存数据 
db.foo.stats()  查看表的状态 
db.foo.storageSize() - includes free space allocated to this collection 查询分配到表空间大小 
db.foo.totalIndexSize() - size in bytes of all the indexes 查询所有索引的大小 
db.foo.totalSize() - storage allocated for all data and indexes 查询表的总大小 
db.foo.update(query, object[, upsert_bool]) 根据条件更新数据 
db.foo.validate() - SLOW 验证表的详细信息 
db.foo.getShardVersion() - only for use with sharding

用户相关

  1. 添加一个用户:每个数据库独立设置自己的用户,数据库之间相互独立
    • db.createUser({user:"admin",pwd:"pass",roles:["readWrite","dbAdmin"]})
    • userSource <database> (可选)代替pwd字段,指向具有相同的用户定义的另一个数据库。pwd或那个数据库的userSource然后被用作该用户的凭据。userSource字段和pwd字段是相互排斥的,一个文档不能同时包含两者。
    • roles:指定用户的角色,可以用一个空数组给新用户设定空角色;在roles字段,可以指定内置角色和用户定义的角色。role里的角色可以选:
Built-In Roles(内置角色):
    1. 数据库用户角色:read、readWrite;
    2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;
    3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
    4. 备份恢复角色:backup、restore;
    5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
    6. 超级用户角色:root  
    // 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
    7. 内部角色:__system
  • 具体角色
Read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
root:只在admin数据库中可用。超级账号,超级权限
  1. 显示当前所有用户:show users
  2. 列出admin数据库中的用户内容:db.system.users.find()
  3. 在控制台中执行认证
    • db.auth("username","password")
    • 输出1为认证成功0为认证失败
  4. 删除用户
    • db.dropUser("userName");

启用认证

  • 在mongodb.conf中添加auth=true,重启mongodb service mongodb restart --config mongodb.conf
  • 启动时使用 mongod --auth

mogoose验证

安全

一. 使用Linux IPtables限制IP范围
这种方法和MongoDB本身没有关系,而是借用Linux的iptables功能,限制允许访问MongoDB端口的IP地址,具体的做法如下:

# 拒绝所有访问27017端口的请求
sudo iptables -I INPUT -p tcp --dport 27017 -j DROP
# 允许本地访问mongo端口
sudo iptables -I INPUT -s 127.0.0.1 -p tcp --dport 27017 -j ACCEPT

sudo iptables-save

OK,这样就只允许通过本地访问MongoDB服务了。

备份

mongodump备份数据库

  1. 常用命令格

    mongodump -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -o 文件存在路径 
    如果没有用户谁,可以去掉-u和-p。
    如果导出本机的数据库,可以去掉-h。
    如果是默认端口,可以去掉--port。
    如果想导出所有数据库,可以去掉-d。
    如果存在当前文件夹,可以去掉-o。
    可输入mongodump --help查看说明
    
  2. 导出所有数据库:mongodump -h 127.0.0.1 -o /home/mongodb/

  1. 导出指定数据库:mongodump -h 192.168.1.108 -d tank -o /home/mongodb/

mongorestore还原数据库

  1. 常用命令格式
    mongorestore -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 --drop 文件存在路径 --drop的意思是,先删除所有的记录,然后恢复。
  2. 恢复所有数据库到mongodb中

mongorestore /home /mongodb/ #这里的路径是所有库的备份路径

  1. 还原指定的数据库

    • mongorestore -d tank /home/mongodb/tank/ #tank这个数据库的备份路径
    • mongorestore -d tank_new /home/mongodb/tank/ #将tank还有tank_new数据库中

    这二个命令,可以实现数据库的备份与还原,文件格式是json和bson的。无法指写到表备份或者还原。

mongoexport导出表,或者表中部分字段

mongoexport -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -c 表名 -f 字段 -q 条件导出 --type=csv -o 文件名 
-f    导出指字段,以字号分割,-f name,email,age导出name,email,age这三个字段
-q    可以根查询条件导出,-q '{ "uid" : "100" }' 导出uid为100的数据
--csv 表示导出的文件格式为csv的,这个比较有用,因为大部分的关系型数据库都是支持csv,在这里有共同点
  1. 导出整张表
    mongoexport -d tank -c users -o /home/mongodb/tank/users.dat
  1. 导出表中部分字段
    mongoexport -d tank -c users --csv -f uid,name,sex -o tank/users.csv
  1. 根据条件敢出数据
    mongoexport -d tank -c users -q '{uid:{$gt:1}}' -o tank/users.json

mongoimport导入表,或者表中部分字段

  1. 还原整表导出的非csv文件
    mongoimport -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -c 表名 --upsert --drop 文件名
    重点说一下--upsert,其他参数上面的命令已有提到,--upsert 插入或者更新现有数据
  2. 还原部分字段的导出文件
    mongoimport -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -c 表名 --upsertFields 字段 --drop 文件名
    --upsertFields根--upsert一样
  3. 还原导出的csv文件
    mongoimport -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -c 表名 --type 类型 --headerline --upsert --drop 文件名
    上面三种情况,还可以有其他排列组合的。
  4. 还原导出的表数据
    mongoimport -d tank -c users --upsert tank/users.dat
  5. 部分字段的表数据导入
    mongoimport -d tank -c users --upsertFields uid,name,sex tank/users.dat
  6. 还原csv文件
    mongoimport -d tank -c users --type csv --headerline --file tank/users.csv

mongoose

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

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

关于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();

关于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

该类型的值由系统自己生成,从某种意义上几乎不会重复,生成过程比较复杂,有兴趣的朋友可以查看源码。

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


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.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属性

修改器和更新器

更新修改器:

‘$inc’ 增减修改器,只对数字有效.下面的实例: 找到 age=22的文档,修改文档的age值自增1

Model.update({‘age’:22}, {’$inc’:{‘age’:1} } );
执行后: age=23

‘$set’ 指定一个键的值,这个键不存在就创建它.可以是任何MondoDB支持的类型.

Model.update({‘age’:22}, {’$set’:{‘age’:‘haha’} } );
执行后: age=‘haha’

‘$unset’ 同上取反,删除一个键

Model.update({‘age’:22}, {’$unset’:{‘age’:‘haha’} } );
执行后: age键不存在

数组修改器:

‘$push’ 给一个键push一个数组成员,键不存在会创建

Model.update({‘age’:22}, {’$push’:{‘array’:10} } );
执行后: 增加一个 array 键,类型为数组, 有一个成员 10

‘$addToSet’ 向数组中添加一个元素,如果存在就不添加

Model.update({‘age’:22}, {’$addToSet’:{‘array’:10} } );
执行后: array中有10所以不会添加

‘$each’ 遍历数组, 和 $push 修改器配合可以插入多个值

Model.update({‘age’:22}, {’$push’:{‘array’:{’$each’: [1,2,3,4,5]}} } );
执行后: array : [10,1,2,3,4,5]

‘$pop’ 向数组中尾部删除一个元素

Model.update({‘age’:22}, {’$pop’:{‘array’:1} } );
执行后: array : [10,1,2,3,4] tips: 将1改成-1可以删除数组首部元素

‘$pull’ 向数组中删除指定元素

Model.update({‘age’:22}, {’$pull’:{‘array’:10} } );
执行后: array : [1,2,3,4] 匹配到array中的10后将其删除

条件查询:

“$lt”   小于
“$lte”  小于等于
“$gt”   大于
“$gte”  大于等于
“$ne”   不等于

Model.find({“age”:{ “$get”:18 , “$lte”:30 } } );
查询 age 大于等于18并小于等于30的文档

或查询 OR:

‘$in’ 一个键对应多个值
‘$nin’ 同上取反, 一个键不对应指定值
“$or” 多个条件匹配, 可以嵌套 $in 使用
“$not”  同上取反, 查询与特定模式不匹配的文档

Model.find({“age”:{ “$in”:[20,21,22.‘haha’]} } );
查询 age等于20或21或21或’haha’的文档

Model.find({"$or" : [ {‘age’:18} , {‘name’:‘xueyou’} ] });
查询 age等于18 或 name等于’xueyou’ 的文档

类型查询:

null 能匹配自身和不存在的值, 想要匹配键的值 为null, 就要通过 “$exists” 条件判定键值已经存在
"$exists" (表示是否存在的意思)

Model.find(“age” : { “$in” : [null] , “exists” : true } );
查询 age值为null的文档

Model.find({name: {$exists: true}},function(error,docs){
//查询所有存在name属性的文档
});

Model.find({telephone: {$exists: false}},function(error,docs){
//查询所有不存在telephone属性的文档
});

正则表达式:

MongoDb 使用 Prel兼容的正则表达式库来匹配正则表达式

find( {“name” : /joe/i } )
查询name为 joe 的文档, 并忽略大小写

find( {“name” : /joe?/i } )
查询匹配各种大小写组合

查询数组:

Model.find({“array”:10} );
查询 array(数组类型)键中有10的文档, array : [1,2,3,4,5,10] 会匹配到

Model.find({“array[5]”:10} );
查询 array(数组类型)键中下标5对应的值是10, array : [1,2,3,4,5,10] 会匹配到

‘$all’ 匹配数组中多个元素

Model.find({“array”:[5,10]} );
查询 匹配array数组中 既有5又有10的文档

‘$size’ 匹配数组长度

Model.find({“array”:{"$size" : 3} } );
查询 匹配array数组长度为3 的文档

‘$slice’ 查询子集合返回

Model.find({“array”:{"$skice" : 10} } );
查询 匹配array数组的前10个元素

Model.find({“array”:{"$skice" : [5,10] } } );
查询 匹配array数组的第5个到第10个元素

where

用它可以执行任意javacript语句作为查询的一部分,如果回调函数返回 true 文档就作为结果的一部分返回

    find( {"$where" : function(){
        for( var x in this ){
         //这个函数中的 this 就是文档
        }
        
        if(this.x !== null && this.y !== null){
            return this.x + this.y === 10 ? true : false;
        }else{
            return true;
        }
        
        
}  }  )
简化版本

    find( {"$where" :  "this.x + this.y === 10" } )
    find( {"$where" : " function(){ return this.x + this.y ===10; } " } )
游标:
  • limit(3) 限制返回结果的数量,
  • skip(3) 跳过前3个文档,返回其余的
  • sort( {“username”:1 , “age”:-1 } ) 排序 键对应文档的键名, 值代表排序方向, 1 升序, -1降序

mongoose在express中的使用

  1. 在express中使用mongoose时在主文件中定义连接,将连接上的connection定义在node全局的global上
    global.db = mongoose.createConnection(uri);
  2. 在路由中单独列出model.js文件,因为model不可以重复定义,但路由总是会重复调用,所以需要将model单列出来。
  3. 在model文件中引入mongoose并定义Schema,和Schema的方法最后module.exports=bd.model(“model名”,scheam)导出model。(db是全局的,所以可以随处调用)
  4. 在需要用到数据库的路由中引用model.js,通过model创建实体Entity和操作数据库。
  5. 每次对数据库的操作结束后要记得db.close();

注意点

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

推荐阅读更多精彩内容