MongoDB相关概念

MongoDB简介

MongoDB是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种。是最像关系型数据库(MySQL)的非关系型数据库。

它支持的数据结构非常松散,是一种类似于 JSON 的 格式叫BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。

MongoDB中的记录是一个文档,它是一个由字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。

使用场景:

具体的应用场景如:

1)社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。

2)游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、高效率存储和访问。

3)物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。

4)物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析。

5)视频直播,使用 MongoDB 存储用户信息、点赞互动信息等。

这些应用场景中,数据操作方面的共同特点是:

(1)数据量大

(2)写入操作频繁(读写都很频繁)

(3)价值较低的数据,对事务性要求不高

对于这样的数据,我们更适合使用MongoDB来实现数据的存储。相对于Mysql,MongoDB可以以更低的成本解决问题(包括学习、开发、运维等成本)

体系结构

MySQL和MongoDB对比

image-20231009162912885

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

数据模型

MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行,数据在MongoDB中以BSON(Binary-JSON)文档的格式存储在磁盘上

BSON(Binary Serialized Document Format)是一种类json的一种二进制形式的存储格式,简称Binay JSON。BSON和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Data和BinData类型。

BSON采用了类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的三个特点,可以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高,但它的缺点是空间利用率不是很理想。

Bson中,除了基本的JSON类型:string,integer,boolean,double,null,array和object,mongo还使用了特殊的数据类型。这些类型包括date,object id,binary data,regular expression 和code。每一个驱动都以特定语言的方式实现了这些类型,查看你的驱动的文档来获取详细信息。

BSON数据类型参考列表:

数据类型 描述 举例
字符串 UTF-8字符串都可表示为字符串类型的数据 {“x” : “foobar”}
对象id 对象id是文档的12字节的唯一ID {“X” : ObjectId()}
布尔值 真或者假:true 或者 false {“x”:true}+
数组 值的集合或者列表可以表示成数组 {“x” : [“a”, “b”, “c”]}
32位整数 类型不可用。JavaScript仅支持64位浮点数,所以32位整数会被自动转换 shell是不支持该类型的,shell中默认会转换成64位浮点数
64位整数 不支持这个类型。shell会使用一个特殊的内嵌文档来显示64位整数 shell是不支持该类型的,shell中默认会转换成64位浮点数
64为浮点数 shell中的数字就是这一种类型 {“x”:3.14159,”y”:3}
null 表示空值或者未定义的对象 {“x”:null}
undefined 文档中也可以使用未定义类型 {“x”:undefined}
符号 shell不支持,shell会将数据库中的符号类型的数据自动转换成字符串
正则表达式 文档中可以包含正则表达式,采用JavaScript的正则表达式语法 {“x” : /foobar/i}
代码 文档中还可以包含JavaScript代码 {“x” : function() { /* …… */ }}
二进制数据 二进制数据可以由任意字节的串组成,不过shell中无法使用
最大值/最小值 BSON包括一个特殊类型,表示可能的最大值。shell中没有这个类型

提示:

shell默认使用64位浮点型数值。{“x”:3.14}或{“x”:3}。对于整型值,可以使用NumberInt(4字节符号整数)或NumberLong(8字节符号整数),{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}

特点

高性能:

  • MongoDB提供高性能的数据持久性。特别是:
    • 对嵌入式数据模型的支持减少了数据库系统上的 I/O 活动
    • 索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地理位置索引可用于构建各种 O2O 应用)
    • mmapv1 、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求
    • Gridfs解决文件存储的需求

高可用性:

  • MongoDB的复制工具称为副本集(replica set),它可提供自动故障转移和数据冗余

高可扩展性:

  • MongoDB提供了水平可扩展性作为其核心功能的一部分。
  • 分片将数据分部在一组集群的机器上。(海量数据存储,服务能力水平扩展)
  • 从3.4开始,MongoDB支持基于片键创建数据区域。在一个平衡的集群中,MongoDB将一个区域所覆盖的读写只定向到该区域内的那些片

丰富的查询支持:

MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如数据聚合、本地搜索和地理空间查询等

其他特点: 如无模式(动态模式)、灵活的文档模型

部署

Windows系统中的安装启动

下载安装

可以在官网上下载。Windows系统用zip,Linux系统用tar

解压安装启动

将压缩包解压到一个目录中。

在解压目录中,手动建立一个目录用于存放数据文件,如 /data/db

  • 方式1 :命令行参数方式启动服务

    /bin 目录中打开命令行提示符,输入如下命令:

    mongod --dbpath = ../data/db

    我们在启动信息中可以看到,mongoDB的默认端口是27017,如果我们想改变默认的启动端口,可以通过–port来指定端口。

    为了方便我们每次启动,可以将安装目录的 bin 目录设置到环境变量的 path 中,bin 目录下是一些常用命令,比如 mongod 是启动服务用的,mongo 是客户端连接服务用的

  • 方式2 :配置文件方式启动服务

    在解压目录中新建 config 文件夹,该文件夹中新建配置文件 mongod.conf ,内容参考如下:

    storage:
    #The directory where the mongod instance stores its data.Default Value is "\data\db" on Windows.
    dbPath: D:\02_Server\DBServer\mongodb-win32-x86_64-2008plus-ssl-4.0.1\data

    详细内容参考文档 :https://docs.mongodb.com/manual/reference/configuration-options/

注意:

  1. 配置文件中如果使用双引号,比如路径地址,自动会将双引号的内容转义。如果不转义,则会报错:

    error-parsing-yaml-config-file-yaml-cpp-error-at-line-3-column-15-unknown-escape-character-d

    解决:

    • \ 换成 / 或者 \\
    • 如果路径中没有控制,则无需加引号
  2. 配置文件中不能以 Tab 分割字段

    解决:

    • 将其转换成空格

      启动方式:

      mongodb -f ../config/mongod.conf
      或
      mongodb --config ../config/mongod.conf

更多参数配置:

systemLog:
	destination: file
	#The path of the log file to which mongod or mongos should send all diagnostic logging information
	path: "D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/log/mongod.log"
	logAppend: true
storage:
	journal:
		enabled: true
	#The directory where the mongod instance stores its data.Default Value is "/data/db".
	dbPath: "D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/data"
net:
	#bindIp: 127.0.0.1
	port: 27017
setParameter:
	enableLocalhostAuthBypass: false

Shell连接(mongo命令)

在命令提示符输入一下 shell 命令即可完成登录

mongo 
或
mongo --host=127.0.0.1 --port=27017

查看已有的数据库

> show databases

退出mongodb

exit

更多参数可以通过 help 查看

mongo --help

提示:

MongoDB javaScript shell 是一个基于 javaScript 的解释器,所以是支持 js 程序和语法的

Compass - 图形化界面客户端

在MongoDB官网下载 MongoDB Compass

如果是下载安装版,则按照步骤安装;如果是下载加压缩版,直接解压,执行里面的 MongoDBCompassCommunity.exe 文件即可。

在打开的界面中,输入主机地址、端口等相关信息,点击连接:

image-20231009193604912

在Linux系统中的安装启动

目标:在Linux中部署一个单机的 MongoDB,作为生产环境下使用

提示:和 Windows 下操作差不多

下载安装连接

  1. 官网下载,可以通过xftp传到Linux中

  2. 上传到Linux后,解压到当前目录:

    tar -zxvf mongodb-linux-x86_64-4.0.10.tgz
  3. 移动解压后的文件夹到指定的目录中:

    mv mongodb-linux-x86_64-4.0.10 /usr/local/mongodb
  4. 新建几个目录,分别用来存储数据和日志:

    #数据存储目录
    mkdir -p /mongodb/single/data/db
    #日志存储目录
    mkdir -p /mongodb/single/log
  5. 新建并修改配置文件

    vim /mongodb/single/mongod.conf

    配置文件的内容如下:

    systemLog:
    #MongoDB发送所有日志输出的目标指定为文件
    # #The path of the log file to which mongod or mongos should send all diagnostic logging information
      destination: file
    #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
      path: "/mongodb/single/data/log/mongod.log"
    #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
      logAppend: true
    storage:
    #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
    ##The directory where the mongod instance stores its data.Default Value is "/data/db".
      dbPath: "/mongodb/single/data/db"
    # journal:
    #启用或禁用持久性日志以确保数据文件保持有效和可恢复。
      # enabled: true
    processManagement:
    #启用在后台运行mongos或mongod进程的守护进程模式。
      fork: true
    net:
    #服务实例绑定的IP,默认是localhost
      bindIp: 192.168.200.133
    #bindIp
    #绑定的端口,默认是27017
      port: 27017
  6. 启动 MongoDB 服务

    /usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf

    启动成功的页面会显示:

    about to fork child process, waiting until server is ready for connections.
    forked process: 90384
    child process started successfully, parent exiting

    注意:

    如果启动后不是 successfully ,则是启动失败了。原因基本上就是配置文件有问题

    可以通过进程来查看服务是否启动了:

    [root@byxlCentos-1 ~]# ps -ef |grep mongod
    root       2717      1  0 19:45 ?        00:00:02 /usr/local/mongodb/bin/mongod -f /mongodb/single/mongodb.conf
    root       3057   2907  0 19:52 pts/0    00:00:00 grep --color=auto mongod
  7. 分别使用mongo命令和compass 工具来连接测试

    • mongo 命令

      注意:--host=自己Linux的ip地址 ,port默认就是 27017这个端口

      image-20231009195702967

    • compass工具

      注意要改成自己Linux的ip地址

      image-20231009195842357

    提示:如果远程连接不上,需要配置防火墙放行,或直接关闭Linux防火墙

    #查看防火墙状态
    systemctl status firewalld
    
    #临时关闭防火墙
    systemctl stop firewalld
    
    #开机禁止启动防火墙
    systemctl disable firewalld
  8. 停止关闭服务

    停止服务的方式有两种:快速关闭和标准关闭:

    • 快速关闭方法(快速、简单,数据可能会出错)

      目标:通过系统的kill命令直接杀死进程:

      杀完要检查一下,避免有的没有杀掉。

      #通过进程编号关闭节点
      kill -2 54410

      补充: 如果一旦是因为数据损坏,则需要进行如下操作(了解):

      1. 删除 lock 文件:

        rm -rf /mongodb/single/data/db/*.lock
      2. 修复数据

        /usr/local/mongdb/bin/mongod --repair --dbpath=/mongodb/single/data/db
    • 标准的关闭方法(数据不容易出错,但麻烦):

      目标:通过mongo客户端中的 shutdownServer 命令来关闭服务

      //客户端登录服务,注意,这里通过localhost登录,如果需要远程登录,必须先登录认证才行。
      mongo --port 27017
      
      //#切换到admin库
      use admin
      
      //关闭服务
      db.shutdownServer()

基本常用命令

案例需求

存放文章品论的数据存放到MongoDB中,数据结构参考如下:

数据库:articledb

专栏文章评论 comment
字段名称 字段含义 字段类型 备注
_id ID ObjectId 或 String Mongo的主键的字段
article 文章ID String
content 评论内容 String
userid 评论人ID String
nickname 评论人昵称 String
createdatetime 评论的日期时间 Date
likenum 点赞数 Int32
replynum 回复数 Int32
state 状态 String 0 :不可见;1 :可见
parentid 上级ID String 如果为0表示文章的顶级评论

数据库操作

选择和创建数据库

选择和创建数据库的语法格式:

use 数据库名称

如果数据库不存在则自动创建,例如,以下语句创建 spitdb 数据库:

use articledb

查看有权限查看的所有的数据库命令

show dbs
或
show databases

注意:在 MongoDB 中,集合只有在内容插入后才会创建!就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建

查看当前正在使用的数据库命令

db

MongoDB 中默认的数据库为 test,如果你没有选择数据库,集合将存放在 test 数据库中。

另外:

数据库名可以是满足以下条件的任意 UTF-8 字符串

  • 不能是空字符串(””)
  • 不得含有 ‘ ‘ (空格)、. 、 $ 、 / 、 \ 和 \0 (空字符)
  • 应全部小写
  • 最多有64字节

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库

  • admin : 从权限的角度来看,这是 “root” 数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器
  • local : 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config : 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片和相关信息

数据库的删除

MongoDB 删除数据库的语法格式如下:

db.dropDatabase()

提示:主要用来删除已经持久化的数据库

集合操作

集合,类似关系型数据库中的表

可以显示的创建,也可以隐式的创建

集合的显示创建(了解)

基本语法格式:

db.createCollection(name)

参数说明:

  • name :要创建的集合名称

例如:创建一个名为 mycollection 的普通集合

db.createCollection(mycollection)

查看当前库中的表:show tables

show collections
或
show tables

集合的命名规范:

  • 集合名不能是空字符串 “ “
  • 集合名不能含有 \0 字符(空字符),这个字符表示集合名的结尾
  • 集合名不能以 “system.” 开头,这是为系统集合保留的前缀
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$

集合的隐式创建(常用)

当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合(collection)。

db.collection.insert({})

集合的删除

集合删除语法格式如下:

db.collection.drop()
或
db.集合.drop()

返回值

如果成功删除选定集合,则 drop() 方法返回 true ,否则返回false

例如:要删除 mycollection 集合

db.mycollection.drop()

文档基本CRUD

文档(document)的数据结构和 JSON 基本一样

所有存储在集合中的数据都是 BSON 格式

文档的插入

  1. 单个文档的插入

    使用 insert() 或 save() 方法向集合中插入文档,语法如下:

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

    参数:

    Parameter Type Description
    document document or array 要插入到集合中的文档或文档数组(json格式)
    writeConcern document Optional. A document expressing the write concern. Omit to use the default write concern.See Write Concern.Do not explicitly set the write concern for the operation if run in a
    ordered boolean 可选。如果为真,则按顺序插入数组中的文档,如果其中一个文档出现错误,MongoDB将返回而不处理数组中的其余文档。如果为假,则执行无序插入,如果其中一个文档出现错误,则继续处理数组中的主文档。在版本2.6+中默认为true

    示例:

    向comment集合(表)中插入一条测试数据:

    db.comment.insert({
    	"articleid":"100000",
    	"content":"今天天气真好,阳光明媚",
    	"userid":"1001",
    	"nickname":"Rose",
    	"createdatetime":new Date(),
    	"likenum":NumberInt(10),
    	"state":null
    })

    提示:

    • comment集合如果不存在,则会隐式创建
    • mongo中的数字,默认情况下是 doouble 类型,如果要存整型,必须使用函数 NumberInt(整型数字),否则取出来就有问题了
    • 插入当前日期使用 new Date()
    • 插入的数据没有指定 _id ,会自动生成主键值
    • 如果某字段没值,可以赋值为 null ,或不写该字段

    执行后,如下,说明插入一个数据成功了。

    writeResult({"nInserted" : 1})

    注意:

    • 文档中的键/值对是有序的
    • 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)
    • MongoDB 区分类型和大小写
    • MongoDB 的文档不能有重复的键
    • 文档的键是字符串,除了少数例外情况,键可以使用任意 UTF-8 字符

    文档键命名规范:

    • 键不能含有 \0 (空字符),这个字符用来表示键的结尾
    • . 和 $ 有特别的意义,只有在特定环境下才能使用
    • 以下划线 “_” 开头的键是保留的(不是严格要求的)
  2. 批量插入

    语法:

    db.collection.insertMany(
    	[ <document 1>, <document 3>, ...],
    	{
    		writeConcern: <document>,
    		ordered: <boolean>
    })

    参数:

    Parameter Type Description
    document document 要插入到集合中的文档或文档数组。(json格式)
    writeConcern document Optional. A document expressing the write concern. Omit to use the default write concern.Do not explicitly set the write concern for the operation if run in a transaction. To use write concern with transactions, see Transactions and Write Concern.
    ordered boolean 可选。一个布尔值,指定Mongod实例应执行有序插入还是无序插入。默认为true。

    示例:

    批量插入多条文章评论:

    db.comment.insertMany([
      {
        "_id": "1",
        "articleid": "100001",
        "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。",
        "userid": "1002",
        "nickname": "相忘于江湖",
        "createdatetime": new Date("2019-08-05T22:08:15.522Z"),
        "likenum": NumberInt(1000),
        "state": "1"
      },
      {
        "_id": "2",
        "articleid": "100001",
        "content": "我夏天空腹喝凉开水,冬天喝温开水",
        "userid": "1005",
        "nickname": "伊人憔悴",
        "createdatetime": new Date("2019-08-05T23:58:51.485Z"),
        "likenum": NumberInt(888),
        "state": "1"
      },
      {
        "_id": "3",
        "articleid": "100001",
        "content": "我一直喝凉开水,冬天夏天都喝。",
        "userid": "1004",
        "nickname": "杰克船长",
        "createdatetime": new Date("2019-08-06T01:05:06.321Z"),
        "likenum": NumberInt(666),
        "state": "1"
      },
      {
        "_id": "4",
        "articleid": "100001",
        "content": "专家说不能空腹吃饭,影响健康。",
        "userid": "1003",
        "nickname": "凯撒",
        "createdatetime": new Date("2019-08-06T08:18:35.288Z"),
        "likenum": NumberInt(2000),
        "state": "1"
      },
      {
        "_id": "5",
        "articleid": "100001",
        "content": "研究表明,刚烧开的水千万不能喝,因为烫嘴。",
        "userid": "1003",
        "nickname": "凯撒",
        "createdatetime": new Date("2019-08-06T11:01:02.521Z"),
        "likenum": NumberInt(3000),
        "state": "1"
      }
    ]);

    提示:

    插入时指定了 _id ,则主键就是该值。

    如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。

    因为批量插入由于数据较多容易出现失败,因此使用 try catch 进行异常捕捉处理,测试的时候可以不处理,如:(了解)

    try{
    db.comment.insertMany([
      {
        "_id": "1",
        "articleid": "100001",
        "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。",
        "userid": "1002",
        "nickname": "相忘于江湖",
        "createdatetime": new Date("2019-08-05T22:08:15.522Z"),
        "likenum": NumberInt(1000),
        "state": "1"
      },
      {
        "_id": "2",
        "articleid": "100001",
        "content": "我夏天空腹喝凉开水,冬天喝温开水",
        "userid": "1005",
        "nickname": "伊人憔悴",
        "createdatetime": new Date("2019-08-05T23:58:51.485Z"),
        "likenum": NumberInt(888),
        "state": "1"
      },
      {
        "_id": "3",
        "articleid": "100001",
        "content": "我一直喝凉开水,冬天夏天都喝。",
        "userid": "1004",
        "nickname": "杰克船长",
        "createdatetime": new Date("2019-08-06T01:05:06.321Z"),
        "likenum": NumberInt(666),
        "state": "1"
      },
      {
        "_id": "4",
        "articleid": "100001",
        "content": "专家说不能空腹吃饭,影响健康。",
        "userid": "1003",
        "nickname": "凯撒",
        "createdatetime": new Date("2019-08-06T08:18:35.288Z"),
        "likenum": NumberInt(2000),
        "state": "1"
      },
      {
        "_id": "5",
        "articleid": "100001",
        "content": "研究表明,刚烧开的水千万不能喝,因为烫嘴。",
        "userid": "1003",
        "nickname": "凯撒",
        "createdatetime": new Date("2019-08-06T11:01:02.521Z"),
        "likenum": NumberInt(3000),
        "state": "1"
      }
    ]);
    } catch (e){
    	print(e);
    }

文档的基本查询

查询数据的语法格式如下:

db.collection.find(<query>, [projection])

参数:

Parameter Type Description
query document 可选。使用查询运算符指定选择筛选器,若要返回集合中的所有文档,请省略此参数或传递空文档 ({})
projection document 可选。指定要在与查询筛选器匹配的文档中返回的字段(投影)。若要返回匹配文档中的所有字段,请省略此参数

示例:

  1. 查询所有

    如果我们要查询 spit 集合的所有文档,输入以下命令:

    db.comment.find()
    或
    db.comment.find({})

    这里你会发现每条文档会有一个叫 _id 的字段,这个相当于我们原来关系数据库中表的主键,当在插入文档记录时没有指定该字段,MongoDB 会自动创建,其类型时 ObjectID 类型。

    如果在插入文档记录时指定该字段也可以,其类型可以时 ObjectID类型,也可以是MongoDB支持的任意类型。

    如果想按一定条件来查询,例如我想查询 userid 为 1003 的记录,需要在find()中添加参数,参数也是json格式,如下:

    db.comment.find({userid : "1003"})

    如果只需要返回符合条件的第一条数据,我们可以使用 findOne 命令,语法和find 一样。

    db.comment.find({userid : "1003"})  //只会返回第一条数据记录
  2. 投影查询(Projection Query):

    如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)

    例如:查询结果只显示 _id 、userid 、nickname

    >db.comment.find({userid:"1002"},{userid:1, nickname:1})

    默认 _id 会显示。

    image-20231010093933385

    例如:查询结果只显示 userid 、nickname ,不显示 _id

    >db.comment.find({userid:"1003"},{userid:1, nickname:1, id:0})
    { "userid" : "1003", "nickname" : "凯撒" }
    { "userid" : "1003", "nickname" : "凯撒" }

    image-20231010094643848

    例如:查询所有数据,但只显示 _id、userid、nickname

    >db.comment.find({}, {userid:1, nickname:1})

    image-20231010094756809

文档的更新

更新文档的语法:

db.collection.update(query, update, options)
//或
db.collection.update(
	<query>,
	<update>,
	{
		upset: <boolean>,
		multi: <boolean>,
		writeConcern: <document>,
		collation: <document>,
		arrayFilters: [ <filterdocument>, ...],
		hint: <ducument|string>   
	}
)

参数:

image-20231016080809741

提示:

主要关注前四个参数即可。

示例:

  1. 覆盖的修改

    如果我们想修改 _id 为1的记录,点赞量为 1001 ,输入一下语句:

    db.comment.update({_id:"1"}, {likenum:NumberInt(1002)})

    执行后,我们会发现,这条文档除了 likenum 字段,其他字段都不见了

  2. 局部修改

    为了解决这个问题,我们需要使用修改器 $set 来实现,命令如下:

    我们想修改为 _id 为2的记录,浏览器为889,输入以下语句:

    db.comment.update({_id:"2"},{$set:{likenum:NumberId(889)}})
  3. 批量的修改

    更新所有用户为 1003 的用户的昵称为 张三

    //默认只修改第一条数据
    db.comment.update({userid:"1003"}, {$set:{nickname:"张三"}})
    //修改所有符合条件的数据
    db.comment.update({userid:"1003"}, {$set:{nickname:"李四"}}, {multi:true})

    提示:如果不加后面的参数{multi:true} ,则只更新符合条件的第一条记录

  4. 列值增长的修改

    如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用 inc 运算符来实现。

    需求:对3号数据的点赞数,每次递增1

    db.comment.update({_id:"3"}, {$inc: {likenum:NumberInt(1)}})

删除文档

删除文档的语法结构:

db.集合名称.remove(条件)

以下语句可以将数据全部删除,请慎用

db.comment.remove({})

如果删除 _id = 1 的记录,输入以下语句:

db.comment.remove({id:"1"})

文档分页查询

统计查询

统计查询使用 count( ) 方法,语法如下:

db.collection.count(query, options)

参数:

Parameter Type Description
query document 查询选择条件
options document 可选。用于修改技术的额外选项

提示:可选项暂时不使用

示例:

  1. 统计所有记录数:

    统计comment集合的所有的记录数:

    db.comment.count()
  2. 按条件统计记录数:

    统计userid为1003的记录条数:

    db.comment.count({userid:"1002"})

image-20231016085540647

提示:默认情况下 count() 方法返回符合条件的全部记录条数

分页列表查询

可以使用 limit() 方法来读取指定数量的数据,使用 skip() 方法来跳过指定数量的数据。

基本语法如下:

db.collection.find().limit(number).skip(number)

如果想返回指定条数的记录,可以在find方法后调用limit方法来返回结果,默认值20,例如:

db.comment.find().limit(3)  //表示只显示最前面的三条数据

skip 方法接收一个数字n作为跳过的记录条数(即前n个数据不要),默认值为0

db.comment.find().skip(3)  //表示跳过前三条数据输出后面的数据

分页查询:需求:每页2个,第二页开始:跳过前两条数据,接着显示3、4条数据

//第一页
db.comment.find().skip(0).limit(2)
//第二页
db.comment.find().skip(2).limit(2)
//第三页
db.comment.find().skip(4).limit(2)

排序查询

sort( ) 方法对数据进行排序,sort( ) 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,-1 为降序排列

语法格式如下:

db.collection.find().sort({key:1})
//或
db.collection.find().sort(排序方式)

示例:

先对 userid 降序排列,再对其中的如果有重复则对点赞数进行升序排列

db.comment.find().sort({userid: -1, likenum: 1})

image-20231016091924754

注意:

skip( ),limit( ),sort( ) 三个放在一起执行的时候,执行的顺序是先 sort( ),然后是 skip( ),最后是显示的 limit( ),和命令编写顺序无关。

文档的更多查询

正则的复杂条件查询

MongoDB的模糊查询是通过 正则表达式 的方式实现的。格式为:

db.collection.find({field:/正则表达式/})

提示:正则表达式是 js 的语法,直接量的写法。

示例: 查询评论内容包含 “开水” 的所有文档:

db.comment.find({content: /开水/})

查询评论内容中以 “专家” 开头的所有文档:

db.comment.find({content: /^专家/})

image-20231016092510869

比较查询

< , <= , > , >= 这个操作符很常用,格式如下:

db.集合名称.find({ "field" : { $gt: value }}) // 大于: field > value
db.集合名称.find({ "field" : { $lt: value }}) // 小于: field < value
db.集合名称.find({ "field" : { $gte: value }}) // 大于等于: field >= value
db.集合名称.find({ "field" : { $lte: value }}) // 小于等于: field <= value
db.集合名称.find({ "field" : { $ne: value }}) // 不等于: field != value

示例: 查询评论点赞数量大于 700 的记录

db.comment.find({likenum:{$gt: NumberInt(700)}})

包含查询

包含使用 $in 操作符。

示例: 查询评论的集合中 userid 字段包含 1003 或 1004 的文档

db.comment.find({userid: {$in: ["1003", "1004"]}})

image-20231016093238732

不包含使用 $nin 操作符。

示例: 查询评论集合中 userid 字段不包含 1003 和 1004 的文档

db.comment.find({userid: {$nin: ["1003", "1004"]}})

image-20231016093301932

条件连接查询

如果需要查询同时满足两个以上条件,需要使用 $and 操作符将条件进行关联。(相当于 SQL 的and),格式为:

db.collection.find($and: [{field:{}}, {field:{}}, {field:{}}])

示例:查询评论集合中 likenum 大于等于 700并且小于 2000的文档:

db.comment.find({$and: [
	{likenum: {$gte: NumberInt(700)}},
	{likenum: {$lt:NumberInt(2000)}}
]})

如果两个以上条件之间是或者的关系,可以使用操作符进行关联,与前面的 and 的使用方式相同,格式为:

db.collection.find({$or: [{field:{}}, {field:{}}, {field:{}}]})

示例:查询评论集合中 userid 为 1003,或者点赞数小于 1000 的文档记录:

db.comment.find({$or: [
	{userid: "1003"},
	{likenum: {$lt: 1000}}
]})

常用命令小结

  1. 选择切换数据库:

    use articledb
  2. 插入数据:

    db.comment.insert({
    	userid:"1001", 
    	nickname:"rose"
    	})
  3. 查询所有数据:

    db.comment.find()
  4. 条件查询数据:

    db.comment.find({
    	userid: "1001" 
    })
  5. 查询符合条件的第一条记录:

    db.comment.findOne({
    	userid: "1001"
    })
  6. 查询符合条件的前几条记录:

    db.comment.find({
    	userid: "1001"
    }).limit(2)
  7. 查询符合条件的跳过的记录:

    db.comment.find({
    	userid: "1001"
    }).skip(2)
  8. 修改数据:

    db.comment.update({条件}, {修改后的数据}) 
    或
    db.comment.update({userid}, {$set: {要修改部分的字段: 数据}})
  9. 修改数据并自增某字段

    db.comment.update(
    	{userid: "1001"}, {$inc: {likenum: NumberInt(1)}}
    )
  10. 删除数据

    db.comment.remove({userid: "1001"})
  11. 统计查询:

    db.comment.count({userid: "1001"})
  12. 模糊查询:

    db.comment.find({content: /专家/})
  13. 条件比较运算

    db.comment.find({likenum: {$gt: 100}})
  14. 包含查询:

    db.comment.find({
    	userid: {$in:["1001", "1002"]}
    })
    或:
    db.comment.find({
    	userid: {$nin: ["1003", "1004"]}
    })
  15. 条件连接查询:

    db.comment.find({
    	$and: [{userid: "1001"}, {nickname: "Rose"}]
    })
    或:
    db.comment.find({
    	$or: [{userid: "1001"}, {nickname: "Rose"}]
    })

索引-Index

概述

匹配的文档。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。

索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB还可以使用索引中的排序返回排序结果。

官网文档:https://docs.mongodb.com/manual/indexes/

了解:

MongoDB索引使用B树数据结构(确切的说是B-Tree,MySQL是B+Tree)

索引的类型

单字段索引

MonogDB支持在文档的单个字段上创建用户定义的升序/降序索引,称为单字段索引(Single Field Index)

对于单个字段索引和排序操作,索引键的排序顺序(即升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。

image-20231016151116470

复合索引

MongoDB还支持多个字段的用户定义索引,即复合索引(Compound Index)。

复合索引中列出的字段顺序具有重要意义。例如,如果符合索引由 {userid: 1, score: -1} 组成,则索引首先按 userid 正序排序,然后在每个 userid 值内,再在按 score 倒序排序。

image-20231016151439276

其他索引

地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(Hashed Indexes)。

地理空间索引(Geospatial Index):

为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面集合的二维索引和返回结果时使用球面集合的二维球面索引。

文本索引(Text Indexes):

MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如 “the”、“a”、“or”),而将集合中的词作为词干,只存储根词。

哈希索引(Hashed Indexes):

为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分部更加随机,但只支持相等匹配,不支持基于范围的查询。

索引的管理操作

索引的查看

说明:

返回一个集合中的索引的数组

语法:

db.collection.getIndexes()

提示: 该语法命令运行要求时 MongoDB 3.0+

示例:查看 comment 集合中所有的索引情况

db.comment.getIndexes()

结果为:

[
    {
        "v" : 2,
        "key" : {
        "_id" : 1
    },
        "name" : "_id_",
        "ns" : "articledb.comment"
    }
]

结果中显示的是默认 _id 索引。

image-20231016160043451

默认 _id 索引:

MonogDB在创建集合的过程中,在 _id 字段上创建一个唯一的索引,默认名字为 _id ,该索引可防止客户端插入两个具有相同值的文档,您不能在 _id 字段上删除此索引。

注意: 该索引是唯一索引,因此值不能重复,即 _id 值不能重复的。在分片集群中,通常使用 _id 作为片键。

索引的创建

说明:

在集合上创建索引:

语法:

db.collection.createIndex(keys, options)

参数:

Parameter Type Description
keys document 包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段上的升序索引,请指定值1;对于降序索引,请指定值-1。比如: {字段:1或-1} ,其中1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。另外,MongoDB 支持几种不同的索引
options document 可选。包含一组控制索引创建的选项的文档。有关详细信息,请参见选项详情列表

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 到 99999 之间,表示该索引相对于其他索引字段的得分权重
default_language String 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。默认为英语
language_override string 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的 language ,默认值为 language

注意:

在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex() ,之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。

示例:

  1. 单字段索引示例:对 userid 字段建立索引:

    db.comment.createIndex({userid: 1})

    image-20231016185101501

    参数1:按升序创建索引,索引名字为 userid

    image-20231016185233215

  2. 复合索引:对 useridnickname 同时建立复合(Compound)索引:

    db.comment.createIndex({userid: 1, nickname: -1})

    查看索引:

    image-20231016185600851

索引的移除

说明:可以移出指定的索引,或移出所有索引

一、指定索引的移除

语法:

db.collection.dropIndex(index)

参数:

Parameter Type Description
index String or document 指定要删除的索引。可以通过索引名称或索引规范文档指定索引。若要删除文本索引,请指定索引名称

示例: 删除 comment 集合中 userid 字段上的上升索引:

db.comment.dropIndex({userid: 1})

查看已经删除了

二、所有索引的移除

语法:

db.collection.dropIndexes()

示例: 删除 spit 集合中所有的索引:

db.comment.dropIndexes()

注意: _id 的字段是无法删除的,只能删除非 _id 字段的索引。

索引的使用

执行计划

分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。

那么,通常,我们想知道,建立的索引是否有效,效果如何,都需要通过执行计划查看。

语法:

db.collection.find(query, options).explain(options)

示例: 查看根据 userid 查询数据的情况:

db.comment.find({userid: "1003"}).explain()

image-20231017081632400

关键看:"stage": "COLLSCAN", 表示全集合扫描

"stage": "INSCAN", 表示索引扫描

涵盖的查询

Covered Queries

当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。这些覆盖的查询可以非常有效。

image-20231017082153190

更多规则信息查看:https://www.mongodb.com/docs/manual/core/query-optimization/#read-operations-covered-query

示例:

db.comment.find({userid: "1003"}, {userid: 1, _id: 0})

查出的是:

image-20231017082758758

db.comment.find({userid: "1003"}, {userid: 1, _id: 0}).explain()

image-20231017083516750

类型题

  1. 进入 my_test 数据库

    use my_test
  2. 向数据库的user集合中插入一个文档

    db.user.insert({
        username:"sunwukong"
    })
  3. 查询user集合中的文档

    db.user.find()
  4. 向数据库的user集合中插入一个文档

    db.users.insert({
        username:"zhubajie"
    })
  5. 查询数据库user集合中的文档

    db.user.find()
  6. 统计数据库user集合中的文档数量

    db.user.find().count()
  7. 查询数据库user集合中的username为sunwukong的文档

    db.user.find({
        username:"sunwukong"
    })
  8. 向数据库user集合中的username为sunwukong的文档,添加一个 address 属性,属性值为 huaguoshan

    db.user.update({username:"sunwukong"}, {$set:{address:"huaguoshan"}})
  9. 使用 {username: “tangseng”} 替换 username 为zhubajie 的文档

    db.user.relaceOne({username:"zhubajie"}, {username:"tangseng"}})
  10. 删除username为sunwukong的文档的address属性

    db.user.update({username:"sunwukong"}, {$unset:{address:1}})
  11. 向username为sunwukong的文档中,添加一个 hobby:cities:["bwijing", "shanghai", "shenzhen"]

    MongoDB的文档属性也可以是一个文档,当一个文档的属性值是一个文档时,我们称这个文档为 “内嵌文档”

    db.user.update(
        {username:"sunwukong"}, 
        {$set: {hobby: {cities:["beijing","shanghai","shenzhen"]}}}
    )
  12. 向username为tangseng的文档中,添加一个hobby: movies:["A Chinese Odyssey", "King of comedy"]

    db.user.update(
    	{username:"tangseng"},
        {$set: {hobby: {movies: ["A Chinese Odyssey", "King of comedy"]}}}
    )
  13. 查询喜欢电影 hero 的文档

    MongoDB支持直接通过内嵌文档的属性进行查询,如果要查询内嵌文档则可以通过 . 的形式来匹配

    如果要通过内嵌文档来对文档进行查询,此时属性名必须使用引号 即:"hobby.movies": "hero"

    db.user.find({
        "hobby.movies": "hero"
    })
  14. 向 tangseng 中添加一个新的电影 Interstellar

    $push 用于向数组中添加一个新的元素

    $addToTest 用于向数组中添加一个新元素,如果数组中已经存在了该元素,则不会添加

    db.user.update(
        {user:"tangseng"},
    	{$push: {"hobby.movies": "Interstellar"}}
    )
  15. 删除喜欢 beijing 的用户

    db.user.remove({"hobby.cities": "beijing"})
  16. 删除user集合

    db.user.drop()

    db.user.remove({})
  17. 向 numbers 中插入 20000条数据

    //效率低
    for(var i = 1; i <= 20000; i++){
        db.numbers.insert({num: i});
    }
    //效率高
    var arr=[];
    for(var i = 1; i <= 20000; i++){
        arr.push({num: i});
    }
    db.numbers.insert(arr);
  18. 查询 numbers 中 num 为500的文档

    db.numbers.find({
        num: 500
    })
  19. 查询 numbers 中 num 大于 5000 的文档

    db.numbers.find({
        num: {$gt:500}
    })
  20. 查询 numbers 中 num 小于 30 的文档

    db.numbers.find({
        num: {$lt: 30}
    })
  21. 查询 numbers 中 num 大于 40 小于 50 的文档

    db.numbers.find({
        num: {$gt: 40, $lt: 50}
    })
  22. 查询 numbers 中 num 大于 19996 的文档

    db.numbers.find({
        num: {$gt: 19996}
    })
  23. 查看 numbers 集合中的前 10 条数据

    db.numbers.find({
        num: {$lte: 10}
    })

    或者

    db.numbers.find({}).limit(10)
  24. 查看 numbers 集合中的第 11 条到 20 条数据

    db.numebers.find({}).limit(10).skip(10)
  25. 查看 numbers 集合中的第 21 条到 30 条数据

    db.numbers.find({}).limit(10).skip(20)
  26. 将 dept 和 emp 集合导入到数据库中

    点import然后导入

  27. 查询工资小于 2000 的员工

    db.emp.find({
        sal: {$lt: 2000, $gt: 1000}
    })
  28. 查询工资在 1000 ~ 2000 之间的员工

    db.emp.find({
        sal: {$lt: 2000, $gt: 1000}
    })
  29. 查询工资小于1000或大于2500的员工

    db.emp.find({$or: [{sal: {$lt: 1000}}, {sal: {$gt: 2000}}]})
  30. 查询财务部的所有员工

    var depno = db.dept.findOne({dname: "财务部"}).deptno;
    db.emp.find({depno: depno})
  31. 查询销售部的所有员工

    var depno = db.dept.findOne({dname: "销售部"}).deptno;
    db.emp.find({depno: depno})
  32. 查询所有 mgr 为 7698 的所有员工

    db.emp.find({
        mgr: 7698
    })
  33. 查询所有 mgr 为 1000 的员工增加工资 400 元

    db.emp.updateMany({sal: {$lte: 1000}}, {$inc: {sal: 400}})