zoukankan      html  css  js  c++  java
  • MongoDB一篇从入门到实战

    MongoDB数据库介绍

    1、MongoDB简介

    MongoDB官网:https://www.mongodb.com

    • MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统
    • MongoDB 可在高负载的情况下,添加更多的节点,可以保证服务器性能
    • MongoDB 可为Web应用提供可扩展高性能数据存储解决方案。
    • MongoDB 将数据存储在灵活的json文档中,这意味着可以直接得到从文档到文档的数据、结构等。
    • MongoDB 是免费使用的(MongoDB分 社区版 [在所有环境下都免费] 和 企业版[在开发环境免费,生产环境收费] 两个版本)
    • MongoDB 数据库具有可伸缩性灵活性,可帮助你快速查询索引你需要数据。

    MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

    它是可扩展的高性能数据存储解决方案,经常被用于非关系型数据的存储,能存储海量的数据。

    常见的非关系型数据库还有:Redis,但 MongoDb 比 redis 更具有数据库的特性。

    • 关系型数据库有:MySQL、Oracle、SQL Serve.....

    • 非关系型数据库有:Redis、MongoDB...

    对比

    传统的关系型数据库(如 MySQL)存储数据使用的是一种“行/列模型”。而 MongoDB 是一种“文档数据库”,它将数据存储在类似 JSON 的文档中。

    以 JSON 文档的方式存储数据,其中的好处有很多。比如,支持使用数组和对象作为值、文档间可以嵌套、查询的本身就是一个 JSON 等。

    2、基本数据结构

    与 MySQL 等关系型数据库一样,MongoDb 也有类似的概念,但是其称呼不同。下表列出了 MongoDb 的基本数据结构以及与 MySQL 的对比。

    SQL术语/概念 MongoDB术语/概念 解释/说明
    database database 数据库
    table collection 数据库表/集合
    row document 数据记录行/文档
    column field 数据字段/域
    index index 索引
    table joins 表连接,MongoDb不支持
    primary key primary key 主键,MongoDb自动将_id字段设置为主键

    3、基本数据类型

    数据类型 描述
    String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
    Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
    Boolean 布尔值。用于存储布尔值(真/假)。
    Double 双精度浮点值。用于存储浮点值。
    Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
    Array 用于将数组或列表或多个值存储为一个键。
    Object 用于内嵌文档。
    Null 用于创建空值。
    Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
    Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
    Object ID 对象 ID。用于创建文档的 ID。(如果不指定的话,mongodb默认会给一个 _id)
    Binary Data 二进制数据。用于存储二进制数据。
    Code 代码类型。用于在文档中存储 JavaScript 代码。
    Regular expression 正则表达式

    MongoDB下载与安装

    1、Win10安装

    官网下载 地址:https://www.mongodb.com/try/download/community

    其他下载方式:除了上面的下载方式以外,也可以试试下面的下载链接!

    1、下载指定版本

    20201210220729

    • MongoDB Enterprise Server:企业版(收费版)
    • MongoDB Community Server:社区版(免费版)(我们下载社区版本)
    • 下载完成后,获得安装包:mongodb-windows-x86_64-4.4.2-signed.msi,双击即可安装。

    2、选择安装方式:

    • 如果已经安装过该版本的MongoDB,会是这个界面(如果没有安装过忽略此步骤)

    20201211231045

    • 选择Complete,表示安装到默认路径(不推荐,C盘懂的都懂)

    • 选择Custom,表示自定义安装路径(推荐)

    20201211231046

    3、选择安装路径:

    20201211231456

    20201211232513

    注意:MongoDB 3.x 系列版本的数据库,在安装成功后,每次在使用前都需要手动启动MongoDB服务!

    现在:MongoDB 4.x 系列版本的数据库,在安装时默认安装(选中了 Install MongoD as a Service)服务 ,就是在开机时自动启动 MongoDB 服务,然后就可以直接使用啦!

    20201211233227

    4、然后就一直下一步、Next 直到 Flnish 安装完毕,到此就MongoDB就安装结束啦!

    5、检查是否安装成功(Windows环境下)

    • 方式一:
    1、运行 Win + R
    2、输入 services.msc 命令便可以查看到 MongoDB Server (MongoDB) 服务啦!!
    
    • 方式二:
    1、打开任意浏览器输入:http://127.0.0.1:27017/
    2、返回如下信息代表安装成功:
    It looks like you are trying to access MongoDB over HTTP on the native driver port.
    
    • 方式三:
    C:Userslenovo>mongod -version
    db version v4.4.2
    Build Info: {
        "version": "4.4.2",
        "gitVersion": "15e73dc5738d2278b688f8929aee605fe4279b0e",
        "modules": [],
        "allocator": "tcmalloc",
        "environment": {
            "distmod": "windows",
            "distarch": "x86_64",
            "target_arch": "x86_64"
        }
    }
    

    6、目录文件说明:

    20201212120352

    默认配置文件:bin/mongod.cfg

    # http://docs.mongodb.org/manual/reference/configuration-options/
    # Where and how to store data.
    storage:
      dbPath: D:EnvironmentMongoDBServer4.4data # 数据存储目录
      journal:
        enabled: true
    
    # where to write logging data.
    systemLog:
      destination: file
      logAppend: true # 日志采用追加模式
      path:  D:EnvironmentMongoDBServer4.4logmongod.log # 日志文件地址
    
    # network interfaces
    net:
      port: 27017
      bindIp: 127.0.0.1 # 数据库端口号
      
    # 开启数据库访问权限验证(注意:换行、缩进格式哦!!)
    security: 
      authorization: enabled
    

    7、配置环境变量(选配,看个人喜好,为了使用方便我配置了)

    配置完毕后就是在电脑中任何地方都可以合用mongo、mongod等命令

    20201212110639

    8、mongod.exe 和 mongo.exe 命令

    一定要以管理员身份启动CMD!一定要以管理员身份启动CMD!一定要以管理员身份启动CMD!

    这两个命令的作用分别是:

    • mongod 启动 MongoDB 服务器。
    • mongo 在启动服务器后,帮助我们连接到 MongoDB 实例,也就是进入 Mongo shell(相当于让我们进入后台操作数据库)。

    mongod 命令启动参数:

    参数 描述
    --config或-f 指定配置文件路径
    --bind_ip 绑定服务IP,若绑定127.0.0.1,则只能本机访问,不指定默认本地所有IP(0.0.0.0)
    --dbpath 指定数据库路径
    --logpath 定MongoDB日志文件,注意是指定文件不是目录
    --logappend 使用追加的方式写日志
    --port 指定服务端口号,默认端口27017
    --serviceName 指定服务名称
    --serviceDisplayName 指定服务名称,有多个mongodb服务时执行。
    --install 指定作为一个Windows服务安装。
    --remove 移除MongoDB服务
    --auth 表示加载认证功能

    mongod 命令安装示例:

    ######################## MongoDB方式创建win服务:########################
    # 安装 MongoDB 服务,指定配置文件安装(没有指定服务名的话默认服务名为:MongoDB)
    mongod --config "D:EnvironmentMongoDBServer4.4inmongod.cfg" --install
    # 或者
    mongod --bind_ip 0.0.0.0 --dbpath "D:EnvironmentMongoDBServer4.4datadb" --logpath "D:EnvironmentMongoDBServer4.4logmongod.log" --logappend --port 27017 --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install
    
    # 启动和关闭 MongoDB 服务,该名称位--serviceName设置的名称
    net start MongoDB
    net stop MongoDB
    
    # 移除 MongoDB 服务,必须先关闭 MongoDB 服务
    mongod --remove
    
    
    ######################## windows自带创建win服务方式:########################
    # 创建 MongoDB 服务(中间几个引号很关键)
    sc.exe create MongoDB binPath= ""D:EnvironmentMongoDBinmongod.exe" --service --config= "E:EnvironmentMongoDBmongo.config"" DisplayName= "MongoDB" start= "auto"
    
    # 删除 MongoDB 服务
    sc delete MongoDB
    

    mongo命令:进入 MongoDB Shell界面(登录)

    # 1、mongo # 直接进入
    # 2、mongo 127.0.0.1:27017/config # 连接到任何数据库config
    # 3、mongo --nodb #不连接到任何数据库
    # 4、启动之后,在需要时运行new Mongo(hostname)命令就可以连接到想要的mongod了:
    > conn=new Mongo('127.0.0.1:27017')
    connection to 127.0.0.1:27017
    > db=conn.getDB('admin')
    admin
    # 4、help查看帮助
    
    
    # mongo -u admin -p password # 连接本地数据库,使用了--auth启动,输入账号密码
    # 连接运程MongoDB数据库:(必须指定 绑定IP 和 端口号)
    # 格式:mongo 绑定IP:端口号/数据库名 -u 用户名 -p 密码
    mongo 127.0.0.1:27017/test -u admin -p password
    或者
    mongo --host 127.0.0.1 --port 27017
    
    # 也可以登录shell后输入用户名和密码登录
    db.auth("admin", "password")
    

    2、Linux安装

    MongoDB官网下载地址:https://www.mongodb.com/try/download/community

    1、安装步骤:

    # 1.切换opt目录
    $ cd /opt/
    
    # 2.wget下载安装包
    $ wget -P /opt/ https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-4.4.2.tgz
    
    # 3.解压
    $ tar -xzvf /opt/mongodb-linux-x86_64-amazon-4.4.2.tgz
    
    # 4.移动目录并且重命名
    $ mv /opt/mongodb-linux-x86_64-amazon-4.4.2 /usr/local/mongodb
    
    # 5.新建data和logs目录
    $ mkdir -p /usr/local/mongodb/{data,logs}
    
    # 6.授权目录
    $ chmod 777 /usr/local/mongodb/{data,logs}
    
    # 7.profile配置文件中增加环境变量,然后刷新配置
    $ echo "export PATH=$PATH:/usr/local/mongodb/bin" >> /etc/profile && source /etc/profile
    

    2、启动MongoDB服务

    MongoDB 的启动方式分为两种:前置启动、后置启动

    无论哪种启动方式都需要执行 bin 目录中的 mongod 命令。MongoDB 在启动时默认的查找数据库的路径为/data/db。如果我们数据库路径有变化,需要在该命令中通过--dbpath 参数来指定 db 目录的路径(该路径可以是绝对路径,也可是相对路径)

    2.1、前置启动(配置参数)

    # data 目录需要提前创建
    mongod --dbpath /usr/local/mongodb/data
    

    2.2、后置启动(配置参数)

    所谓后置启动就是以守护进程的方式启动 MongoDB。

    我们需要在执行 mongod 命令中添加 --fork 参数。需要注意的是,--fork 参数需要配合着--logpath 或者是--syslog 参数使用。--logpath 与--syslog 参数是指定 MongoDB 的日志文件。MongoDB 的日志文件可以在系统中*的任意位置,本文章中我们在 mongodb 目录下创建 log 目录,在该目录中创建一个名为mongodb.log 的日志文件。

    # data和logs目录需要提交创建,mongodb.log文件可不创建。--fork后台启动
    mongod --dbpath /usr/local/mongodb/data --logpath /usr/local/mongodb/logs/mongodb.log --fork
    

    2.3、配置文件方式启动

    如果觉得在启动 MongoDB 时给定的参数项太多,那么我们也可以通过配置文件来配置启动参数,配置文件可以在任意目录中,配置文件的扩展名应为.conf,配置文件中使用key=value 结构。在执行 MongoDB 时通过--config 参数来指定需要加载的配置文件。我们在 /bin 目录中创建一个名为 mongodb.conf 配置文件

    # 1.创建配置文件 mongodb.conf 
    $ sudo tee /usr/local/mongodb/bin/mongodb.conf <<-'EOF'
    dbpath=/usr/local/mongodb/data
    logpath=/usr/local/mongodb/logs/mongodb.log
    port=27017
    fork=true
    bind_ip=0.0.0.0
    #auth=true
    EOF
    
    # 1.或vim 编辑
    $ vim /usr/local/mongodb/bin/mongodb.conf
    # 增加如下配置
    dbpath=/usr/local/mongodb/data                 # 指定 db 路径
    logpath=/usr/local/mongodb/logs/mongodb.log    # 指定日志文件
    logappend=true                                 # 使用追加的方式写日志
    port=27017                                     # 配置端口
    fork=true                                      # 配置后端启动
    bind_ip=0.0.0.0                                # 设置所有ip都可以访问
    journal=true      # 每次写入会记录一条操作日志(通过journal可以重新构造出写入的数据)。
    directoryperdb=true
    quiet=true
    bind_ip_all=true
    auth=true   # 启动验证
    #noauth=true # 不启用验证
    
    # 2.通过加载配置文件启动MongoDB
    $ mongod -f /usr/local/mongodb/bin/mongodb.conf
    $ mongod --config /usr/local/mongodb/bin/mongodb.conf
    

    3、关闭MongoDB

    • Ctrl+C关闭(安全关闭):前置启动的话可以直接使用快捷键 Ctrl+C 就可以关闭 MongoDB

    • kill命令关闭(非安全关闭):kill -9 PID、kill -2 PID、kill -4 PID

    • MongoDB函数关闭(安全关闭,要在admin库执行):db.shutdownServer()db.runCommand("shutdown")

    • mongod 命令关闭(安全关闭):./mongod --shutdown --dbpath<数据库路径>

    示例:

    ## 方式一
    > use admin
    switched to db admin
    > db.shutdownServer()
    server should be down...
    > 
    
    ## 方式二
    $ mongod --shutdown --dbpath /usr/local/mongodb/data/db
    

    3、Docker安装

    docker 安装

    # 1.下载MongoDB Docker镜像
    $ docker pull mongo:4.4.2
    $ docker pull mongo:latest
    
    # 2.启动MongoDB服务
    $ docker run -itd -p 27017:27017 --name mongodb mongo:latest
    $ docker run -itd -p 27017:27017 --name mongodb 
      -v /mydata/mongo/db:/data/db 
      mongo:latest
    
    # 3.启动MongoDB服务并设置账号
    $ docker run -itd -p 27017:27017 --name mongodb mongo:latest --auth
    $ docker run -itd -p 27017:27017 --name mongodb 
      -v /mydata/mongo/db:/data/db 
      mongo:latest --auth
    
    # 4.进入容器中的MongoDB客户端
    $ docker exec -it mongodb mongo
    
    # 5.在admin数据库中创建超级管理员账号
    > use admin
    > db.createUser({
        user: 'admin',
        pwd: 'password',
        roles: [{ role: "userAdminAnyDatabase", db: "admin" }] 
    });
    > 
    # 6.创建完成后验证是否可以登录
    > db.auth("admin","password")
    

    docker-compose 安装

    • docker-compose.yml
    version: '3'
        services:
        mongodb:
          image: mongo:4.4.2        # 镜像:版本
          container_name: mongo_db  # 容器名
          environment:
            - MONGO_INITDB_ROOT_USERNAME=admin     # root管理员账户
            - MONGO_INITDB_ROOT_PASSWORD=password  # root管理员密码
            - MONGO_INITDB_DATABASE=admin          # 默认的数据库
          volumes:
            - ./mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
            - ./mongo/mongo-volume:/data/db
          ports:
            - "27017-27019:27017-27019"
          restart: always
    
    • init-mongo.js
    // db.getSiblingDB() 相当于 use admin;
    db.getSiblingDB('admin').createUser({
            user: 'user',
            pwd: 'user',
            roles: ['readWrite']
    });
    
    • 执行启动命令即可
    docker-compose up -d
    

    参考:http://www.apgblogs.com/docker-mongodb/#4mongoDb

    账户及权限管理

    注意:安装好MongoDB数据库后,默认是【非授权模式】(也就是不需要任何权限验证、不需要验证账户,直接输入 mongo 命令就可以进行相关操作)

    所以:为了数据的安全,我们都应该去配置数据库的访问权限 和 修改默认(mongodb://127.0.0.1:27017)连接绑定IP 和 端口号!


    MongoDB是基于角色的访问控制,所以创建用户需要指定用户的角色,在创建用户之前需要满足:

    1. 先在admin数据库中创建角色为 userAdminuserAdminAnyDatabase 的用户作为管理用户的用户
    2. 启用访问控制,进行登录用户验证,这样创建用户才有意义

    0、用户角色介绍

    数据库角色分类(Built-In Roles—内置角色):

    • 数据库用户角色:read、readWrite
    • 数据库管理角色:dbAdmin、dbOwner、userAdmin
    • 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
    • 备份恢复角色:backup、restore
    • 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
    • 超级用户角色:root
    • 内部角色:__system
    • 这几个角色直接或间接提供系统超级用户的访问权限:dbOwner 、userAdmin、userAdminAnyDatabase

    各角色说明:

    • read:允许用户读取指定数据库

    • readWrite:允许用户读写指定数据库

    • dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问 system.profile

    • userAdmin:允许用户向 system.users 集合写入,可以找指定数据库里创建、删除和管理用户

    • clusterAdmin:只在 admin 数据库中可用,赋予用户所有分片和复制集相关函数的管理权限

    • readAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的读权限

    • readWriteAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的读写权限

    • userAdminAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的 userAdmin 权限

    • dbAdminAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的 dbAdmin 权限

    • root:只在 admin 数据库中可用。超级账号,超级权限

    • MongoDB数据库中的内置角色:

    (1)、【数据库用户角色】针对每一个数据库进行控制。
    read:提供了读取所有非系统集合,以及系统集合中的system.indexes, system.js, system.namespaces
    readWrite:包含了所有read权限,以及修改所有非系统集合的和系统集合中的system.js的权限.
     
     
    (2)、【数据库管理角色】每一个数据库包含了下面的数据库管理角色。
    dbOwner:该数据库的所有者,具有该数据库的全部权限。
    dbAdmin:一些数据库对象的管理操作,但是没有数据库的读写权限。(参考:http://docs.mongodb.org/manual/reference/built-in-roles/#dbAdmin)
    userAdmin:为当前用户创建、修改用户和角色。拥有userAdmin权限的用户可以将该数据库的任意权限赋予任意的用户。
     
     
    (3)、【集群管理权限】admin数据库包含了下面的角色,用户管理整个系统,而非单个数据库。这些权限包含了复制集和共享集群的管理函数。
    clusterAdmin:提供了最大的集群管理功能。相当于clusterManager, clusterMonitor, and hostManager和dropDatabase的权限组合。
    clusterManager:提供了集群和复制集管理和监控操作。拥有该权限的用户可以操作config和local数据库(即分片和复制功能)
    clusterMonitor:仅仅监控集群和复制集。
    hostManager:提供了监控和管理服务器的权限,包括shutdown节点,logrotate, repairDatabase等。
    备份恢复权限:admin数据库中包含了备份恢复数据的角色。包括backup、restore等等。
     
     
    (4)、【所有数据库角色】
    admin:数据库提供了一个mongod实例中所有数据库的权限角色:
    readAnyDatabase:具有read每一个数据库权限。但是不包括应用到集群中的数据库。
    readWriteAnyDatabase:具有readWrite每一个数据库权限。但是不包括应用到集群中的数据库。
    userAdminAnyDatabase:具有userAdmin每一个数据库权限,但是不包括应用到集群中的数据库。
    dbAdminAnyDatabase:提供了dbAdmin每一个数据库权限,但是不包括应用到集群中的数据库。
     
     
    (5)、【超级管理员权限】
    root: dbadmin到admin数据库、useradmin到admin数据库以及UserAdminAnyDatabase。但它不具有备份恢复、直接操作system.*集合的权限,但是拥有root权限的超级用户可以自己给自己赋予这些权限。
     
     
    (6)、【备份恢复角色】
    backup:数据库备份
    restore:数据库恢复
    
     
    (7)、【内部角色】
    __system
    

    1、创建管理员用户

    启用访问控制登录之前,首先需要在admin数据库中创建角色为userAdminuserAdminAnyDatabase作为用户管理的用户,之后才能通过这个用户创建其它角色的用户,这个用户作为其它所有用户的管理者。

    > # 1.切换admin数据库
    > use admin
    > # 2.添加管理员用户:user/pwd 分别代表用户名和密码,role 代表角色,db 代表要设置的数据库。
    > db.createUser({user:"admin", pwd:"password", roles:[{role: "userAdminAnyDatabase", db: "admin" }]})
    > db.createUser({
            user: "admin",
            pwd: "password",
            roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
        }
    )
    
    Successfully added user: {
    	"user" : "admin",
    	"roles" : [
    		{
    			"role" : "userAdminAnyDatabase",
    			"db" : "admin"
    		}
    	]
    }
    

    2、开启授权访问控制

    要开启访问控制,则需要在mongod进程启动时加上选项--auth或在启动配置文件加入选项auth=true,并重启mongodb实例。

    Windows 环境下

    方式一:配置文件方式启动

    MongoDB数据库的相关配置信息,是存储在mongodb安装目录bin目录中的mongod.cfg文件中,

    • 找到mongod.cfg文件,然后增加如下配置:
    # 开启数据库访问权限验证(注意:换行、缩进格式哦!!)
    security: 
      authorization: enabled
    
    • 然后重新启动MongoDB服务.

    方式二:启动时添加授权参数(以管理员身份启动CMD)

    # 先关闭MongoDB服务,然后移除,最后重新安装MongoDB服务,带上--auth参数
    net stop MongoDB
    mongod --remove
    mongod --config "D:EnvironmentMongoDBServer4.4inmongod.cfg" --install --auth
    net start MongoDB
    

    Linux 环境下

    方式一:配置文件方式启动

    # 1.mongod配置文件追加如下配置:
    $ echo "auth=true" >> /usr/local/mongodb/bin/mongodb.conf
    
    # 2.启动mongodb实例
    $ mongod --conf mongodb.cnf
    
    # 3.使用mongo shell登录mongodb实例:
    $ mongo 127.0.0.1:27017
    MongoDB shell version v4.0.9
    connecting to: mongodb://192.168.58.2:27017/test?gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("428c215c-2927-49ee-8507-573efc4a1185") }
    MongoDB server version: 4.0.9
    >
    
    # 如果没有开启访问控制,则在登录时会提示如下警告信息
    ** WARNING: Access control is not enabled for the database.
    **          Read and write access to data and configuration is unrestricted.
    

    方式二:启动时添加授权参数

    # 前台启动
    mongod --dbpath /usr/local/mongodb/data --auth
    # 后台启动
    mongod --dbpath /usr/local/mongodb/data --logpath /usr/local/mongodb/logs/mongodb.log --fork --auth
    

    3、用户认证登陆

    注:重新启动mongodb服务后,用一个新的命令窗口来做操作,在配置账户权限后,直接在命令窗口中用 mongo 命令,运行相关的操作是没有响应的,只有在正确的输入账户和密码后才进行相关操作!!

    可以在使用mongo shell登录时添加选项--authenticationDatabase或登录完后在admin数据库下进行验证。

    • 在mongo shell登录时同时进行验证:
    $ mongo 127.0.0.1:27017 -uuser_admin -p --authenticationDatabase admin
    MongoDB shell version v4.0.9
    Enter password:  # 输入密码admin
    
    connecting to: mongodb://192.168.58.2:27017/test?authSource=admin&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("94663b8d-7d88-4c97-ad1c-c3c24262ad39") }
    MongoDB server version: 4.0.9
    > 
    
    • mongo shell 登录完成之后进行验证:
    $ mongo 127.0.0.1:27017
    MongoDB shell version v4.0.9
    connecting to: mongodb://192.168.58.2:27017/test?gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("531e75df-3a5d-4f35-9e18-d7a6e090df63") }
    MongoDB server version: 4.0.9
    
    > use admin
    switched to db admin
    
    > # 登录认证,返回1,代表登录成功
    > db.auth('user_admin','admin')
    1
    
    • 连接本地MongoDB数据库(不指定IP 和端口号):mongo -u admin -p password
    • 连接远程MongoDB数据库(指定IP 和 端口号):
      • 不指定数据库名(没有输入账号登录):mongo --host 127.0.0.1 --port 27017
      • 不指定数据库名(输入账号登录):mongo --host 127.0.0.1 --port 27017 -u admin -p password
      • 指定登录数据库(输入账号登录):mongo 127.0.0.1:27017/admin -u admin -p password

    4、创建普通用户

    注:各个不同的数据库之间,可以创建有一个 或 多个账户,各数据库之间账户、密码都是独立的,不能互相访问!

    • 使用admin用户在admin数据库中创建基于角色dbOwner的用户test
    # 方式一:
    # customData 内可以设置任何描述该用户的信息
    > use admin
    > db.createUser({
            user: "test",
            pwd: "password",
            roles: [{role: "dbOwner", db: "test"}],
            customData: {info: "user for dbOwner"}
        }
    )
    
    # 方式二:
    > db.getSiblingDB("admin").runCommand({
            createUser: "test",
            pwd: "password",
            roles: [{role: "dbOwner", db: "test"}],
            customData: { info: "user for dbOwner"}
        }
    )
    { "ok" : 1 }
    
    • 登录test数据库给创建test用户,并且赋予该用户权限
    > use test
    > db.createUser({
        user: "test_admin",
        pwd: "password",
        roles: [
            {role: "read", db: "test"},
            {role: "readWrite", db: "test"}]
    })
    

    5、查看用户信息

    1、查询当前数据库的指定用户:db.getUser(‘用户名’, args)

    2、查看当前数据库的所有用户:db.getUsers()、show users

    参数说明:

    Field Type Description
    showCredentials boolean 可选的。将字段设置为 true 以显示用户的密码哈希。默认情况下,此字段为false
    showPrivileges boolean 可选的。将字段设置为 true 以显示用户的完整特权集,包括继承角色的扩展信息。默认情况下,此字段为false。如果查看所有用户,则不能指定此字段。
    showAuthenticationRestrictions boolean 可选的。将字段设置为 true 以显示用户的身份验证限制。默认情况下,此字段为false。如果查看所有用户,则不能指定此字段。
    • 查询指定用户
    # 方式一:
    > use admin
    > db.getUser("admin", { showPrivileges: true })
    
    # 方式二:
    > db.getSiblingDB("admin").runCommand({
            usersInfo: "admin",
            showPrivileges: true
        }
    )
    
    • 查询所有用户:
    > show users
    {
            "_id" : "admin.admin",
            "userId" : UUID("f4000321-33d7-43bd-b791-a42294c4a004"),
            "user" : "admin",
            "db" : "admin",
            "roles" : [
                    {
                            "role" : "root",
                            "db" : "admin"
                    }
            ],
            "mechanisms" : [
                    "SCRAM-SHA-1",
                    "SCRAM-SHA-256"
            ]
    }
    {
            "_id" : "admin.test",
            "userId" : UUID("c6788e01-a812-4bb8-8d57-0553731e27c4"),
            "user" : "test",
            "db" : "admin",
            "roles" : [
                    {
                            "role" : "dbOwner",
                            "db" : "test"
                    }
            ],
            "mechanisms" : [
                    "SCRAM-SHA-1",
                    "SCRAM-SHA-256"
            ]
    }
    

    6、更新用户信息

    updateuser()它是完全替换之前的值:

    如果要新增或添加roles而不是代替它 则使用方法: db.grantRolesToUser() 和 db.revokeRolesFromUser()

    如果只是更新密码也可以只用此方式:db.changeUserPassword()

    更新用户 test 具有 admin 数据库 readWrite 角色为read角色。

    > use admin
    > db.updateUser("test",{
            customData: { info: "user for test" },
            roles: [{role: "read", db: "test" }]
    })
    
    > use admin
    > db.runCommand({
            updateUser: "test",
            roles: [{ role: "read", db: "test" }],
            customData: { info: "user for test" }
    })
    

    7、更新用户角色

    1、db.grantRolesToUser():为用户增加角色

    2、db.revokeRolesFromUser():为用户回收角色

    • 为用户test添加admin数据库的readWrite角色。
    # 方式一:
    > use admin
    > db.grantRolesToUser("test",
        [
            {role: "readWrite", db: "admin"},
            {role: "dbOwner", db: "admin"}
        ]
    )
    
    # 方式二:
    > use admin
    > db.runCommand({
            grantRolesToUser: "test",
            roles:
                [
                    {role: "readWrite", db: "admin" },
                    {role: "dbOwner", db: "admin" }
                ]
        }
    )
    
    • 用户dbabd_user回收admin数据库的read角色。
    # 方式一:
    > use admin
    > db.revokeRolesFromUser("test", [{ role: "dbOwner", db: "test"}])
    
    # 方式二:
    > use admin
    > db.runCommand({
            revokeRolesFromUser: "test",
            roles:[{ role: "dbOwner", db: "test"}]
    })
    

    8、更改用户密码

    • db.updateUser('username',{'pwd':'newpassword'})。必须有用户管理权限才能使用。这个方式不止可以更新密码。

    • db.changeUserPassword('username','newpassword')。必须有用户管理全出现才能使用

    use admin
    db.changeUserPassword("addmin", "admin123")
    >
    use admin
    db.updateUser('addmin',{'pwd':'admin123'})
    

    9、删除用户

    use admin
    db.dropUser("test")
    >
    use admin
    db.runCommand({ dropUser: "test" })
    

    数据库操作

    1、数据库命名规则

    MongoDB 数据库的命名要符合 UTF-8 标准的字符串,同时要遵循下表所示的注意事项。

    序号 注意事项
    1 不能是空串
    2 不得含有 /、、?、$、空格、空字符等,基本只能使用 ASCII 中的字母和数字
    3 区分大小写,建议全部小写
    4 名称最多为 64 字节
    5 不得使用保留的数据库名,如:admin、local、config

    注意:数据库最终会成为文件,数据库名就是文件的名称。

    • 由于数据库名称在 MongoDB 中不区分大小写,因此数据库名称不能仅仅区别于字符。
    • 对于在 Windows 上运行的 MongoDB,数据库名称不能包含以下字符:/、、“、$、*、< >、:、|、? 。
    • 对于在 UNIX 和 Linux 系统上运行的 MongoDB,数据库名称不能包含以下字符:/、、。、"、$。
    • 虽然 UTF-8 可以提供很多国家的语言的命名格式,在 MongoDB 数据库命名时也可以使用汉字作为数据库名,但是最好尽量采用英文字母、数字、字符等为主的命名格式。

    示例:

    • 正确的命名格式:myDB、my_NewDB、myDB12

    • 错误的命名格式:.myDB、/123

    保留数据库:MongoDB 系统保留的数据库如下:

    库名 作用
    admin 权限数据库,添加用户到该数据库中,该用户会自动继承数据库的所有权限
    local 数据库中的数据永远不会被复制
    config 分片时,config 数据库在内部使用,保存分子信息
    test 默认数据库,可以用来做各种测试等

    2、创建切换数据库

    创建、打开、切换 数据库:use dbname

    use 
    > use mydb
    switched to db mydb
    

    注:

    1. 如果打开的这个数据库存在就是打开这个数据库。
    2. 如果打开的是一个不存在的数据库(没有这个数据库名字),那么就会创建一个同名的数据库。
    3. 创建一个新的数据库时,需要向数据库中创建集合并且插入一条数据,这个数据库才能创建成功!否则show dbs查询不到此数据库

    示例:往集合中插入一条数据。可以不用先创建集合,直接往里添加数据即可:

    # user 就是集合(表)名
    db.user.insert({"name": "xiaoming"})     
    

    当命令执行后,数据库系统发现 user 是一个数据集合不存的,就自动创建一个集合,并随着数据的插入,数据库和集合也就真正的创建成功了。

    要在不切换当前数据库访问其他的数据库使用 db.getSiblingDB() 方法。

    3、查看数据库

    • 查看当前所在数据库(默认为test):db
    • 查看所有数据库:show dbs
    • 查看当前数据库相关信息(名称、文档个数、视图、索引、大小等):db.stats()
    > db
    test
    > show dbs
    admin   0.000GB
    config  0.000GB
    local   0.000GB
    >
    > db.stats()                       # 统计数据信息
    {
            "db" : "test",             # 数据库名
            "collections" : 0,         # 集合数量
            "views" : 0,
            "objects" : 0,             # 文档数量
            "avgObjSize" : 0,          # 平均每个文档的大小
            "dataSize" : 0,            # 数据占用空间大小,不包括索引,单位为字节
            "storageSize" : 0,         # 分配的存储空间
            "totalSize" : 0,           # 总共空间大小
            "indexes" : 0,             # 索引个数
            "indexSize" : 0,           # 索引占用空间大小
            "scaleFactor" : 1,
            "fileSize" : 0,
            "fsUsedSize" : 0,
            "fsTotalSize" : 0,
            "ok" : 1
    }
    

    备注:为什么查询所有数据库时没有看到当前默认的test数据库呢?因为test数据库内没有数据,实际上不算真正意义上的存在。

    4、删除数据库

    删除当前所在的数据库:db.dropDatabase()

    注:删除后集合中的所以文档、以及相关的索引等都会被删除!!

    > db.dropDatabase()
    { "ok" : 1 }
    

    6、查看帮助文档

    1、数据库帮助命令

    • 导入工具所有操作方法:mongoexport --help

    • 导出工具所有操作方法:mongoimport --help

    • 备份工具所有操作方法:mongodump --help

    • 恢复工具所有操作方法:mongorestore --help

    • 列出MongoDB数据库所有的操作方法:db.help()

    • 列出MongoDB文档(表)所有的操作方法:db.collection.help()

    集合操作

    1、集合命名规则

    MongoDB 的集合就相当于 MySQL 的一个表 table,集合是一组文档,是无模式的,集合名称要求符合 UTF-8 标准的字符串,同时要遵循下表所示的注意事项。

    序号 注意事项
    1 集合名不能是空串
    2 不能含有空字符
    3 不能以“system.”开头,这是系统集合保留的前缀
    4 集合名不能含保留字符“$”

    对于分别部署在 Windows、Linux、UNIX 系统上的 MongoDB,集合的命名方式与数据库命名方式一致。

    2、集合创建

    MongoDB 集合的创建有显式和隐式(推荐)两种方法。

    2.1、显示创建集合函数db.createCollection(name, options)

    语法格式:db.createCollection(name,options)
    name:要创建的集合名称
    options:可选参数,指定有关内存大小及索引的选项
    
    • options 可以是如下参数:
    参数 类型 描述
    capped Boolean (可选)如果为 true,则启用封闭的集合。上限集合是固定大小的集合,它在达到其最大时自动覆盖其最旧的条目。如果指定 true,则还需要指定 size 参数
    autoindexid Boolean (可选)如果为 true,自动在 _id 字段创建索引。默认为false。
    size 数字 (可选)指定上限集合的最大大小(以字节为单位)。如果 capped 为 true,那么还需要指定此字段的值
    max 数字 (可选)指定上限集合中允许的最大文档数
    • 创建无参数的集合:
    db.createCollection('day1')
    
    • 创建带参数的集合:
    # 创建名为day2的固定集合,集合空间大小为:2000000kb(大约1.9g),文档最大个数为:1000
    db.createCollection('day2',{capped:true,autoIndexId:true,size:2000000,max:1000})
    
    • 示例:
    > db.createCollection('day1')
    { "ok" : 1 }
    > 
    > db.createCollection('day2',{capped:true,autoIndexId:true,size:2000000,max:1000})
    {
            "note" : "The autoIndexId option is deprecated and will be removed in a future release",
            "ok" : 1
    }
    > show collections
    day1
    day2
    students
    > 
    

    2.2、隐式创建集合(推荐):

    当插入文档时,如果集合不存在,则 MongoDB会隐式地自动创建集合:

    db.myDB.insert({"name": "tom"})
    

    3、集合查询

    MySQL 列出的所有表都可以使用 show tables。MongoDB 列出所有集合可以使用如下两种方式:

    • show tables

    • show collections

    • db.getCollectionNames()

    > db
    test
    > show tables
    dept
    student
    > show collections
    dept
    student
    > db.getCollectionNames()
    [ "dept", "student" ]
    >     
    

    4、集合删除

    集合删除(当前集合):db.collection.drop();

    > db.day1.drop()
    true
    

    5、集合修改

    集合重新命名:db.collection.renameCollection();

    > db.students.renameCollection("student")
    { "ok" : 1 }                                                                                                           
    

    文档操作

    1、文档命名规则

    文档是 MongoDB 中存储的基本单元,是一组有序的键值对集合。文档中存储的文档键的格式必须是符合 UTF-8 标准的字符串,同时要遵循以下注意事项:

    • 不能包含字符(空字符),因为这个字符表示键的结束;
    • 不能包含$.,因为.$是被保留的,只能在特定环境下使用;
    • 键名区分大小写;
    • 键的值区分类型(如字符串和整数等);
    • 键不能重复,在一条文档里起唯一的作用。

    注意:以上所有命名规范必须符合 UTF-8 标准的字符串,文档的键值对是有顺序的,相同的键值对如果有不同顺序,也是不同的文档。

    示例:

    • 例1:以下两组文档是不同的,因为值的类型不同。
    { "recommend" : "5" }
    { "recommend" : 5 }
    
    • 例2:以下两组文档也是不同的,因为键名是区分大小写的。
    { "Recommend" : " 5 "}
    { "recommend" : "5" }
    

    2、数据插入

    注:插入数据时不需要专门去创建集合(表),因为插入数据时会自动创建集合!!

    save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,已使用 insertOne()replaceOne() 来代替。

    insert():若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据.

    • db.collection.insert():在指定集合中插入一个或者多个文档
    • db.collection.insertOne():在指定集合插入一个新文档
    • db.collection.insertMany():在指定集合插入一个多个文档

    db.collection.insert()

    在平时的使用当中,db.collection.insert()是我用得最多的文档插入方式,具体的语法格式如下:

    db.collection.insert(
       <document or array of documents>,
       {
         writeConcern: <document>,
         ordered: <boolean>
       }
    )
    

    参数说明:

    • document:指定写入的文档,可以一个或多个
    • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求
    • ordered:指定是否按顺序写入(可选),默认 true
      • 当指定为true时,插入多个文档时将文档排序保存在一个数组中进行插入,如果其中有一个文档插入失败,则会导致数组中余下的文档不进行插入操作;
      • 当指定为false时,插入多个文档时将文档不进行排序保存在一个数组中进行插入,如果其中有一个文档插入失败,则不影响数组中余下的文档进行插入操作。

    如果插入的文档当中没有指定_id字段,则MongoDB会自动为文档生成具有唯一ObjectId值的字段_id

    示例:

    // insert 插入一条数据
    db.student.insert({"name": "zhangsan", "age": 28, addr: "ShenZhen"});
    
    // insert 插入一条数据,并指定_id
    db.student.insert({_id: 1, "name": "zhangsan", "age": 28, addr: "ShenZhen"});
    
    // insert 插入多条数据,不进行排序
    db.student.insert([
        {"name": "zhangsan", "age": 32, addr: "GuangZhou"},
        {"name": "wangwu", "age": 16, addr: "ShangHai"}
    ]);
    
    // insert 插入多条数据,并进行排序
    db.student.insert(
        [
            {"name": "zhangsan", "age": 32, addr: "GuangZhou"},
            {"name": "wangwu", "age": 16, addr: "ShangHai"}
        ],
        {ordered: false}
    );
    

    db.collection.insertOne() 与 db.collection.insertMany()

    语法格式如下:

    // 单行插入
    db.collection.insertOne(
        <document>,
        {
            writeConcern: <document>
        }
    )
    // 多行插入
    db.collection.insertMany(
        [ <document1> , <document2>, ... ],
        {
            writeConcern: <document>,
            ordered: <boolean>
        }
    )
    
    • 参数说明:
      • document:要写入的文档。
      • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
      • ordered:指定是否按顺序写入,默认 true,按顺序写入。

    使用示例:

    // 单行插入文档,关于_id字段指定与否也与db.collection.insert()一致
    db.student.insertOne({"name": "zhangsan", "age": 28, addr: "ShenZhen"});
    
    // 插入多条数据
    db.student.insertMany([
        {"name": "zhangsan", "age": 32, addr: "GuangZhou"},
        {"name": "wangwu", "age": 16, addr: "ShangHai"}
    ]);
    

    关于返回确认信息

    > # insert 插入一条数据
    > db.student.insert({"name": "zhangsan", "age": 28, addr: "ShenZhen"});
    WriteResult({ "nInserted" : 1 })
    >
    > # insert 插入多条数据
    > db.student.insert([
    ...     {"name": "zhangsan", "age": 32, addr: "GuangZhou"},
    ...     {"name": "wangwu", "age": 16, addr: "ShangHai"}
    ... ]);
    BulkWriteResult({
            "writeErrors" : [ ],
            "writeConcernErrors" : [ ],
            "nInserted" : 2,
            "nUpserted" : 0,
            "nMatched" : 0,
            "nModified" : 0,
            "nRemoved" : 0,
            "upserted" : [ ]
    })
    >
    > # 插入1条数据
    > db.student.insertOne({"name": "zhangsan", "age": 28, addr: "ShenZhen"});
    {
            "acknowledged" : true,
            "insertedId" : ObjectId("5fd4c7676817e7a0fa646b85")
    }
    >
    > # 插入多条数据
    > db.student.insertMany([
    ...     {"name": "zhangsan", "age": 32, addr: "GuangZhou"},
    ...     {"name": "wangwu", "age": 16, addr: "ShangHai"}
    ... ]);
    {
            "acknowledged" : true,
            "insertedIds" : [
                    ObjectId("5fd4c7706817e7a0fa646b86"),
                    ObjectId("5fd4c7706817e7a0fa646b87")
            ]
    }
    >
    

    3、数据查询

    查询(Read Operations)读操作,是对集合中已存在的文档进行查询,即对应关系型数据库当中的select操作,MongoDB提供以下几种主要查询文档方法:

    • db.collection.find():查询指定集合中满足条件的一个或多个文档和视图;
    • db.collection.findOne():查询指定集合中满足条件的第一个文档
    • *.pretty():以格式化方式展现,通过pretty()方法

    插入测试数据

    db.user.insert([
        { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" },
        { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "GuangZhou" },
        { "_id" : 3, "name" : "LaoWan", "age" : 30, "addr" : "BeiJing" },
        { "_id" : 4, "name" : "WangWu", "age" : 32, "addr" : "ShangHai" },
        { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "ShenZhen" },
        { "_id" : 6, "name" : "LiSi", "age" : 20, "addr" : "HangZhou" }
    ])
    

    此时 user 表中的数据列表如下:

    id name age addr
    1 ZhangSan 25 ShenZhen
    2 XiaoMing 27 GuangZhou
    3 LaoWan 30 BeiJing
    4 WangWu 32 ShangHai
    5 ZhaoSi 28 ShenZhen
    6 LiSi 20 HangZhou

    db.collection.find() 与 db.collection.findOne()

    db.collection.find() :是使用频率最高的方法了,可以用来查询数据库集合当中的文档

    db.collection.findOne():方法显示符合条件查询的第一条文档

    语法格式如下:

    db.collection.find(<query>, <projection>)
    db.collection.findOne(<query>, <projection>)
    
    • query:查询表达式;
    • projection:指定查询结果集中需要显示的字段。
      • Col_name:1|true 代表显示该字段;
      • Col_name:0 | false 代表不显示该字段。

    _id 字段是默认显示的,如果不想显示,则显式指定 {"_id" : 0}

    使用示例:

    // 查询全部数据的两种方式
    db.collection.find()
    db.collection.find({})
    // 查询单个文档:
    db.collection.findOne()
    
    //============================查询所有文档数据============================
    > db.user.find()
    { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }
    { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "GuangZhou" }
    { "_id" : 3, "name" : "LaoWan", "age" : 30, "addr" : "BeiJing" }
    { "_id" : 4, "name" : "WangWu", "age" : 32, "addr" : "ShangHai" }
    { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "ShenZhen" }
    { "_id" : 6, "name" : "LiSi", "age" : 20, "addr" : "HangZhou" }
    
    
    //============================查询单个文档数据============================
    > db.user.findOne({addr: "ShenZhen"})
    { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }
    

    指定返回字段

    db.getCollection('user').find(用于过滤记录的字典,用于限定字段的字典)
    
    • 如果值为0,则表示在全部字段中剔除值为0的这些字段并返回。
    • 如果值为1,则表示只返回值为1的这些字段。(默认都为1,显示)

    使用示例:

    // 只显示name字段和age字段
    db.user.find({},{ name : 1 , age : 1 }).limit(1)
    { "_id" : 1, "name" : "ZhangSan", "age" : 25 }
    
    // 不显示_id字段和name字段,其他字段都显示
    db.user.find({},{ _id : 0 , name : 0 }).limit(1)
    { "age" : 25, "addr" : "ShenZhen" }
    

    注意:不能0(显示字段)和1(剔除字段)在同一个语句中一起使用,不然会报错

    范围操作符

    名称 操作 格式 范例 RDBMS中的类似语句
    $eq 等于 {:} db.col.find({"by":"Mongo教程"}) where by = 'Mongo教程'
    $lt 小于 {:{$lt:}} db.col.find({"likes":{$lt:50}}) where likes < 50
    $te 小于或等于 {:{$lte:}} db.col.find({"likes":{$lte:50}}) where likes <= 50
    $gt 大于 {:{$gt:}} db.col.find({"likes":{$gt:50}}) where likes > 50
    $gte 大于或等于 {:{$gte:}} db.col.find({"likes":{$gte:50}}) where likes >= 50
    $ne 不等于 {:{$ne:}} db.col.find({"likes":{$ne:50}}) where likes != 50
    $in 指定值在数组中 {:{$in:[]}} db.col.find({"likes":{$in:[50]}}) where likes in (50)
    $nin 指定值不在数组中 {:{$nin:[]}} db.col.find({"likes":{$nin:[50]}}) where likes not in (50)

    使用示例:

    // $eq:等值查询 SQL: SELECT * FROM user WHERE name = "XiaoHei";
    db.user.find( { name: "ZhangSan" } )
    
    // $ne:等值查询 SQL: SELECT * FROM user WHERE name != "XiaoHei";
    db.user.find( { name: {$ne: "XiaoHei"} } )
    
    // $gt:范围查询(以大于为例) SQL: SELECT * FROM user WHERE age > 30;
    db.user.find( { age: {$gt: 30} } )
    
    // $gte、$lt、$lte 同$gt
    
    // $in:或查询,可使用or代替 SQL: SELECT * FROM user WHERE addr in ("ShenZhen", "GuangZhou")
    db.user.find( { addr: { $in: [ "ShenZhen", "GuangZhou" ] } } )
    
    // $nin:SQL: SELECT * FROM user addr not in ("BeiJing")
    db.user.find( { addr: { $nin: [ "BeiJing"] } } )
    

    逻辑操作符

    名称 说明
    $and 指定查询同时满足多个条件查询子句(默认)
    $not 指定查询不满足条件查询子句
    $or 指定查询满足其中某个条件查询子句
    $nor 指定查询无法满足多个条件查询子句

    使用实例:

    // $and: 逻辑与查询 SQL: SELECT * FROM user WHERE addr = "ShenZhen" AND age < 30;
    db.user.find( { $and: [ { addr: { $eq: "ShenZhen" },  age: { $lt: 30 } } ] } )
    // $and: 或者(一般这种形式更加常用,$and 和 $eq 默认可以省略)
    db.user.find( { addr: "ShenZhen",  age: { $lt: 30 } } )
    
    // $not: 不符合查询 SQL: SELECT * FROM user WHERE addr <> "GuangZhou";
    db.user.find( { addr: { $not: { $eq: "GuangZhou" } } } )
    
    // $or: 逻辑或查询 SQL: SELECT * FROM user WHERE addr = "GuangZhou" OR age < 30;
    db.user.find( { $or: [ { addr: "GuangZhou" }, { age: { $lt: 30 } } ] } )
    
    /*
    $nor: 无法同时满足多个条件查询,字段不存在时也符合 
    SQL: SELECT * FROM user WHERE addr <> "GuangZhou" AND age > 30; 
    符合以下条件之一都会出现在结果集中:
    1.文档包含addr和age字段并且符合条件;
    2.文档包含addr字段并且符合条件,不包含age字段;
    3.文档不包含addr字段,包含age字段并且符合条件;
    4.文档不包含addr字段和age字段。
    */
    db.user.find( { $nor: [ { addr: { $ne: "GuangZhou" } },  { age: { $gt: 30 } } ] } )
    

    元素操作符

    名称 说明
    $exists 指定查询文档是否有对应的字段
    $type 指定查询文档的某个字段是否是对应类型

    使用示例:

    // $exists: 是否存在指定字段查询,返回所有存在该字段的数据
    db.user.find( { name: { $exists: true } } )
    
    // $type: 字段是否是指定类型查询,返回所有age字段为double的数据
    db.user.find( { "age": { $type: "double" } } )
    

    评估操作符

    参考官方文档:https://docs.mongodb.com/manual/reference/operator/query-evaluation/

    名称 说明
    $expr 为同一个文档中的字段指定表达式并且符合条件的查询,比如比较同一文档当中两个字段的值
    $mod 为字段值取模并且符合条件的查询

    为了更好的使用这两个主要的操作符,额外创建个文档:

    db.monthlyBudget.insertMany([
        { "_id" : 1, "category" : "food", "budget": 400, "spent": 450 },
        { "_id" : 2, "category" : "drinks", "budget": 100, "spent": 150 },
        { "_id" : 3, "category" : "clothes", "budget": 100, "spent": 50 },
        { "_id" : 4, "category" : "misc", "budget": 500, "spent": 300 },
        { "_id" : 5, "category" : "travel", "budget": 200, "spent": 650 }
    ]);
    

    使用示例:

    // $expr: 允许使用聚合表达式,这里以$gt为例,更多表达式参考:
    // https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#aggregation-expressions
    db.monthlyBudget.find( { $expr: { $gt: [ "$spent" , "$budget" ] } } )
    
    // $mod: 对字段所在值进行取模运算,显示符合条件的查询,如qty字段值对4取模,并且余数为0
    db.inventory.find( { qty: { $mod: [ 4, 0 ] } } )
    

    查询结果修饰

    db.collection.find().count()
    db.collection.find().limit(number)
    db.collection.find().skip(number)
    db.collection.find().sort({key, 1或-1})
    db.collection.find().distinct()
    

    (1)查询当前集合数据量:db.collection.find().count()

    (2)查询集合前几条数据:db.collection.find().limit(number)

    (3)限定跳过前几条结果:db.collection.find().skip(number)

    (4)对查询结果进行排序:db.collection.find().sort(key, 1或-1)

    (5)对查询结果进行去重:db.collection.distinct()

    使用示例:

    // 相当于select count(1) from user
    > db.user.find().count()
    6
    
    // 查询前 1 条数据
    // 相当于:SELECT * FROM user limit 10
    > db.user.find().limit(1)
    { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }
    
    // 查询第 5 条以后的数据
    > db.user.find().skip(5)
    { "_id" : 6, "name" : "LiSi", "age" : 20, "addr" : "HangZhou" }
    
    /*
    * 查询 10 到 20 之间的数据
    * 列表分页
    * limit:就是 pageSize
    * skip :就是第几页 * pageSize
    */
    > db.user.find().limit(20).skip(10);
    
    // 查询某个结果集的记录条数(统计数量)
    // 相当于:select count(*) from user where age >= 18;
    db.user.find({age: {$gte: 18}}).count();
    
    // 如果要返回限制之后的记录数量,要使用 count(true)或者 count(非 0)
    db.user.find().limit(20).skip(10).count(true);
    
    // 1为升序,-1为降序
    > db.user.find().sort({"age": -1});
    { "_id" : 4, "name" : "WangWu", "age" : 32, "addr" : "ShangHai" }
    { "_id" : 3, "name" : "LaoWan", "age" : 30, "addr" : "BeiJing" }
    { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "ShenZhen" }
    { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "GuangZhou" }
    { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }
    { "_id" : 6, "name" : "LiSi", "age" : 20, "addr" : "HangZhou" }
    
    > db.user.distinct('age', { 'age':{'$gte':25 } } )
    [ 25, 27, 28, 30, 32 ]
    

    4、聚合查询

    聚合查询

    MongoDB聚合框架(Aggregation Framework)是一个计算框架,它可以:

    1. 可作用在一个 或 几个集合上
    2. 对集合中的数据进行一系列的运算
    3. 可将数据转化为所期望数据形式,如(数学计算,统计,类型,格式处理等)

    对效果而言,聚合查询相录于传统SQL查询中的,ORDER BY,GROUP BY,LIMIT,LEFT OUTER JOIN,AS等!

    • 聚合查询对照:(MongoDB 与 传统数据库 比对)
    步骤 作用 SQL等价运算符
    $match 过滤 where / having
    $group 分组 group by
    $project 投影(别名) as
    $limit 结果限制 limit
    $skip 结果限制 skip
    $sort 排序 order by
    $lookup 左外连接(多表操作) left outer join
    $graphLookup 图搜索 N/A
    $facet 分面搜索 N/A
    $bucket 分面搜索 N/A
    $unwind 展开数组 N/A

    Aggregate简介

    db.collection.aggregate() 是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。

    通过这张图,可以了解Aggregate处理的过程:

    20201212120353

    db.user.aggregate([
        { $match : { age : 20 } },                             ==》$match stage
        { $group: { _id:  '$addr', total: { $sum: '$age' } } } ==》$group stage
    ])
    ====================================================================
    { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }
    { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "ShenZhen" }
    { "_id" : 3, "name" : "LaoWan", "age" : 30, "addr" : "BeiJing" }
    { "_id" : 4, "name" : "WangWu", "age" : 32, "addr" : "ShangHai" }
    { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "GuangZhou" }
    { "_id" : 6, "name" : "LiSi", "age" : 30, "addr" : "HangZhou" }
    { "_id" : 7, "name" : "KaiFa", "age" : 20, "addr" : "HangZhou" }
    { "_id" : 8, "name" : "QianDuan", "age" : 40, "addr" : "HangZhou" }
    { "_id" : 9, "name" : "YunWei", "age" : 20, "addr" : "GuangZhou" }
    { "_id" : 10, "name" : "CeSi", "age" : 20, "addr" : "GuangZhou" }
    ===================================================================
                           ⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇
                               $match
                           ⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇
    ====================================================================  
    { "_id" : 7, "name" : "KaiFa", "age" : 20, "addr" : "HangZhou" }
    { "_id" : 9, "name" : "YunWei", "age" : 20, "addr" : "GuangZhou" }
    { "_id" : 10, "name" : "CeSi", "age" : 20, "addr" : "GuangZhou" }
    ====================================================================
                           ⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇
                               $group
                           ⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇
    ===========================Results==================================
    { "_id" : "HangZhou", "total" : 20 }
    { "_id" : "GuangZhou", "total" : 40 }
    ===========================Results==================================
    

    1、db.collection.aggregate() 可以用多个构件创建一个管道,对于一连串的文档进行处理。这些构件包括:筛选操作的match、映射操作的project、分组操作的group、排序操作的sort、限制操作的limit、和跳过操作的skip。
    2、db.collection.aggregate()使用了MongoDB内置的原生操作,聚合效率非常高,支持类似于SQL Group By操作的功能,而不再需要用户编写自定义的JavaScript例程。
    3、 每个阶段管道限制为100MB的内存。如果一个节点管道超过这个极限,MongoDB将产生一个错误。为了能够在处理大型数据集,可以设置allowDiskUse为true来在聚合管道节点把数据写入临时文件。这样就可以解决100MB的内存的限制。
    4、db.collection.aggregate()可以作用在分片集合,但结果不能输在分片集合,MapReduce可以 作用在分片集合,结果也可以输在分片集合。
    5、db.collection.aggregate()方法可以返回一个指针(cursor),数据放在内存中,直接操作。跟Mongo shell 一样指针操作。
    6、db.collection.aggregate()输出的结果只能保存在一个文档中,BSON Document大小限制为16M。可以通过返回指针解决,版本2.6中后面:DB.collect.aggregate()方法返回一个指针,可以返回任何结果集的大小。

    Aggregate语法

    db.collection.aggregate(pipeline, options)
    

    参数说明:

    参数 类型 描述
    pipeline array 一系列数据聚合操作或阶段。详见聚合管道操作符 在版本2.6中更改:该方法仍然可以将流水线阶段作为单独的参数接受,而不是作为数组中的元素;但是,如果不将管道指定为数组,则不能指定options参数
    options document 可选。 aggregate()传递给聚合命令的其他选项。 2.6版中的新增功能:仅当将管道指定为数组时才可用。

    注意:使用db.collection.aggregate()直接查询会提示错误,但是传一个空数组如db.collection.aggregate([])则不会报错,且会和find一样返回所有文档。

    pipeline有很多stage,但这里我只记录我经常用到的几个,如果后续用到再补充。stage详见官网

    接下来介绍这几个常用的stage

    aggregate常用pipeline stage介绍

    准备测试数据:

    db.getCollection("user").drop()
    
    db.getCollection("user").insert([
        { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" },
        { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "ShenZhen" },
        { "_id" : 3, "name" : "LaoWan", "age" : 30, "addr" : "BeiJing" },
        { "_id" : 4, "name" : "WangWu", "age" : 32, "addr" : "ShangHai" },
        { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "GuangZhou" },
        { "_id" : 6, "name" : "LiSi", "age" : 30, "addr" : "HangZhou" },
        { "_id" : 7, "name" : "KaiFa", "age" : 20, "addr" : "HangZhou" },
        { "_id" : 8, "name" : "QianDuan", "age" : 40, "addr" : "HangZhou" },
        { "_id" : 9, "name" : "YunWei", "age" : 20, "addr" : "GuangZhou" },
        { "_id" : 10, "name" : "CeSi", "age" : 20, "addr" : "GuangZhou" }
    ])
    

    操作示例:$match、$count、$limit、$skip、$sort、$project

    // 执行:
    // 1)$match 阶段排除age小于等于80的文档,将大于80的文档传到下个阶段
    // 2)$count阶段返回聚合管道中剩余文档的计数,并将该值分配给名为age_count的字段。
    > db.getCollection("user").aggregate([
        { $match : { age : { $gt: 25} } },
        { $count : "age_count" }
    ])
    { "age_count" : 6 }
    
    
    // 1.$skip和$limit 查询6-10条数据
    // 2.首先按照age降序排序,然后按照_id降序排序
    // 3.给name、age、addr 字段取别名
    > db.getCollection("user").aggregate([
        { $skip : 5 },
        { $limit : 9 },
        { $sort : { age : -1 , _id : -1}},
        { $project : {
            '姓名': '$name',
            '年龄': '$age',
            '地址': '$addr'
        }} 
    ])
    { "_id" : 8, "姓名" : "QianDuan", "年龄" : 40, "地址" : "HangZhou" }
    { "_id" : 6, "姓名" : "LiSi", "年龄" : 30, "地址" : "HangZhou" }
    { "_id" : 10, "姓名" : "CeSi", "年龄" : 20, "地址" : "GuangZhou" }
    { "_id" : 9, "姓名" : "YunWei", "年龄" : 20, "地址" : "GuangZhou" }
    { "_id" : 7, "姓名" : "KaiFa", "年龄" : 20, "地址" : "HangZhou" }
    

    聚合分组($group)的表达式:

    语法:

    { 
        $group: 
        { 
            _id: <expression>, 
            <field1>: { 
                <accumulator1> : <expression1> 
            },
            <field2>: { 
                <accumulator2> : <expression2> 
            },
            ... 
        } 
    }
    
    • _id字段是必填的,但是可以指定_id值为null来为整个输入文档计算累计值。
    • 剩余的计算字段是可选的,并使用<accumulator>运算符进行计算。
    • _id和<accumulator>表达式可以接受任何有效的表达式

    示例:

    [
        {
            $group: {
                _id: {
                    addr: '$addr'
                },
                totalCount: {
                    $sum: 1
                }
            }
        }
    ]
    
    • $group是固定的,表示这里一个分组聚合操作。
    • _id表示需要根据哪一些列进行聚合,其实一个JSON对象,其key/value对分别表示结果列的别名以及需要聚合的的数据库列。
    • totaoCount表示聚合列的列名。
    • $sum表示要进行的聚合操作,后面的1表示每次加1。

    accumulator操作符

    表达式 描述 实例
    $sum 计算总和。 db.col.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
    $avg 计算平均值 db.col.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
    $min 获取集合中所有文档对应值得最小值。 db.col.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
    $max 获取集合中所有文档对应值得最大值。 db.col.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
    $push 在结果文档中插入值到一个数组中。 db.col.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
    $addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.col.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
    $first 根据资源文档的排序获取第一个文档数据。 db.col.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
    $last 根据资源文档的排序获取最后一个文档数据 db.col.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])

    使用示例:$sum、$avg、$max、$min、$first、$last

    // 地址分组统计:个数、总和、平均数、最大、最小、首个、最后一个
    db.getCollection("user").aggregate([{
        $group: {
            _id: { userAddr: '$addr' },
            totalCount: { $sum: 1 },
            ageSum: { $sum: '$age' },
            ageAvg: { $avg: '$age' },
            ageMax: { $max: '$age' },
            ageMin: { $min: '$age' },
            ageFirst: { $first: '$age' },
            ageLast: { $last: '$age' }
        }
    }])
    // 返回结果
    { "_id" : { "userAddr" : "HangZhou" }, "totalCount" : 3, "ageSum" : 90, "ageAvg" : 30, "ageMax" : 40, "ageMin" : 20, "ageFirst" : 30, "ageLast" : 40 }
    { "_id" : { "userAddr" : "BeiJing" }, "totalCount" : 1, "ageSum" : 30, "ageAvg" : 30, "ageMax" : 30, "ageMin" : 30, "ageFirst" : 30, "ageLast" : 30 }
    { "_id" : { "userAddr" : "ShenZhen" }, "totalCount" : 2, "ageSum" : 52, "ageAvg" : 26, "ageMax" : 27, "ageMin" : 25, "ageFirst" : 25, "ageLast" : 27 }
    { "_id" : { "userAddr" : "ShangHai" }, "totalCount" : 1, "ageSum" : 32, "ageAvg" : 32, "ageMax" : 32, "ageMin" : 32, "ageFirst" : 32, "ageLast" : 32 }
    { "_id" : { "userAddr" : "GuangZhou" }, "totalCount" : 3, "ageSum" : 68, "ageAvg" : 22.666666666666668, "ageMax" : 28, "ageMin" : 20, "ageFirst" : 28, "ageLast" : 20 }
    >    
    
    // 指定组_id为null,计算集合中所有文档的总价格和平均数量以及计数:
    db.getCollection("user").aggregate([{
        $group: {
            _id: null,
            totalCount: { $sum: 1 },
            ageSum: { $sum: '$age' },
            ageAvg: { $avg: '$age' },
            ageMax: { $max: '$age' },
            ageMin: { $min: '$age' },
            ageFirst: { $first: '$age' },
            ageLast: { $last: '$age' }
        }
    }])
    // 返回结果
    { "_id" : null, "totalCount" : 10, "ageSum" : 272, "ageAvg" : 27.2, "ageMax" : 40, "ageMin" : 20, "ageFirst" : 25, "ageLast" : 20 }
    

    数据转换:将集合中的数据按addr分组,然后把所有age转换成数组,ages可以自定义,是分组后的列表

    使用示例:$push

    db.getCollection("user").aggregate([{
        $group: {
            _id: { userAddr: '$addr' },
            totalCount: { $sum: 1 },
            ages: { $push: '$age' }
        }
    }])
    // 返回结果
    { "_id" : { "userAddr" : "HangZhou" }, "totalCount" : 3, "ages" : [ 30, 20, 40 ] }
    { "_id" : { "userAddr" : "BeiJing" }, "totalCount" : 1, "ages" : [ 30 ] }
    { "_id" : { "userAddr" : "ShenZhen" }, "totalCount" : 2, "ages" : [ 25, 27 ] }
    { "_id" : { "userAddr" : "ShangHai" }, "totalCount" : 1, "ages" : [ 32 ] }
    { "_id" : { "userAddr" : "GuangZhou" }, "totalCount" : 3, "ages" : [ 28, 20, 20 ] }
    
    
    
    // 用系统变量$$ROOT按addr对文档进行分组,生成的文档不得超过BSON文档大小限制。
    // $$ROOT代表文档本身或者说文档所有字段
    db.getCollection("user").aggregate([{
        $group: {
            _id: { userAddr: '$addr' },
            users: { $push: '$$ROOT' }
        }
    }])
    // 返回结果
    { "_id" : { "userAddr" : "HangZhou" }, "users" : [ { "_id" : 6, "name" : "LiSi", "age" : 30, "addr" : "HangZhou" }, { "_id" : 7, "name" : "KaiFa", "age" : 20, "addr" : "HangZhou" }, { "_id" : 8, "name" : "QianDuan", "age" : 40, "addr" : "HangZhou" } ] }
    { "_id" : { "userAddr" : "BeiJing" }, "users" : [ { "_id" : 3, "name" : "LaoWan", "age" : 30, "addr" : "BeiJing" } ] }
    { "_id" : { "userAddr" : "ShenZhen" }, "users" : [ { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }, { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "ShenZhen" } ] }
    { "_id" : { "userAddr" : "ShangHai" }, "users" : [ { "_id" : 4, "name" : "WangWu", "age" : 32, "addr" : "ShangHai" } ] }
    { "_id" : { "userAddr" : "GuangZhou" }, "users" : [ { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "GuangZhou" }, { "_id" : 9, "name" : "YunWei", "age" : 20, "addr" : "GuangZhou" }, { "_id" : 10, "name" : "CeSi", "age" : 20, "addr" : "GuangZhou" } ] }
    

    $match + $group

    使用示例:

    // 单独使用 $match
    db.getCollection("user").aggregate([
        { $match : { age : { $gt: 20, $lt: 30}   } }
    ])
    { "_id" : 1, "name" : "ZhangSan", "age" : 25, "addr" : "ShenZhen" }
    { "_id" : 2, "name" : "XiaoMing", "age" : 27, "addr" : "ShenZhen" }
    { "_id" : 5, "name" : "ZhaoSi", "age" : 28, "addr" : "GuangZhou" }
    
    
    // 使用 $match + $group
    db.getCollection("user").aggregate([
        { $match : { age : { $gt: 20, $lt: 30}   } },
    		{
            $group: {
                _id: { userAddr: '$addr' },
                totalCount: { $sum: 1 }
            }
        }
    ])
    { "_id" : { "userAddr" : "ShenZhen" }, "totalCount" : 2 }
    { "_id" : { "userAddr" : "GuangZhou" }, "totalCount" : 1 }
    

    注意:$match 必须使用在 $group 之前

    4、数据更新

    • db.collection.update():更新或替换集合中符合条件的一个或多个文档;
    • db.collection.updateOne():只更新集合中符合条件的第一个文档,即使有多个文档(版本3.2新增)
    • db.collection.updateMany():更新集合中所有符合条件的文档(版本3.2新增)
    • db.collection.replaceOne():替换除 _id 字段以外的文档的所有内容

    db.collection.update()

    根据update指定的表达式可以修改文档中符合条件的字段或代替整个文档。具体的语法格式如下:

    db.collection.update(
       <query>,   //查询表达式
       <update>,  //更新表达式
       {
         upsert: <boolean>,
         multi: <boolean>,
         writeConcern: <document>,
         collation: <document>,
         arrayFilters: [ <filterdocument1>, ... ],
         hint:  <document|string>                   // 版本4.2新增
       }
    )
    

    参数说明:

    • query:更新文档的查询表达式;如果指定了参数upsert: true并且集合中没有符合查询条件的文档,查询条件中有关于字段_id指定了.分隔符的,并不会插入新的文档;

    • update:主要包含三种格式

      • 1.更新文档:只包含更新操作符表达式;
      • 2.替换文档:只包含<field1>: <value1>对;
      • 3.聚合管道:版本4.2新增,详细参考官方文档。
    • upsert:当query查询条件没符合更新的文档,就新创建文档(可选),默认值为false

    • multi:是否更新多个符合条件的文档(可选),默认值为false,只更新符合条件的第一个文档;

    • writeConcern:参考db.collection.insert()相同参数说明;

    • collation:指定校对规则(可选,版本3.4新增);

    • arrayFilters:文档数组更新过滤操作符(可选,版本3.6新增);

      详细参考:https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-array-update-operations

    • hint:采用文档或字符串的形式指定适用于查询表达式的索引,如果索引不存在则报错(可选,版本4.2新增)。

    使用示例:

    使用示例将通过使用两种场景进行,一是没有使用参数选项upsert,二是使用参数选项upsert

    • 不使用选项upsert
    // 测试数据
    db.books.remove({});
    
    db.books.insertMany([
      {
        "_id" : 1,
        "item" : "TBD",
        "stock" : 0,
        "info" : { "publisher" : "1111", "pages" : 430 },
        "tags" : [ "technology", "computer" ],
        "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "lmn", "rating" : 5 } ],
        "reorder" : false
       },
       {
        "_id" : 2,
        "item" : "XYZ123",
        "stock" : 15,
        "info" : { "publisher" : "5555", "pages" : 150 },
        "tags" : [ ],
        "ratings" : [ { "by" : "xyz", "rating" : 5 } ],
        "reorder" : false
       }
    ]);
    
    
    /* 使用选项参数 upsert: true
    1、如果查询表达式找到匹配的文档,则执行更新操作;
    2、如果查询表达式没有找到匹配的文档,则执行插入操作;
    */
    db.books.update(
       { item: "ZZZ135" },   // 查询表达式
       {                     // 更新或替换文档
         item: "ZZZ135",
         stock: 5,
         tags: [ "database" ]
       },
       { upsert: true }
    );
    
    // 1.使用更新操作表达式
    /* $set操作符
    1、查询表达式指定需要更新的文档 _id;
    2、$inc操作符: stock的字段值+5;
    3、$set操作符: 替换item字段值,替换嵌入文档info的publisher字段值,替换tags字段值,替换数组ratings的第二个元素值
    */
    db.books.update(
       { _id: 1 },
       {
         $inc: { stock: 5 },
         $set: {
           item: "ABC123",
           "info.publisher": "2222",
           tags: [ "software" ],
           "ratings.1": { by: "xyz", rating: 3 }
         }
       }
    );
    更新之后的文档:
    {
    
      "_id" : 1,
      "item" : "ABC123",
      "stock" : 5,
      "info" : { "publisher" : "2222", "pages" : 430 },
      "tags" : [ "software" ],
      "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "xyz", "rating" : 3 } ],
      "reorder" : false
    }
    
    // 2.为已存在的数组添加元素
    // $push操作符: 为指定文档数组ratings添加一个元素
    db.books.update(
       { _id: 2 },
       {
         $push: { ratings: { "by" : "jkl", "rating" : 2 } }
       }
    );
    更新之后的文档:
    {
      "_id" : 2,
      "item" : "XYZ123",
      "stock" : 15,
      "info" : {
       "publisher" : "5555",
       "pages" : 150
      },
      "tags" : [ ],
      "ratings" : [
       { "by" : "xyz", "rating" : 5 },
    
       { "by" : "jkl", "rating" : 2 }
    
      ],
      "reorder" : false
     }
    
    // 3.文档移除字段
    // $unset操作符: 移除文档的指定字段,为_id:1文档移除tags字段
    db.books.update( { _id: 1 }, { $unset: { tags: 1 } } );
    更新后的文档:
    {
      "_id" : 1,
      "item" : "TBD",
      "stock" : 0,
      "info" : {
       "publisher" : "1111",
       "pages" : 430
      },
      "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "lmn", "rating" : 5 } ],
      "reorder" : false
     }
    
    // 4.替换整个文档
    // 替换_id:2的文档
    db.books.update(
       { _id: 2 },
       {
         item: "XYZ123",
         stock: 10,
         info: { publisher: "2255", pages: 150 },
         tags: [ "baking", "cooking" ]
       }
    );
    更新后的文档:
    {
       "_id" : 2,
       "item" : "XYZ123",
       "stock" : 10,
       "info" : { "publisher" : "2255", "pages" : 150 },
       "tags" : [ "baking", "cooking" ]
    }
    
    // 5.更新多个文档
    db.books.update(
       { stock: { $lte: 10 } },
       { $set: { reorder: true } },
       { multi: true }
    );
    更新后的全部文档:
    [
      {
        "_id" : 1,
        "item" : "ABC123",
        "stock" : 5,
        "info" : {
         "publisher" : "2222",
         "pages" : 430
        },
        "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "xyz", "rating" : 3 } ],
    
        "reorder" : true
    
       }
       {
         "_id" : 2,
         "item" : "XYZ123",
         "stock" : 10,
         "info" : { "publisher" : "2255", "pages" : 150 },
         "tags" : [ "baking", "cooking" ],
    
         "reorder" : true
    
       }
    ]
    
    • 使用upserts选项
    /* 使用选项参数 upsert: true
    1、如果查询表达式找到匹配的文档,则执行更新操作;
    2、如果查询表达式没有找到匹配的文档,则执行插入操作;
    */
    
    // 1.插入未符合更新条件的文档
    db.books.update(
       { item: "ZZZ135" },   
       {                     
         item: "ZZZ135",
         stock: 5,
         tags: [ "database" ]
       },
    
       { upsert: true }      
    
    );
    因为集合并未满足条件的文档,则插入的文档为:
    {
      "_id" : ObjectId("5da78973835b2f1c75347a83"),
      "item" : "ZZZ135",
      "stock" : 5,
      "tags" : [ "database" ]
    }
    
    // 2.插入未符合更新条件并且基于更新操作符的文档
    // 如果没有符合更新查询条件,并且使用的是更新操作符,则会基于当前的查询条件和更新操作符字段插入新的文档
    db.books.update(
       { item: "BLP921" },   
       {                     
          $set: { reorder: false },
          $setOnInsert: { stock: 10 }
       },
       { upsert: true }      
    );
    新插入的文档为:
    {
      "_id" : ObjectId("5da79019835b2f1c75348a0a"),
      "item" : "BLP921",
      "reorder" : false,
      "stock" : 10
    }
    
    // 3.插入未符合更新条件并且基于聚合管道的文档
    // 关于聚合管道请参考官方文档:https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline
    
    // 4.插入未符合更新条件并且同时联合多文档操作符的文档
    如果不符合查询条件,则只会插入单个文档
    db.books.update(
      { "info.publisher": "Self-Published" },   
      {                                         
        $set: { reorder: false, tags: [ "literature", "hardcover" ], stock: 25 }
      },
      { upsert: true, multi: true }             
    );
    新插入的文档:
    {
      "_id" : ObjectId("5db337934f670d584b6ca8e0"),
      "info" : { "publisher" : "Self-Published" },
      "reorder" : false,
      "stock" : 25,
      "tags" : [ "literature", "hardcover" ]
    }
    

    db.collection.updateOne() 与 db.collection.updateMany()

    根据update指定的参数可以修改文档中符合条件的字段或代替整个文档,与db.collection.update()不同的是每次只更新单个文档。

    根据update指定的参数可以修改文档中符合条件的字段或代替整个文档,与db.collection.updateOne()不同的是更新所有符合条件的文档。

    语法格式如下:

    // 更新单个文档语法:
    db.collection.updateOne(
       <filter>,
       <update>,
       {
         upsert: <boolean>,
         writeConcern: <document>,
         collation: <document>,
         arrayFilters: [ <filterdocument1>, ... ],
         hint:  <document|string>        
       }
    )
    
    // 更新单个或多个文档语法:
    db.collection.updateMany(
       <filter>,
       <update>,
       {
         upsert: <boolean>,
         writeConcern: <document>,
         collation: <document>,
         arrayFilters: [ <filterdocument1>, ... ],
         hint:  <document|string>        
       }
    )
    

    参数说明:参考db.collection.update()的参数说明。

    使用示例:

    // 参考db.collection.update()
    

    db.collection.replaceOne( filter , replacement , options )

    db.collection.replaceOne(
       <filter>,
       <replacement>,
       {
         upsert: <boolean>,
         writeConcern: <document>,
         collation: <document>
       }
    )
    

    参数说明:参考db.collection.update()的参数说明。

    使用示例:

    // 使用一个新的文档来替换name为B的文档
    db.user.replaceOne(
        { name: "B" },
        { name: "newB", age: 8, addr: "GuangZhou"}
    )
    

    5、数据删除

    • db.collection.deleteOne():只删除集合中符合条件的一个文档
    • db.collection.deleteMany():删除集合中所有符合条件的文档
    • db.collection.remove():删除集合中符合条件的一个或多个文档(已废弃

    db.collection.deleteOne() 与 db.collection.deleteMany()

    根据filter选项条件删除集合中的单个或多个文档,具体语法格式如下:

    // 删除单个语法
    db.collection.deleteOne(
       <filter>,
       {
          writeConcern: <document>,
          collation: <document>
       }
    )
    // 删除单个或者多个语法
    db.collection.deleteMany(
       <filter>,
       {
          writeConcern: <document>,
          collation: <document>
       }
    )
    

    参数说明:

    • filter:指定基于查询表达式的过滤条件,关于查询表达式可以查看db.collecion.find()中的<query>
    • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
    • collation:指定校对规则(可选,版本3.4新增);

    使用示例:

    // 删除指定条件的单个文档
    db.user.deleteOne( { "_id" : 1 } );
    { "acknowledged" : true, "deletedCount" : 1 }
    
    // 删除指定条件的多个文档
    db.user.deleteMany( {"addr" : "GuangZhou"} );
    { "acknowledged" : true, "deletedCount" : 2 }
    

    db.collection.remove()

    根据filter选项条件删除集合中的单个或多个文档,具体语法格式如下:

    db.collection.remove(
        <query>,
        {
            justOne: <boolean>, 
            writeConcern: <document>
        }
    )
    

    参数说明:

    • query:必选项,是设置删除的文档的条件。
    • justOne:布尔型的可选项,默认为false,删除符合条件的所有文档,如果设为 true,则只删除一个文档。
    • writeConcem:可选项,设置抛出异常的级别。

    使用示例:

    // remove 删除全部
    db.day.remove({})
    
    // remove 条件删除
    db.day.remove({age:10})
    

    注意

    此方法已经过时,官方推荐使用deleteOne和deleteMany函数来实现删除操作。且remove()函数并不会真正的释放掉存储空间,需要删除后,再用管理员权限的账户执行db.repairDatabase()函数来释放存储空间!

    索引操作

    1、创建索引

    • db.collection.createIndex(keys, options):创建集合索引

    参数说明:

    keys:对象参数为1表示正序建立索引,-1表示逆序建立索引。如果添加多个属性为联合索引

    options:可选属性如下:

    Parameter Type Description
    background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false
    unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
    name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
    dropDups Boolean 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
    sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
    expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
    v index version 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
    weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
    default_language string 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
    language_override string 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.

    使用示例:

    // 唯一索引
    > db.user.createIndex({name:1})
    {
    	"createdCollectionAutomatically" : false,
    	"numIndexesBefore" : 1,
    	"numIndexesAfter" : 2,
    	"ok" : 1
    }
    
    // 复合索引
    > db.user.createIndex({name:1, addr:1})
    {
    	"createdCollectionAutomatically" : false,
    	"numIndexesBefore" : 2,
    	"numIndexesAfter" : 3,
    	"ok" : 1
    }
    
    

    2、查看索引

    • db.collection.getIndexes():查看集合索引
    • db.collection.totalIndexSize():查看集合索引大小
    // 查看user集合索引大小
    > db.user.totalIndexSize()
    61440
    
    // 查看user集合索引
    > db.user.getIndexes()
    [
    	{
    		"v" : 2,
    		"key" : {
    			"_id" : 1
    		},
    		"name" : "_id_"
    	},
    	{
    		"v" : 2,
    		"key" : {
    			"name" : 1,
    			"addr" : 1
    		},
    		"name" : "name_1_addr_1"
    	},
    	{
    		"v" : 2,
    		"key" : {
    			"name" : 1
    		},
    		"name" : "name_1"
    	}
    ]
    

    3、删除索引

    • db.collection.dropIndexes():删除集合所有索引(除了_id字段外)

    • db.collection.dropIndex({'索引名称'}):删除集合指定索引(升序降序不能错)

    // 删除所有索引,只会删除除了_id以外的所有索引
    > db.user.dropIndexes()
    {
    	"nIndexesWas" : 3,
    	"msg" : "non-_id indexes dropped for collection",
    	"ok" : 1
    }
    
    // 删除name字段的索引,升序降序不能错,如果为-1,则提示无索引
    > db.user.dropIndex({name:1})
    { "nIndexesWas" : 3, "ok" : 1 }
    

    4、定时删除任务

    利用 TTL 集合对存储的数据进行失效时间设置:经过指定的时间段后或在指定的时间点过期,MongoDB 独立线程去清除数据。

    在指定秒数后使文档过期

    1、首先创建索引,设置过期时间为180秒

    db.col.createIndex( { createDate: 1 }, { expireAfterSeconds: 180 } )
    

    2、然后存储数据入库

    db.col.insert( { _id: 1, createDate: new Date() } )
    

    3、mongodb 会在大于 expireAfterSeconds 指定值的秒数后,过期

    在特定时钟时间使文档过期

    1、首先建立索引,设置 expireAfterSeconds 为 0

    db.col.createIndex( { createDate: 1 }, { expireAfterSeconds: 0 } )
    

    2、然后存储数据入库

    db.col.insert( { _id: 1, "createDate": new Date('July 22, 2020 12:00:00') } )
    

    3、createDate 的值为特定的时间值,等时间到达expireAt的值时,这个文档就 失效了。

    其他注意事项:

    • 索引关键字段必须是 Date 类型。
    • 非立即执行:扫描 Document 过期数据并删除是独立线程执行,默认 60s 扫描一次,删除也不一定是立即删除成功。
    • 单字段索引,混合索引不支持

    参考:https://www.cnblogs.com/jiangqw/p/12174746.html

    备份恢复及导入导出

    MongoDB 4.4版本 之后 MongoDB数据库工具现在与MongoDB服务 分开发布 需要单独安装

    • 下载地址:https://docs.mongodb.com/database-tools/
    • 下载安装或解压,配置到环境变量 path 中,然后可以使用 mongodump、 mongorestore、 mongoimport、 mongoexport 命令
    • 在导入其他人的库后,应该清空 -->该库下的用户 -->再创建用户后再连接

    数据导出与导入:mongoexport、mongoimport

    1、导出:mongoexport

    1、概念:

    mongoDB 中的 mongoexport 工具可以把一个 collection(集合) 导出成JSON格式或CSV格式的文件。可以通过参数指定导出的数据项,也可以根据指定的条件导出数据。
    

    2、语法:

    mongoexport -d dbname -c cname -o file --type json/csv -f field
    

    参数说明:

    • -d、--db:数据库名

    • -c 、--collection:集合名

    • -o、--out :输出的文件名

    • --type: 输出的格式,默认为json。可以为json/csv

    • -f:输出的字段,如果-type为csv,则需要加上-f "字段名"

    • -h:指明数据库宿主机的IP。如是本机可以去除该参数

    • -u:指明数据库的用户名

    • -p:指明数据库的密码

    示例:

    # 导出dbname数据库的cname集合中所有 _id,user_id,user_name,age,status 字段到 /mongoBack/cname.json文件中
    mongoexport -d dbname -c cname -o /mongoBack/cname.json -f "_id,user_id,user_name,age,status"
    

    2、导入:mongoimport

    当要插入的数据太多时,一条一条的insert在繁琐了。所以,我们可以在代码编辑器中以JSON格式编辑好要插入的数据,以 *.json 文件格式保存,然后导入数据库:下面就是将mydata.json文件,导入到test数据库的student集合中。

    1、语法:

    mongoimport -d dbname -c cname --file filename --headerline -f field
    

    参数说明:

    • -d :数据库名

    • -c :collection名

    • --type :导入的格式默认json。可以为json/csv

    • -f :导入的字段名

    • --headerline :如果导入的格式是csv,则可以使用第一行的标题作为导入的字段

    • --file :要导入的文件

    • --drop:导入前把集合中的数据清空

    • -h:指明数据库宿主机的IP。如是本机可以去除该参数

    • -u:指明数据库的用户名

    • -p:指明数据库的密码

    示例:

    # 把/mongoBack/cname.json中的数据导入dbname数据中的cname集合中,并且只导入_id,user_id,user_name,age,status字段
    mongoimport -d dbname -c cname --file /mongoBack/cname.json -f "_id,user_id,user_name,age,status" --drop
    

    MongoDB备份与恢复:mongodump、mongorestore

    3、备份:mongodump

    1、语法:

    mongodump -h host -u user -d dname -o dbdirectory
    

    参数说明:

    • -d: 需要备份的数据库实例,例如:test

    • -o: 备份的数据存放位置,该路径需要提前创建。例:/home/mongodump/

    • -h:指明数据库宿主机的IP。如是本机可以去除该参数

    • -u:指明数据库的用户名

    • -p:指明数据库的密码

    • --port:端口号。如是默认可以去除该参数

    示例:

    # 备份单个表
    mongodump -u user -p 123 --authenticationDatabase admin -d dname -c cname -o /backup/mongodb/dname_d_bak_201507021701.bak
    
    # 备份单个库
    mongodump -u user -p 123 --authenticationDatabase admin -d dname -o /backup/mongodb/
    
    # 备份所有库
    mongodump -u user -p 123 --authenticationDatabase admin -o /backup/allbak 
    
    # 备份所有库推荐使用添加--oplog参数的命令,这样的备份是基于某一时间点的快照,只能用于备份全部库时才可用,单库和单表不适用:
    mongodump -h 127.0.0.1 --port 27017 --oplog -o /backup/allbak 
    

    4、恢复:mongorestore

    1、语法:

    mongorestore -h host -u user -d dname --dir dbdirectory
    

    参数或名:

    • -d: 需要恢复的数据库实例,当然这个名称也可以和备份时候的不一样,比如test2

    • --dir: 备份数据所在位置,例如:/home/mongodump/itcast/

    • --drop: 恢复前把集合中的数据清空。

    • -h: MongoDB所在服务器地址。如是本机可以去除该参数

    • -u:指明数据库的用户名

    • -p:指明数据库的密码

    • --port:端口号。如是默认可以去除该参数

    示例:

    # 恢复单表
    mongorestore -u user -p 123 --authenticationDatabase admin -d dname -c cname /backup/mongodb/dname_d_bak_201507021701.bak/myTest/d.bson  
    
    # 恢复单个库:
    mongorestore -u user -p 123 --authenticationDatabase admin -d dname /backup/mongodb/
    
    # 恢复所有库:
    mongorestore -u user -p 123 --authenticationDatabase admin /backup/allbak
    
    # 备份时如果加了--oplogReplay参数,恢复时也要加上--oplogReplay参数,具体命令如下(下面是恢复单库的命令):
    mongorestore -d dname --oplogReplay /home/mongo/swrdbak/swrd/
    

    注意:

    1、mongorestore恢复数据默认是追加,如打算先删除后导入,可以加上--drop参数,不过添加--drop参数后,会将数据库数据清空后再导入,如果数据库备份后又新加入了数据,也会将新加的数据删除,它不像mysql有一个存在的判断

    2、mongodump在mongo关闭时,也是可以备份的,不过需要指定数据目录,命令为:mongodump --dbpath /data/db

    使用MongoDB其他命令行工具远程操作连接时,尽量加上如下参数.

    --authenticationDatabase admin
    

    Java操作MongoDB

    参考:http://c.biancheng.net/view/6571.html

    SpringBoot集成MongoDB

    Spring Boo与MongoDB集成,并通过MongoRepository以及MongoTemplate来执行CRUD操作。

    1、准备工作

    1.1、依赖包版本号:

    Spring Boot:2.3.0.RELEASE

    Spring Boot Data MongoDB:2.3.0.RELEASE

    MongoDB:4.2.6

    MongoDB Driver:4.0.3

    1.2、要操作集合数据结构(即model)

    Department:

    字段名 类型
    id String
    name String
    description String
    employees Arrays

    Employee:

    字段名 类型
    empId String
    name String
    age int
    address String

    2、创建项目

    2.1、创建SpringBoot项目,并添加依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    

    2.2、配置文件增加MongoDB配置,application.properties

    spring.data.mongodb.host=127.0.0.1
    spring.data.mongodb.port=27017
    spring.data.mongodb.database=test
    spring.data.mongodb.username=admin
    spring.data.mongodb.password=password
    
    # 简写,其中name是用户名,pwd是密码,test是数据库
    # spring.data.mongodb.uri=mongodb://127.0.0.1:27017/test
    # spring.data.mongodb.uri=mongodb://name:pwd@127.0.0.1:27017/test
    # 如果要配置多个数据库,则中间用","分割
    # spring.data.mongodb.uri=mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/test
    

    备注:username不能是admin管理员用户。必须是当前database的用户才可以

    2.3、测试项目能否正常启动,然后查看项目结构:

    ├─src
    │  ├─main
    │  │  ├─java
    │  │  │  └─com
    │  │  │      └─example
    │  │  │          └─mongodb
    │  │  │              │  MongodbApplication.java
    │  │  │              │
    │  │  │              ├─controller
    │  │  │              │      DepartmentController.java
    │  │  │              │      EmployeeController.java
    │  │  │              │
    │  │  │              ├─model
    │  │  │              │      Department.java
    │  │  │              │      Employee.java
    │  │  │              │
    │  │  │              └─repository
    │  │  │                      DeptRepository.java
    │  │  │                      DeptTemplate.java
    │  │  │                      EmpRepository.java
    │  │  │                      EmpTemplate.java
    │  │  │
    │  │  └─resources
    │  │      │  application.properties
    │  │      │
    │  │      ├─static
    │  │      └─templates
    │  └─test
    │
    └─target
    

    2.4、创建Model Bean(Department.classEmployee.clss

    package com.example.mongodb.model;
    
    import lombok.Data;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.index.Indexed;
    import org.springframework.data.mongodb.core.mapping.DBRef;
    import org.springframework.data.mongodb.core.mapping.Document;
    import java.io.Serializable;
    import java.util.List;
    
    @Data
    @Document("Department")
    public class Department implements Serializable {
        @Id
        private String id;
        @Indexed(name = "deptName")
        private String name;
        private String description;
        @DBRef
        private List<Employee> employees;
    }
    
    package com.example.mongodb.model;
    
    import lombok.Data;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;
    import java.io.Serializable;
    
    @Data
    @Document("Employee")
    public class Employee implements Serializable {
        @Id
        private String empId;
        private String name;
        private int age;
        private String address;
    }
    

    2.5、两种操作数据的方式:MongoRepositoryMongoTemplate

    MongoRepository

    DeptRepository.classEmpRepository.class

    package com.example.mongodb.repository;
    
    import com.example.mongodb.model.Department;
    import org.springframework.data.mongodb.repository.MongoRepository;
    import org.springframework.data.mongodb.repository.Query;
    import org.springframework.stereotype.Repository;
    import java.util.List;
    
    @Repository
    public interface DeptRepository extends MongoRepository<Department,String> {
    
        @Query(value = "{'Employee.name': ?0}", fields = "{'employees' : 0}")
        Department findDepartmentByEmployeeName(String empName);
    
        List<Department> findDepartmentByName(String name);
    }
    
    package com.example.mongodb.repository;
    
    import com.example.mongodb.model.Employee;
    import org.springframework.data.mongodb.repository.MongoRepository;
    
    public interface EmpRepository extends MongoRepository<Employee,String> {
    
    }
    

    MongoTemplate

    DeptTemplate.classEmpTemplate.class

    package com.example.mongodb.repository;
    
    import com.example.mongodb.model.Department;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.core.query.Update;
    import org.springframework.stereotype.Repository;
    import java.util.List;
    
    @Repository
    public class DeptTemplate {
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        public List<Department> findAll() {
            return mongoTemplate.findAll(Department.class);
        }
    
        public List<Department> findDepartmentByName(String deptName){
            Query query = new Query();
            query.addCriteria(Criteria.where("name").is(deptName));
            return mongoTemplate.find(query, Department.class);
        }
    
        public Department save(Department department) {
            mongoTemplate.save(department);
            return department;
        }
    
        public Department update(Department department){
            Query query = new Query();
            query.addCriteria(Criteria.where("id").is(department.getId()));
            Update update = new Update();
            update.set("name", department.getName());
            update.set("description", department.getDescription());
            return mongoTemplate.findAndModify(query, update, Department.class);
        }
    
        public void deleteById(String deptId) {
            Query query = new Query();
            query.addCriteria(Criteria.where("id").is(deptId));
            mongoTemplate.remove(query, Department.class);
        }
    
    }
    
    package com.example.mongodb.repository;
    
    import com.example.mongodb.model.Employee;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.core.query.Update;
    import org.springframework.stereotype.Repository;
    import java.util.List;
    
    @Repository
    public class EmpTemplate {
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        public List<Employee> findAll() {
            return mongoTemplate.findAll(Employee.class);
        }
    
        public Employee save(Employee employee) {
            mongoTemplate.save(employee);
            return employee;
        }
    
        public Employee update(Employee employee){
            Query query = new Query();
            query.addCriteria(Criteria.where("empId").is(employee.getEmpId()));
            Update update = new Update();
            update.set("name", employee.getName());
            update.set("description", employee.getAddress());
            return mongoTemplate.findAndModify(query, update, Employee.class);
        }
    
        public void deleteById(String empId) {
            Query query = new Query();
            query.addCriteria(Criteria.where("empId").is(empId));
            mongoTemplate.remove(query, Employee.class);
        }
    }
    
    

    2.6、创建Controller(DepartmentController.classEmployeeController.class

    package com.example.mongodb.controller;
    
    import com.example.mongodb.model.Department;
    import com.example.mongodb.model.Employee;
    import com.example.mongodb.repository.DeptRepository;
    import com.example.mongodb.repository.DeptTemplate;
    import com.example.mongodb.repository.EmpRepository;
    import com.example.mongodb.repository.EmpTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    import java.util.Collections;
    import java.util.List;
    import java.util.Optional;
    
    @RestController
    public class DepartmentController {
        // v1的接口使用 MongoRepository 方式操作数据
        @Autowired DeptRepository deptRepository;
        @Autowired EmpRepository empRepository;
        // v2的接口使用 MonoTemplate 方式操作数据
        @Autowired DeptTemplate deptTemplate;
        @Autowired EmpTemplate empTemplate;
    
        /********************* MongoRepository方式 *********************/
    
        @PostMapping("/v1/dept/save")
        public Department v1save(@RequestBody Department department) {
            // 添加部门信息前首先保存员工信息
            List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList());
            employees.forEach(employee -> empRepository.save(employee));
            return deptRepository.save(department);
        }
        @GetMapping("/v1/dept/list")
        public List<Department> v1list(){
            return deptRepository.findAll();
        }
    
        @PutMapping("/v1/dept/update/{deptId}")
        public Department v1update(@RequestBody Department department, @PathVariable String deptId) {
            department.setId(deptId);
            List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList());
            employees.forEach(Employee -> empRepository.save(Employee));
            return deptRepository.save(department);
        }
        @DeleteMapping("/v1/dept/delete/{deptId}")
        public String v1delete(@PathVariable String deptId) {
            deptRepository.deleteById(deptId);
            return deptId;
        }
        @GetMapping("/v1/dept/get/{deptName}")
        public List<Department> v1getByName(@PathVariable String deptName) {
            return deptRepository.findDepartmentByName(deptName);
        }
        @GetMapping("/v1/dept/get/emp/{empName}")
        public Department v1getByEmpName(@PathVariable String empName) {
            return deptRepository.findDepartmentByEmployeeName(empName);
        }
    
        /********************* MonoTemplate方式 *********************/
    
        @PostMapping("/v2/dept/save")
        public Department v2save(Department department) {
            List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList());
            employees.forEach(Employee -> empRepository.save(Employee));
            return deptTemplate.save(department);
        }
        @GetMapping("/v2/dept/list")
        public List<Department> v2list() {
            return deptTemplate.findAll();
        }
        @PutMapping("/v2/dept/update")
        public Department v2update(Department department){
            List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList());
            employees.forEach(emp -> empRepository.save(emp));
            return deptTemplate.update(department);
        }
        @DeleteMapping("/v2/dept/delete/{deptId}")
        public void v2delete(@PathVariable String deptId) {
            deptTemplate.deleteById(deptId);
        }
        @GetMapping("/v2/dept/get/{deptName}")
        public List<Department> v2getByName(@PathVariable String deptName){
            return deptTemplate.findDepartmentByName(deptName);
        }
    }
    
    package com.example.mongodb.controller;
    
    import com.example.mongodb.model.Department;
    import com.example.mongodb.model.Employee;
    import com.example.mongodb.repository.DeptRepository;
    import com.example.mongodb.repository.DeptTemplate;
    import com.example.mongodb.repository.EmpRepository;
    import com.example.mongodb.repository.EmpTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.Collections;
    import java.util.List;
    import java.util.Optional;
    
    @RestController
    public class EmployeeController {
        // v1的接口使用 MongoRepository 方式操作数据,v2的接口使用 MonoTemplate 方式操作数据
        @Autowired EmpRepository empRepository;
        @Autowired EmpTemplate empTemplate;
    
        /********************* MongoRepository方式 *********************/
    
        @PostMapping("/v1/emp/save")
        public Employee v1save(@RequestBody Employee employee) {
            return empRepository.save(employee);
        }
        @GetMapping("/v1/emp/list")
        public List<Employee> v1list(){
            return empRepository.findAll();
        }
    
        @PutMapping("/v1/emp/update/{empId}")
        public Employee v1update(@RequestBody Employee employee, @PathVariable String empId) {
            //employee.setEmpId(empId);
            return empRepository.save(employee);
        }
        @DeleteMapping("/v1/emp/delete/{empId}")
        public String v1delete(@PathVariable String empId) {
            empRepository.deleteById(empId);
            return empId;
        }
    }
    

    3、测试接口

    v1的接口使用MongoRepository方式操作数据,v2的接口使用MonoTemplate方式操作数据。

    ###新增
    POST http://localhost:8080/v1/dept/save
    Content-Type: application/json
    
    {
      "id": "007",
      "name": "阿里巴巴",
      "description": "阿里巴巴Java开发",
      "employees": [{
        "empId": "1",
        "name": "Sam Liu",
        "age": 28,
        "address": "深圳"
      }]
    }
    
    
    ###更新
    PUT http://localhost:8080/v1/dept/update/007
    Content-Type: application/json
    
    {
      "id": "007",
      "name": "阿里巴巴-new",
      "description": "阿里巴巴Web开发",
      "employees": [
        {
          "empId": "1",
          "name": "Sam Liu",
          "age": 28,
          "address": "深圳"
        },
        {
          "empId": "2",
          "name": "Tom",
          "age": 38,
          "address": "杭州"
        }
      ]
    }
    
    ###删除
    DELETE http://localhost:8080/v1/dept/delete/007
    
    ###查询
    GET http://localhost:8080/v1/dept/list
    
    
    ##############################################################
    ###新增
    POST http://localhost:8080/v1/emp/save
    Content-Type: application/json
    
    {
      "empId": "1",
      "name": "Sam Liu",
      "age": 28,
      "address": "深圳"
    }
    
    ###更新
    PUT http://localhost:8080/v1/emp/update/1
    Content-Type: application/json
    
    {
      "empId": "1",
      "name": "Sam Liu-update",
      "age": 28,
      "address": "深圳-update"
    }
    
    ###删除
    DELETE http://localhost:8080/v1/emp/delete/007
    
    ###查询
    GET http://localhost:8080/v1/emp/list
    
    
  • 相关阅读:
    [蓝桥杯][基础练习VIP]完美的代价
    [蓝桥杯][基础练习VIP]分解质因数
    [蓝桥杯][基础练习VIP]Sine之舞
    [蓝桥杯][基础练习VIP]回形取数
    ZJOI 2006 书架
    HNOI2012 永无乡
    9.23 逃跑的牛Barn Running Away
    9.20 Magical multisets
    9.18 Roads on a kingdom
    【MySQL】MySQL复制表的两种方法
  • 原文地址:https://www.cnblogs.com/liusuixing/p/14130896.html
Copyright © 2011-2022 走看看