• Mongodb基础 学习小结


    MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展
    的高性能数据存储解决方案。
    MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰
    富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以
    存储比较复杂的数据类型。
    Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,
    几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

    MongoDB主要场景如下:
    1)网站实时数据处理。非常适合实时的插入、更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
    2)缓存。由于性能很高,它适合作为信息基础设施的缓存层。在系统重启之后,由它搭建的持久化缓存层可以避免下层的数据源过载。
    3)高伸缩性的场景。非常适合由数十或数百台服务器组成的数据库,它的路线图中已经包含对MapReduce引擎的内置支持。

    不适用的场景如下:
    1)要求高度事务性的系统。
    2)传统的商业智能应用。
    3)复杂的跨文档(表)级联查询。

    /*********** Windows版
    Windows 安装 msi 包
    下载:https://www.mongodb.com/download-center#community
    选择 Community Server 选择一个稳定版
    安装...

    然后找个合适位置 建立目录,用于存放数据和日志:
    md "datadb" "datalog"

    测试启动:(必须指定db路径) */
    "C:Program FilesMongoDBServer3.6inmongod.exe" --dbpath="c:datadb"

    /*将 MongoDB 安装为 Windows 服务:
    创建配置文件:C:Program FilesMongoDBServer3.6mongod.cfg
    内容如下:(指定了log和db的位置) */
    systemLog:
    destination: "file"
    path: "c:\data\log\mongod.log"
    storage:
    dbPath: "c:\data\db"

    // 安装服务:
    sc.exe create MongoDB binPath= ""C:Program FilesMongoDBServer3.6inmongod.exe" --service --config="C:Program FilesMongoDBServer3.6mongod.cfg"" DisplayName= "MongoDB" start= auto

    // 如果成功则显示:CreateService SUCCESS
    // 以后就可以系统服务中管理了。

    // 卸载服务:(需要先停止服务)
    sc.exe delete MongoDB

    /**
    管理工具 Robomongo :
    下载:https://robomongo.org/download
    MongoChef
    下载:http://3t.io/mongochef/#mongochef-download-compare
    */
    // ----------------------------------------------------------------------------


    //-------------------------------------- Linux版 -------------------------------
    // Mongodb 需要64位linux 本次使用ubuntu18.0.4 如果是centos,某些命令会不同。
    // 到官网下载tar包 tgz,  注意选择OS版本

    // https://www.mongodb.com/download-center

    tar zxvf mongodb-linux-x86_64-3.6.4.tgz
    mv mongodb-linux-x86_64-3.6.4 mongodb364
    cd mongodb364

    // 建立所需的db文件夹和logs文件夹,在根目录创建,是因为停止服务时,不带参数,会默认寻找此路径
    mkdir -p /data/db
    mkdir -p /data/logs

    // 修改环境变量,vim /etc/profile 添加
    export PATH=/home/mongodb364/bin:$PATH

    source /etc/profile // 生效

    // ------------如果不能启动服务,ubuntu18 可能需要安装某些依赖。--------------
    // 如果 apt-get 出现 E: Unable to locate package 问题, 则执行以下命令:
    add-apt-repository main
    add-apt-repository universe
    add-apt-repository restricted
    add-apt-repository multiverse

    apt-get update
    // 如果不能解决,再执行这条:
    apt-get upgradee

    // 移除某个包:
    apt-get remove xxx

    // 错误: libcurl.so.4: version `CURL_OPENSSL_3' not found
    apt-get install curl
    apt-get install libcurl3

    // CentOS的依赖
    yum install libcurl openssl  // 安装依赖 

    // 启动服务    指定路径       --port 10086 指定端口
    bin/mongod --dbpath=/opt/mongodb/data/db --logpath=/opt/mongodb/logs/mongo.log --fork  
    
    bin/mongo --help // 使用客户端帮助 
    bin/mongo        // 首次本机登录无需认证
    
    bin/mongod --config /etc/mongod.conf  // 或者指定配置文件启动,通常在设置安全后
    
    // 停止服务 mongod --shutdown   // 不指定dbpath, 默认会寻找 /data/db
    // 停止服务 mongod --shutdown --dbpath /home/mongodb364/data/db  // 指定dbpath
    // 停止服务 mongod --shutdown --config=/etc/mongod.conf  // 也可指定配置文件
    
    /************* 安全性流程:
        1.创建超级管理员
        2.修改配置文件,启用身份验证
        3.重启服务
        4.使用超级管理员登录
        5.创建并使用普通用户
    *************/

    // -------------------------------使用配置文件:--------------------------
    vim /etc/mongod.conf  // 较简单的格式
    bind_ip = 0.0.0.0
    port=27017
    dbpath=/data/db
    logpath=/data/logs/mongod.log
    pidfilepath=/opt/mongodb/mongod.pid
    fork=true
    logappend=true
    auth=true

    // ---------------------------YAML格式配置文件--------------------------

    如果是 rpm / yum 方式安装,则自动添加 /etc/mongo.conf ,示例:

    # mongod.conf
    
    # for documentation of all options, see:
    #   http://docs.mongodb.org/manual/reference/configuration-options/
    
    # where to write logging data.
    systemLog:
      destination: file
      logAppend: true
      path: /data/logs/mongod.log
    
    # Where and how to store data.
    storage:
      dbPath: /data/db
      directoryPerDB: true
      journal:
        enabled: true
    #  engine:
    #  mmapv1:
    #  wiredTiger:
    
    # how the process runs
    processManagement:
      fork: true  # fork and run in background
      pidFilePath: /opt/mongodb/mongod.pid  # location of pidfile
      timeZoneInfo: /usr/share/zoneinfo
    
    # network interfaces
    net:
      port: 27017
      bindIp: 127.0.0.1  # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
    
    security:
      authorization: "enabled"
    #operationProfiling:
    
    #replication:
    
    #sharding:
    
    ## Enterprise-Only Options
    
    #auditLog:
    
    #snmp:

    bin/mongod --config /etc/mongod.conf // 指定配置文件启动
    // 如果启动失败,请检查配置中文件路径是否正确

    //------------------------配置远程连接---------------------------------
    systemctl status ufw    // 查看防火墙状态
    systemctl stop ufw      // 停止防火墙
    ufw allow 27017         // 防火墙允许端口
    // --------上面是ubuntu的,下面是centos7的。--------------------------
    firewall-cmd --permanent --add-port=27017/tcp  // 添加3306
    systemctl restart firewalld.service   // 重启fw
    firewall-cmd --list-all         // 查看fw

    如果需要,也可将Mongodb安装为服务,便于管理

    ############# 在 CentOS7 中安装为服务
    # 1. 创建用户和组
    groupadd -g 1002 mongogrp
    useradd -u 1002 -g mongogrp -G mongogrp mongouser
    passwd mongouser
    
    # 2. 更改mongodb文件夹拥有者
    chown -R mongouser:mongogrp mongod/
    
    # 3.编辑服务文件 /etc/systemd/system/mongod.service 并保存
    #   写User和Group是限制指定用户可以操作
    
    [Unit]
    Description=MongoDB Server
    After=network.target remote-fs.target nss-lookup.target
    
    [Service]
    User=mongouser
    Group=mongogrp
    Type=forking
    ExecStart=/data/mongod/bins/mongod -f /data/mongod/conf/mongod.conf
    ExecReload=/bin/kill -s HUP $MAINPID
    ExecStop=/data/mongod/bins/mongod --shutdown -f /data/mongod/conf/mongod.conf
    PrivateTmp=true
    LimitFSIZE=infinity
    LimitCPU=infinity
    LimitAS=infinity
    LimitNOFILE=64000
    LimitNPROC=64000
    
    [Install]
    WantedBy=multi-user.target
    
    
    
    # 4. 试试用 mongouser 操作服务
    sudo systemctl daemon-reload
    sudo systemctl start mongod 
    sudo systemctl status mongod 
    sudo systemctl stop mongod 
    sudo systemctl enable mongod      # 开机启动
    sudo systemctl disable mongod     # 关闭开机启动
    sudo systemctl is-enabled mongod  # 检查是否开机启动
    sudo systemctl kill mongod        # 杀死进程mongod
    
    sudo systemctl list-units -t service          # 列出已激活的服务
    sudo systemctl list-units -t service -a       # 列出所有服务

    //###########################  mongo shell  ####################################

    mongo  // 登录本机(未开启认证时)
        // 执行以下命令,可以添加 管理员
        use admin;
            db.createUser(  
              {  
                user: "admin",  
                pwd: "admin",  
                roles: [ { role: "root", db: "admin" } ]  
              }  
            );
            
    /**  下面是成功时返回的提示:
        Successfully added user: {
            "user" : "admin",
            "roles" : [
                {
                    "role" : "userAdminAnyDatabase",
                    "db" : "admin"
                }
            ]
        };
    **/
        db.auth("admin","admin")   // 认证测试,若 1 则成功
        
    //-----------------------------------------------------------------------------
    
    // 如开启认证:
        use admin  // 进入管理库
        db.auth("username","pwd")  // 认证登录
    
    // 或直接认证连接: 本地方式
    mongo --username admin --password --authenticationDatabase admin 
    mongo -u admin -p --authenticationDatabase admin 
    // 远程方式 
    mongo "mongodb://admin:pwd@localhost:27017/?authSource=admin" 
    
    // 更多连接实例 // 参考: https://www.mongodb.org.cn/tutorial/7.html //连接本地数据库服务器,端口是默认的。 mongodb://localhost //使用用户名fred,密码foobar登录localhost的admin数据库。 mongodb://fred:foobar@localhost //使用用户名fred,密码foobar登录localhost的baz数据库。 mongodb://fred:foobar@localhost/baz //连接 replica pair, 服务器1为example1.com服务器2为example2。 mongodb://example1.com:27017,example2.com:27017 //连接 replica set 三台服务器 (端口 27017, 27018, 和27019): mongodb://localhost,localhost:27018,localhost:27019 //连接 replica set 三台服务器, 写入操作应用在主服务器 并且分布查询到从服务器。 mongodb://host1,host2,host3/?slaveOk=true //直接连接第一个服务器,无论是replica set一部分或者主服务器或者从服务器。 mongodb://host1,host2,host3/?connect=direct;slaveOk=true //当你的连接服务器有优先级,还需要列出所有服务器,你可以使用上述连接方式。 //安全模式连接到localhost: mongodb://localhost/?safe=true //以安全模式连接到replica set,并且等待至少两个复制服务器成功写入,超时时间设置为2秒。 mongodb://host1,host2,host3/?safe=true;w=2;wtimeoutMS=2000
    // Shell 常用操作 db show dbs show collections use local show users // 查看当前库中用户 db.test.insert({'a':'333323'}) // 插入数据时会自动创建库和Collections db.test.find() // 显示所有数据 exit // 退出mongo shell // 最后用Robo 3T 测试连接
    /*************** 如果需要重置密码: ***************/
    //  1. 停止服务 mongod --shutdown --config=/etc/mongod.conf
    //  2. 修改conf文件,关掉security和authorization
    //  3. 启动服务 mongod --config /etc/mongod.conf
    //  4. mongo 登录,use admin , 
          show users // 显示本库用户
          db.system.users.find() // 查看全部用户信息。
          db.dropUser(用户名)  // 删除指定的用户
    //  5. 使用上面的 db.createUser 添加用户:
        use admin
            db.createUser(  
              {  
                user: "admin",  
                pwd: "admin",  
                roles: [ { role: "root", db: "admin" } ]  
              }  
            );
    
        // 或者修改用户:
        db.updateUser('admin',
              {
                pwd: "admin",  
                roles: [{ role: "root", db: "admin" } ]  
              }  
            )
    //  6. 重新开启配置文件的安全选项,并重启服务 

    /*********** Mongodb内置的用户角色
    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

    #  创建一个 __system 用户,可以访问 所有元数据

    db.createUser({user:"sa", pwd:"123456", roles:[{role:"__system", db:"admin"}]})  

    获得数据库的所有用户权限信息:db.getUsers()

    获得某个用户的权限信息:db.getUser()
    创建角色: db.createRole()
    更新角色:db.updateRole()
    删除角色:db.dropRole()
    获得某个角色信息:db.getRole()
    删除用户:db.dropUser()
    删除所有用户:db.dropAllUsers()
    将一个角色赋予给用户:db.grantRolesToUser()
    撤销某个用户的某个角色权限:db.revokeRolesFromUser()
    更改密码:db.changeUserPassword()

    ***********/

        // 例1:给test库添加用户,只能访问test   即 --authenticationDatabase test
        use test
            db.createUser(  
              {  
                user: "test1",  
                pwd: "test1",  
                roles: [ { role: "readWrite", db: "test" } ]  
              }  
            );
            
        // 例2:修改user1权限  --authenticationDatabase demo
        use demo
        db.updateUser('user1',{
            roles: [{role:"dbOwner", db:"demo"}]
        })
        
        // 例3:修改test用户权限,读写所有库
        use admin
        db.updateUser('test',{
            roles: [{role:"readWriteAnyDatabase", db:"admin"}]
        })
    
    /************ 其它命令 ******
        创建 capped collection 上限集合:
           size:大小KB,如果size字段小于或等于4096,则集合的上限为4096字节
           max: 最大文档数。size参数总是必需的,即使指定max的文件数量。
                如果集合在达到最大文档计数之前达到最大大小限制,MongoDB将删除旧文档。 
        参考官方文档 或 https://www.jianshu.com/p/86942c09094a    
    ************/
    db.createCollection("sub",{capped:true,size:100000,max:5})
    
    db.sub.isCapped() // 检查是否 capped
    
    // 转换为 capped 集合
    db.runCommand({"convertToCapped": "test", size: 100000, max:10 });

    /**************
    *** CURD ***
    **************/

    //----------------------------------- 增 ---------------------------------------
    // Mongodb 数据库名称不区分大小写
    // 数据库不必创建,直接 use databasename 即可
    // 集合名称可以createCollection创建,
    // 一般集合也可不创建,直接在插入数据时自动生成。
    // 集合名称只能字母或下划线开头,不能包含 . $ 和空字符,不能 system. 开头
    // 更多限制,参考官文:https://docs.mongodb.com/manual/reference/limits/
    // 插入多条 db.users.insert([ {name:'Allan',age:32,gender:1}, {name:'Bob',age:25,gender:1}, {name:'Celine',age:32,gender:2}, {name:'Dennis',age:12,gender:1}, ]) // 以下两种插入方法是为了语义清晰 // db.test.insertOne() // db.test.insertMany() // ================================== 查 ======================================= db.test.find({}) // 空对象或空,查询所有的 db.test.find({name:"Jerry"}) // 返回数组 db.test.findOne({name:"Jerry"}) // 返回对象 db.test.find({}).count() // 数量 或 db.test.count() db.users.count({age:{$gt:25}}) // 条件数量 db.test.find({age:{$gt:25}}) // 大于 或者 $gte 即 >= db.test.find({create_ts:{$gt:ISODate("2019-09-19T00:00:00.000Z")}}).pretty() //大于某时间
    db.test.find({age:{$eq:25}}) // 等于 或者 $ne 不等于 db.test.find({age:{$lt:25}}) // 小于 或者 $lte 即 <= db.test.find({age:{$gt:20, $lt:30}}) // 大于且小于 db.demoes.find({$or:[{num:{$lt:10}},{num:{$gt:1990}}]}) // 或 $or:[{},{},] db.users.find({age:{$in:[25,32]}}) // 在in 或 不在nin db.users.find({name:/^C/}) // 正则,以C开头 db.users.find({name:{$regex:"^A"}}) // 正则, 以A开头 db.users.find({nickname:{$exists: false}})  // 找出不存在昵称字段的 (true 存在)
    // 虽然可用JS语法,但是效率低 db.users.find({$where:function(){return this.age>20}}) db.users.find({$where:function(){return this.name.startsWith("C")}}) db.test.find().limit(10) // 前10条 db.test.find().skip(10).limit(10) // 跳过10条
    // 官方文档也提到 skip() 由于是从头开始数,所以,当数据量变大时,会变得非常慢。
    // 我测试的时候,大于5万条数据,已经很慢了。 所以,尽量使用 条件查询,避免使用 skip()
    // skip( (页码 -1) * 每页条数).limit(每页条数) // limit()与skip()或sort() 前后无关,mongodb自动调整 db.test.find().sort({age:-1}) // 按年龄降序 // 1取0不要,只有_id需要特别指定为0 db.test.find({},{name:1, _id:0, age:1}) // 投影:只要name和age,不要_id db.users.distinct("gender", {age:{$lt:20}}) // 年龄小于20的性别 // --------------------------------- 改 ---------------------------------------- // db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{age:23}) // 小心,新对象替换旧对象 // 应该使用修改符来操作 db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{$set:{name:"老沙", gender:"",address:"流沙河"}}) db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{$set:{age:88}}) db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{$unset:{address:0}}) // 删除某字段 只删除第一个匹配到的
    // $unset 删除字段时,字段写什么值并不影响结果。

      db.test.update({},{$unset:{"Profiles":""}},{multi:true})      // 删除了所有的 Profiles 字段
      // db.test.update({},{$unset:{"Profiles":""}})
      db.test.update({},{$unset:{"Qr":"SOCKET_EMPTY"}},{multi:true})  // 删除所有的 Qr 字段

    db.test.update({name:"AAAA"},{$set:{address:"test"}})       // 默认只改一条
    db.test.update({name:"AAAA"},{$set:{address:"999"}},{multi:true})  // 使用multi改所有的
    
    db.test.updateMany({name:"AAAA"},{$set:{address:"105"}})   // 改所有符合的
    db.posts.updateMany({created_at:{$exists:false}},{$set:{created_at:'2019-07-08 16:20:00'}})  // 不存在某字段时,加上
     
    /* ********************************* 删 ****************************************/
    db.test.remove({address:"999"})     // 默认删除全部匹配的行
    db.test.remove({address:"999"}, true)    // 只删除一行
    db.test.remove({},true)    // 删除第一行
    db.test.remove({})         // 清空集合 
    
    db.test.drop()     // 删除集合
    db.dropDatabase()  // 删当前库(赶紧跑路)
    
    // 通常上面的都不会执行, 使用字段isDel代替
    db.test.updateOne({name:"AAAA"},{$set:{isDel:1}})  
    db.test.find({isDel:0})
    // $type 操作符 https://www.mongodb.org.cn/tutorial/15.html
    db.test.find({"title" : {$type : 2}})  // 条件:字段为string类型

     // -----------#-----------#----------- 练习 ------#-----------#-----------#------- 

    // 属性值也可以是个文档 即 内嵌文档
    db.goods.update({name:"CPU"},{$set:{band:{Intel:["I3","I5","I7"],AMD:["R5","R7"]}}})
    db.goods.find({'band.AMD':'R5'})   // 查询内嵌时必须引号, 包含
    db.goods.find({'band.AMD':'R5'},{'band.AMD.$':1})   // 仅取出AMD.R5的列表元素,不要其它的元素。
    
    db.goods.update({name:"Mem"},{$push:{'band.KST':'3333'}})     // 插入一个,可重复
    db.goods.update({name:"Mem"},{$addToSet:{'band.KST':'3333'}}) // 添加到集合,不重复
    db.goods.update({name:"Mem"},{$pop:{'band.KST':1}})           // 移除最后一个元素
    
    //# 插入20000条数据
     // 1. 笨方法:
        for(var i=1; i<20001;i++){
               db.demos.insert({num:i})
        }
        
     // 2. 好方法:
        var arr=[];
        for(var i=1; i<20001;i++){
           arr.push({num:i});
        }
        db.demoes.insert(arr)

     文档间关系

    //# 1. 一对一:通过内嵌文档的形式.
        db.married.insert([
            {name:"Tom",guy:{name:"Jerry"}},
            {name:"大郎",guy:{name:"金莲"}}
        ])
    
    //# 2. 一对多, 多对一:
        db.users.insert([
            {username:"swk"},
            {username:"zbj"}
        ])
        
        db.order.insert([
            {list:["CPU","Mem","Disk"],
             user_id: ObjectId("5cef98a9e4a985f4ad3e897c")
            },
            {list:["Apple","Pear","Cherry"],
             user_id: ObjectId("5cef98a9e4a985f4ad3e897c")
            }
        ])
        
        db.order.insert(
            {list:["Pig","Donkey","Sheep"],
             user_id: ObjectId("5cef98a9e4a985f4ad3e897d")
        })
        
        // 查询
        var user_id=db.users.findOne({username:"swk"})._id;
        db.order.find({user_id:user_id})
    
    //# 3. 多对多:
        db.teacher.insert([
            {name:"如来"},
            {name:"观音"},
            {name:"菩提"}
        ])
        
        db.students.insert([
            {"name" : "Tom",
            "teacher_ids" : [ 
                ObjectId("5cefbdffe4a985f4ad3e8984"), 
                ObjectId("5cefbdffe4a985f4ad3e8982")
            ]},
            {"name" : "Jerry",
            "teacher_ids" : [ 
                ObjectId("5cefbdffe4a985f4ad3e8984"), 
                ObjectId("5cefbdffe4a985f4ad3e8983")
            ]}
        ])
        
        // 查询: 如来的徒弟
        var tid = db.teacher.find({name:"如来"})[0]._id     //
        var tid = db.teacher.findOne({name:"如来"})._id 
        db.students.find({teacher_ids:tid})
    
    //-----------------#----------------- 练习 ------------------#------------------- 
    // 1. 找出财务部员工
    var deptno = db.dept.findOne({dname:"财务部"}).deptno 
    db.emp.find({deptno:deptno})
    
    // 2. 低于1000的加400
    db.emp.updateMany({sal:{$lt:1000}},{$inc:{sal:400}})
    db.emp.updateMany({deptno:2},{$inc:{sal:500}})   // IT部加工资
    
    // 3. 价高于300的减100
    db.goods.updateMany({price:{$gt:300}},{$inc:{price:-100}})

    聚合,统计

    // aggregate 用于聚合统计,$group 类似于SQL的sum(), avg() 
    
    // 各求男女人数
    db.users.aggregate([{$group:{_id:'$gender',counter:{$sum:1}}}])
    
    db.users.aggregate([{$group:{_id:'$gender',counter:{$sum:'$age'}}}]) // 年龄合计
    db.users.aggregate([{$group:{_id:'$gender',counter:{$avg:'$age'}}}]) // 平均年龄
    db.users.aggregate([{$group:{_id:'$gender',counter:{$max:'$age'}}}]) // 或 min
    db.users.aggregate([{$group:{_id:'$gender',counter:{$first:'$age'}}}]) // 或 last
    
    // 列出各年龄到数组,不去重
    db.users.aggregate([{$group:{_id:'$gender',counter:{$push:'$age'}}}])
    
    // 男女分组,列出name到数组
    db.users.aggregate([{$group:{_id:'$gender',counter:{$push:'$name'}}}])
    
    // 男女分组,基于整个文档列出所有字段到数组
    db.users.aggregate([{$group:{_id:'$gender',counter:{$push:'$$ROOT'}}}])
    
    
    //##  $match
    db.users.aggregate([{$match:{age:{$gt:25}}}])    // 筛选
    
    // 将筛选后的结果再聚合
    db.users.aggregate([
        {$match:{age:{$gt:25}}},
        {$group:{_id:'$gender',counter:{$sum:1}}}
    ])                                       
    
    // 筛选,聚合,投影
    db.users.aggregate([
        {$match:{age:{$gt:25}}},
        {$group:{_id:'$gender',counter:{$push:'$name'}}},
        {$project:{_id:0,counter:1}}
    ])
    
    // 排序,和find()中的几乎一样 {$sort:{_id: -1}} 也可以用在多个地方
    db.users.aggregate([
        {$match:{age:{$gt:25}}},
        {$sort:{name: 1}}, 
        {$group:{_id:'$gender',counter:{$push:'$name'}}},
        {$sort:{_id:-1}}
    ])    // 内按name升序,外按_id降序
    
    // 跳过 {$skip:10},限制{$limit:5} 此处区分顺序
    
    /**  
    * {$unwind:"$counter"} 将数组字段拆开成单条 
    */
    db.students.aggregate([
        {$match:{name:"WuKong"}},
        {$unwind:"$teacher_ids"}
    ])
    
    db.t3.insert([
        {"_id":1,"item":"a","size":["S","M","L"]},
        {"_id":2,"item":"b","size":[]},
        {"_id":3,"item":"c","size":"M"},
        {"_id":4,"item":"d"},
        {"_id":5,"item":"e","size":null},
    ])
    
    db.t3.aggregate([{$unwind:{path:"$size"}}])  // 取出值 不含空和null以及无字段
    
    // 取出值,不丢任何数据
    db.t3.aggregate([
        {$unwind:{path:"$size", preserveNullAndEmptyArrays:true}}
    ])


    // 示例1:找出几个字段组合的重复数据
    db.products.aggregate([
    {
        '$group': {
            '_id': {
                'Product': '$Product',
                'Machine': '$Machine',
                'Station': '$Station',
                'Status': '$Status',
                'Price': '$Price'
            },
            'uniqueIds': {
                '$addToSet': '$_id'
            },
            'count': {
                '$sum': 1
            }
        }
    },
    {
        '$match': {
            'count': {
                '$gt': 1
            }
        }
    }], 
    {
        allowDiskUse: true
    });
    
    

    // 示例2: 排除某个状态的重复数据查找

    db.members.aggregate([
    {'$match': {'Status': {'$ne': '-1'}}},
    {
            '$group': {
                '_id': {
                'Station': '$Station',
                'Name': '$Name'            
                },
                'uniqueIds': {
                    '$addToSet': '$_id'
                },
                'count': {
                    '$sum': 1
                }
            }
        },
        {
            '$match': {
                'count': {
                    '$gt': 1
                }
            }
        }
    ], {
        allowDiskUse: true
        });

    // 示例3: 某日期之前,产品按名称统计。
    db.cms.aggregate([
    {'$match':{'create_ts':{'$lt':ISODate('2020-06-01T06:30:29.655Z')}}},
    {$group:{_id:'$ProductName',counter:{$sum:1}}}])
    
    
     

    索引

    /********************************** 索引 **************************************/
    db.t1.ensureIndex({name:1})                  // 添加索引
    db.t1.ensureIndex({name:1}, {unique:true})  // 多参数
    db.t1.ensureIndex({name:1,age:1})           // 联合索引
    db.t1.getIndexes()                          // 查询索引
    db.t1.dropIndex("name_1")                   // 删除指定索引
    db.t1.dropIndexes()                         // 删除全部索引,保留默认 _id_ 索引
    
    // 测试数据
    let arr=[];
    for(let i=0;i<50000;i++){
        arr.push({name:"test"+i, age:i});
    }
    db.t1.insert(arr)
    
    // 性能评估   其中结果 executionTimeMillis 为毫秒
    db.t1.find({name:'test9999'}).explain('executionStats')
    
    db.t1.ensureIndex({name:1}) // 添加索引
    
    // 再次执行 结果 executionTimeMillis 降低很多
    db.t1.find({name:'test9999'}).explain('executionStats')

    // 删除数据时,索引不会删除,并且体积可能变得更大,必要时可以删除索引,重建索引。

    /*************************** 复制集 ******************************/

     // 官文 https://docs.mongodb.com/manual/replication/

    主备

    // 例1:单机演示。(生产环境不适用)使用不同端口,不同数据目录,必须设置相同 replSet
        mongod --bind_ip 192.168.112.9 --port 27018 --dbpath /data/t1 --replSet rs0
        mongod --bind_ip 192.168.112.9 --port 27019 --dbpath /data/t2 --replSet rs0
        // 服务运行窗口可以看到后续操作的日志显示
    
        // 然后客户端分别连接服务
        mongo --host 192.168.112.9 --port 27018    
        rs.initiate()   // 初始化为master 提示符发生变化 
        rs.status()     // 查看复制集状态  rs0:PRIMARY>
        rs.isMaster()   // 是否主节点
        rs.add("192.168.112.9:27019")  // 添加slave
    
        mongo --host 192.168.112.9 --port 27019
        rs.slaveOk()    // slave上执行同意
        
    // 例2:不同机器(接近生产环境)。必须设置相同 replSet
        // 注意OS版本,rs1:PRIMARY 可低版本,rs1:SECONDARY可高版本。反之则rs.add出错
        // 本例PRIMARY为112.6(centos6.5), SECONDARY为112.9(centos7.5)
        mongod --bind_ip 192.168.112.6 --port 27018 --dbpath /data/t3 --replSet rs1
        mongod --bind_ip 192.168.112.9 --port 27018 --dbpath /data/t3 --replSet rs1
    
        // 使用客户端分别连接服务
        mongo --host 192.168.112.6 --port 27018
        mongo --host 192.168.112.9 --port 27018
    
        rs.initiate()   // 112.6 为 master 
        rs.status()     // 查看复制集状态  rs1:PRIMARY>
        rs.add("192.168.112.9:27018")  // 添加slave
    
        rs.slaveOk()    // 112.9 上执行同意
    
    // 测试数据 master
    use demo1
    db.users.insert({name:"Allen",age:28})
    db.users.insert({name:"Bob",age:38})
    
    // slave上查询
    use demo1
    db.users.find()  // 得到相同结果
    
    // 发现master当机后,某slave则自动变为PRIMARY 
    // 而修好后的master启用,加入到rs1,转为SECONDARY 
    
    /////////////////////// 添加一个节点(192.168.112.7:27017) // 新节点 安装,添加目录,然后执行: mongod --bind_ip 192.168.112.7 --port 27017 --dbpath /data/t3 --replSet rs1 // 然后 rs1:PRIMARY> 上执行: rs.add("192.168.112.7:27017") // 添加slave节点 // slave 新窗口上执行: mongo --host 192.168.112.7 --port 27017 > rs.slaveOk() // 即可自动更新所有数据 /////////////////////// 删除一个节点 rs1:PRIMARY> rs.remove("192.168.112.6:27018") // 被删节点将不可用 /////////////////////// 停止节点服务 mongod --bind_ip 192.168.112.7 --port 27017 --dbpath /data/t3 --shutdown // 其它命令:
    rs.conf() // 显示复制集信息
    rs.printReplicationInfo() // 从primary角度显示复制集信息,显示 oplog 信息
    rs.printSlaveReplicationInfo() // 从secondary角度显示复制集信息
    db.serverStatus().asserts // 查看断言
    rs.stepDown() // 强制primary降为secondary,并引发新的选举. 慎用
    #故障案例:某个 slave 节点自动down掉,再次启动时过一会还是down掉,检查日志后发现:
    [replication-0] We are too stale to use 10.1.1.34:27017 as a sync source. Blacklisting this sync source because our last fetched timestamp: 
    Timestamp(1584041941, 30) is before their earliest timestamp: Timestamp(1584399943, 455) for 1min until:

    解决办法:将此 slave 停止服务, 改名或删除 mongodb 仅数据目录后,重新启动 mongodb, 如果状态显示为 startup2 则表示成功开始同步数据。等待同步完成即可恢复正常。

    备份与恢复

    // mongodump -h dbhost -d dbname -o outdir  
    // 备份将在输出目录中生成Collection对应的 bson 和 metadata.json 文件
    mongodump -u admin -p admin --authenticationDatabase admin -h 192.168.112.9 -d test -o /bak/mongodb
    
    // 恢复 mongorestore -h dbhost -d dbname --dir inputdir
    mongorestore -u admin -p admin --authenticationDatabase admin -h localhost -d test --dir /bak/mongodb/test/

    Python pymongo

    // driver for python 
    pip3 install pymongo

    /*************************************** demo.py ******************************/

    #!/usr/bin/env python
    # coding:utf-8
    
    from pymongo import *
    
    client = MongoClient('mongodb://test1:test1@192.168.112.9:27017/test')
    
    db = client.test  #
    users = db.users  # Collections
    
    # res = users.insert({'name':'python','age':88,'gender':'female'})      # 插入
    # print(res)  # 返回 id
    # ret = users.insert_one({'name':'pip3','age':19,'gender':'male'})      # 插入
    # print(ret)  # 返回 object
    
    # ret2 = users.update_one({'name':'python'},{'$set':{'name':'Python3'}}) # 修改
    # print(ret2)
    
    # ret3 = users.delete_one({'name':'pip3'})    # 删除
    # print(ret3)
    
    # cursor = users.find_one({'name':'Python3'})   # 只取一条
    # print(type(cursor), cursor)
    # for k, v in cursor.items():
    #     print(k + " --> " + str(v))
    
    # cursor = users.find({'age':{'$gt':30}})       # 取多条
    # for s in cursor:
    #     print(s)
    #     # print(s['name'], s['age'])
    
    # cursor = users.find().sort([('gender',1),('age',-1)])   # 多条件降序
    # for s in cursor:
    #     print(s)
    
    cursor = users.find().sort('age',-1).limit(2).skip(3)    # 子集
    for s in cursor:
        print(s)

    Node.js  mongoose

    因为npm 库中的 mongodb没有这个好用,所以直接使用 mongoose 

    /**
        1. 安装
            npm i mongoose --save
    
        2. 引入
            let mongoose = require("mongoose");
    
        3. 连接数据库
            mongoose.connect('mongodb://root:123456@192.168.112.9/odm_demo?authSource=admin',{useNewUrlParser:true});
            默认27017可以省略
            通常只需连接一次,除非项目停止或服务器关闭,否则连接一般不会断开。
    
        4. 断开连接(一般不需要)
            mongoose.disconnect();
            一般情况下,只需要连接一次; 除非项目停止服务器关闭,否则连接一般不会断开
    
        - 监听Mongodb连接状态
                - 在mongoose对象中,有一个属性叫做connection 表示的就是数据库连接
                  通过监视该对象的状态,可以来监听数据库的连接与断开
    
                - 连接成功事件
                  mongoose.connection.once("open",function(){});
    
                - 数据库断开事件
                  mongoose.connection.once("close",function(){});
    */
    
    const mongoose = require('mongoose');
    
    
    mongoose.connect('mongodb://admin:admin@192.168.112.9/admin', {useNewUrlParser: true}); // 正确写法
    
    mongoose.connection.once('open',function () {
        console.log("连接成功.");
    });
    
    mongoose.connection.once('close',function () {
        console.log('数据库已断开.');
    });
    
    mongoose.disconnect(); // 一般不会调用

    mongoose demo

    const mongoose = require('mongoose');
    
    mongoose.connect('mongodb://root:123456@192.168.112.9/odm_demo?authSource=odm_demo',{useNewUrlParser:true});
    mongoose.connection.once('open',function (err) {
        if(err){
            console.log(err);
        }else{
            console.log("连接成功~~~");
        }
    });
    
    /**
     *  Schema
     *  Model
     *  Document
    */
    
    // 1. 定义 Schema
    let Schema = mongoose.Schema;
    
    // 2. 创建模式(约束) Schema 对象
    let stuSchema = new Schema({
        name: String,
        age: Number,
        gender:{
            type: String,
            default:"female"
        },
        address:String
    });
    
    // 3. 通过Schema创建 Model (Collection),通过Model才能操作数据库
    let StuModel = mongoose.model("child", stuSchema); // 集合名自动变复数
    
    // 4. 插入文档 Model.create(doc,function (err) {});
    StuModel.create({
        name:"紫霞仙子",
        age:18,
        // gender:"male",
        address:"水帘洞"
    },function (err) {
        if(!err){
            console.log("插入成功.");
        }
    });

    模型用法示例

    require("./common/conn_mongo");
    let StuModel = require('./model/users');
    
    // 插入 Model.create(doc(s),[callback])
    /* StuModel.create([
        {
            name:"沙和尚",
            age:42,
            gender:"male",
            address:"流沙河"
        }
    ],function (err) {
        if(!err){
            // console.log('插入成功~~~');
            console.log(arguments);  // 返回插入的内容
        }
    });
    
    StuModel.create(
        [{
            name:'白骨精',
            age:90,
            address:'山洞'
        },{
            name:'女王',
            age:38,
            address:'女儿国'
        }], function (err) {
            if(!err){
                console.log('插入成功.')
            }
        }
    );   */
    
    
    /** 查询:
     *  Model.find(conditions,[projection],[options],[callback])
     *     查询所有符合条件的文档 返回数组
     *  Model.findById(id,[projection],[options],[callback])
     *     根据ID属性查询文档
     *  Model.findOne([conditions],[projection],[options],[callback])
     *     查询符合条件的第一个文档
     *
     *  Model.count(conditions, [callback])  // 数量
     *
     *    - projection 投影 想要的字段
     *         两种方式: {name:1, _id:0}
     *                   "name -_id"
     *    - options  选项(skip, limit )
     *          {skip:2, limit:1}
     *    - callback  回调函数 必传, 不传则不会查询
     */
    
    // StuModel.count({},function (err, count) {  // count 是旧方法
    StuModel.countDocuments({},function (err, count) { // 新方法
        if(!err){
            console.log(count);
        }
    });
    
    
    StuModel.find({name:"八戒"},function (err,docs) {  // 返回数组
        if(!err){
            if(docs.length > 0){
                console.log(docs)
                // console.log(docs[0].name)
            }else{
                console.log('docs is empty')
            }
        }
    });
    
    StuModel.findOne({name:"唐僧"},function (err, doc) {  // 返回文档对象, 就是Document
        if(!err){
            if(doc){
                // console.log(doc)
                console.log(doc.address);
                console.log(doc instanceof StuModel)  // Document对象是Model的实例
            }else{
                console.log('no result')
            }
        }
    });
    
    StuModel.findById('5d108a9fe15d35242c37666c',function (err, doc) {
        if(!err){
            console.log(doc.name);
        }
    });
    
    
    // // 投影 只取name age 不要 _id
    // StuModel.find({},{name:1, age:1, _id:0},function (err, docs) { // 对象参数
    StuModel.find({},'name age -_id', {skip:1,limit:1}, function (err, docs) {   // 字符串参数
        if(!err){
            if(docs.length > 0){
                console.log(docs)
            }
        }
    });
    
    
    
    /** 修改
     *  Model.update(conditions, doc, [options], [callback])
     *  Model.updateMany(conditions, doc, [options], [callback])
     *  Model.updateOne(conditions, doc, [options], [callback])
     *  Model.replaceOne(conditions, doc, [options], [callback])
     *     - conditions 查询条件
     *     - doc 修改后的对象
     */
    
    StuModel.updateOne({name:"唐僧"},{$set:{age:77}},function (err) {
        if(!err){
            console.log('修改成功.');
        }
    });
    
    
    /** 删除  一般不用
     *  Model.remove(conditions, [callback])
     *  Model.deleteOne(conditions, [callback])
     *  Model.deleteMany(conditions, [callback])
     */
    
    // StuModel.deleteOne({name:'白骨精'},function (err) {
    //     if(!err){
    //         console.log('删除成功.')
    //     }
    // });

    common/conn_mongo.js

    let mongoose = require("mongoose");
    
    mongoose.connect('mongodb://admin:admin@192.168.112.9/odm_demo?authSource=admin',{useNewUrlParser:true});
    mongoose.connection.once('open',function (err) {
        if(err){
            console.log(err);
        }else{
            console.log("连接成功~~~");
        }
    });

    model/users.js

    /**
      定义模型
     */
    
    const mongoose = require('mongoose');
    let Schema = mongoose.Schema;
    
    let stuSchema = new Schema({
        name: String,
        age: Number,
        gender:{
            type: String,
            default:"female"
        },
        address:String
    });
    
    // 有了model 即可CURD
    let UsrModel = mongoose.model("users", stuSchema); // 如果是复数,则名称不变
    module.exports = UsrModel;

    文档用法示例:

    require('./common/conn_mongo');
    let monster = require('./model/monster');
    
    /**
     *  Document 和 集合中的文档一一对应,Document是Model的实例
     *  通过 Model 查询到的结果都是 Document
     */
    let stu = new monster({
        name:"奔波儿霸",
        age:888,
        gender:"male",
        address:"碧波潭"
    });
    
    // console.log(stu);
    
    
    // Document对象的方法  Model#save([options],[fn])
    // 而 create 是 Model 的方法
    // stu.save(function (err) {
    //     if(!err){
    //         console.log("写入成功~")
    //     }
    // });
    
    
    monster.findOne({},function (err, doc) {
        if(!err){
            /**
             *  update(update,[options],[callback]))
             *    - 修改对象
             *  save([callback]))
             *  update([callback]))
             */
            // console.log(doc);
            // doc.update({$set:{age:22}},function (err) {  // 过时的方法
            //     if(!err){
            //         console.log("修改成功.")
            //     }
            // });
    
            // doc.updateOne({$set:{address:"高老庄"}},function (err) {
            //     if(!err){
            //         console.log("修改成功");
            //     }
            // });
    
            // // 直接修改
            // doc.age = 2000;
            // doc.save();
    
            // 删除
            // doc.remove(function (err) {
            //     if(!err){
            //         console.log("师傅再见~")
            //     }
            // })
    
            /**
             * get(name)
             * set(name, value)
             * id 获取文档的 _id 属性
             * toJSON()
             * toObject()  document对象方法则失效
             */
            console.log(doc.get("name"));
            console.log(doc.address);
    
            // doc.set("name","猪悟能");  // 需要 save() 才能修改数据库
            // doc.name = "奔波霸";
            // doc.save();
            console.log(doc.id);
    
            console.log(typeof doc); // Object
            let t = doc.toObject();  // 转为普通 JS 对象
            delete t.__v;
            delete t._id;
            console.log(t);
        }
    });

     model/monster.js

    const mongoose = require('mongoose');
    let Schema = mongoose.Schema;
    let stuSchema = new Schema({
        name: String,
        age: Number,
        gender:{
            type: String,
            default:"female"
        },
        address:String
    });
    
    let MonsterModel = mongoose.model("monsters", stuSchema);
    module.exports = MonsterModel;
  • 相关阅读:
    单链表
    找最长最短字符串
    注解
    Json字符串
    java的反射机制
    java类的加载与加载器
    String StringBuilder StringBuffer
    查看运行某类时当前的进程状态
    迷宫小游戏
    类的初始化过程、super关键字与函数绑定
  • 原文地址:https://www.cnblogs.com/frx9527/p/mongodb.html
Copyright © 2020-2023  润新知