mongodb笔记

作者: AkaTBS | 来源:发表于2017-01-31 12:40 被阅读315次

    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来操作。

    相关文章

      网友评论

        本文标题:mongodb笔记

        本文链接:https://www.haomeiwen.com/subject/rzfkittx.html