第5章〓文档数据库MongoDB的原理与应用 5.1MongoDB简介 5.1.1概述 MongoDB是一个文档数据库,由C++语言编写。它将数据存储为类似于JSON的文档,数据结构由键值(keyvalue)对组成,可以存储比较复杂的数据类型,字段值除支持基础的数字、字符串、布尔值外,还支持数组、文档子对象等。 【名词概念解释】 键值对: 键(Key)通常又被称为关键字,每个关键字只出现一次; 值(Value)则为该关键字对应的值(可以为单一数值,也可为数组、对象等),这样就构成了一对数值关系,从而被称为键值对。若读者仍不了解,可学习哈希表这一数据结构。 JSON: 可简单理解为由若干键值对组成的字符串。 文档: 类似于JSON对象,也可以存储若干键值对。 5.1.2特点 MongoDB的特点为高性能、易部署、易使用、存储数据非常方便。其主要特点有以下6点。 (1) 数据类型丰富: MongoDB将数据存储为BSON(一种JSON的扩展)文档,与传统关系数据库中由行和列组成的表相比,BSON支持更丰富、更灵活的数据结构。除支持基础的数字、字符串、布尔值等字段类型外,BSON字段还可以是数组或嵌套子对象。 (2) 结构灵活: MongoDB无须像关系数据库一样提前声明文档结构,字段类型可以灵活变化,同时也不存在模式,用户可以灵活地插入不同类型的文档等。 (3) 性能优越: MongoDB支持完全索引,包括内部对象,即可以直接对文档中的任何对象进行索引检索。同时实验证明,对于大量数据,索引字段查询不会比关系数据库(如MySQL)查询慢,非索引字段查询相对于关系数据库则全面胜出。 (4) 可扩展性强: 文档被视为单个单元,这意味着它可以分布在多个服务器上,因此可以轻松地扩展,实现分布式的数据分布。 (5) 地理位置索引: MongoDB除支持传统关系数据库中的索引外,还支持特有的地理位置索引。 (6) 支持多种编程语言: 支持Java、C++、Python、C#等多种编程语言。 5.1.3发展历程 2007年10月, Dwight Eliot和Kevin Ryan一同创办了名为10gen的公司,在研发过程中,他们每次都在解决同样的数据水平扩展性问题。于是 MongoDB在此背景下诞生,通过自动分片构建大型MongoDB集群,从而实现水平伸缩。2009年2月,MongoDB首次在数据库领域亮相,至此MongoDB 1.0发布。 2012年5月,MongoDB 2.1开发分支发布。该版本采用全新架构,包含诸多功能增强。 2012年6月,MongoDB 2.0.6 发布,分布式文档数据库诞生。 2013年4月,MongoDB 2.4.3 发布,此版本包括一些性能优化、功能增强以及bug修复。 2015年3月,MongoDB 3.0发布,包含新的存储引擎,大幅提升了MongoDB的写入性能。 2017年10月,MongoDB公司成立10周年之际,顺利上市,成为26年来第一家以数据库产品为主要业务的上市公司。 2018年6月,MongoDB 4.0发布,提供跨文档事务处理能力。 2019年8月,MongoDB 4.2发布,开始支持分布式事务。 至今,MongoDB已经从一个在数据库领域籍籍无名的“小透明”,变成了话题度和热度都很高的“流量”数据库。MongoDB数据库平台下载量超过1.25亿次,MongoDB客户遍布全球100多个国家和地区,其中思科使用MongoDB取代Oracle重构电商平台,字节跳动也将部分MySQL应用迁移到MongoDB上来,如与地理相关的查询、游戏运营日志等业务。 【名词概念解释】 水平扩展性: 连接多个软硬件特性,从而将多个服务器从逻辑上看成一个整体,进而提高性能。水平扩展性最大的优点是不需要单一机器具有极高的性能。 垂直扩展性: 通过对单一物理实体增加资源而提高性能。 5.1.4应用场景 基于MongoDB支持的数据类型丰富、结构灵活、性能优越和可扩展性强等特点,MongoDB具体在以下6个领域得以应用。 (1) 游戏应用: 游戏应用需求灵活多变,MongoDB结构灵活、可扩展性强等特点能够很好地解决游戏应用中的痛点。可以使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新。 (2) 移动应用: MongoDB 支持二维空间索引,可以高效地查询地理位置关系和检索用户地理位置数据,很好地支撑基于地理位置查询的移动类App的业务需求。同时,MongoDB 动态模式存储方式非常适合存储多重系统的异构数据,满足移动 App 应用的需求。 (3) 电商应用: 对于商品信息、订单信息,借助MongoDB支持的数据类型丰富等特点,通过嵌套的子文档,一次简单查询就能获取所需的信息。例如,上衣和裤子有相同的属性,如产地、价格、材质、颜色等; 还有各自独有的属性,如上衣独有的肩宽、胸围、袖长等,裤子独有的臀围、脚口、长度等。独有属性可以直接以BSON子文档方式嵌套到商品这个文档中,一次查询直接获取全部内容,不需要进行多表join查询。 (4) 物流应用: 在电商配套的物流领域,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。可以将一个快递的物流信息直接嵌套在以商品Id为唯一索引的文档中,一次查询就可以获取完整的快递流向信息。 (5) 视频直播: 视频直播行业会产生大量的礼物信息、用户聊天信息等,数据量较大。MongoDB水平可扩展的优点能够很好地解决上述问题。 (6) 社交应用: 使用 MongoDB 存储用户信息,以及用户发表的朋友圈信息,通过MongoDB特有的地理位置索引特点,实现附近的人、地点等功能。 5.2MongoDB的相关基本概念 关系数据库中涉及数据库、表、行、列等基本概念,MongoDB中同样涉及相关的概念,但是却存在相应的差异。为便于理解,下面将对比关系数据库的基本概念,对MongoDB中相关的基本概念进行介绍。MongoDB数据库与关系数据库相关概念的对比如表51所示。 表51MongoDB数据库与关系数据库相关概念的对比 关系数据库MongoDB数据库 数据库(Database)数据库(Database) 表(Table)集合(Collection) 行(Row)文档(Document) 列(Column)字段(Field) 5.2.1命名规则 MongoDB关于数据库名、文档名、字段键名等存在统一的命令规则,同时也存在一些不同。MongoDB统一命名规则有以下2点: (1) 不能为空字符串。 (2) 不得有空格、$、\和\0(空字符)(其中,空字符表示结尾除外)。 同时,还存在特殊的命名规则,具体为以下4点: (1) 数据库名和文档键中不得有点('.'),只能在特定环境中使用。 (2) 数据库名中的字母必须全为小写字母。 (3) 集合名不能以“system.”开头,因为这是为系统集合保留的前缀,同时用户创建的集合名中不能含有保留字符。 (4) 以下画线“_”开头的键为系统保留的键。 5.2.2数据库 MongoDB中的库类似于传统关系数据库中库的概念,用不同的库来隔离不同的应用数据。MongoDB中可以建立多个数据库,每个数据库都有自己的集合和权限,不同的数据库放置在不同的文件中。在操作过程中,若不指定数据库,则MongoDB默认操作的数据库为test。 1. MongoDB默认数据库 MongoDB中默认包含admin、local、config三个数据库,可以直接访问。这三个数据库的含义如下。 (1) admin: 从权限角度看,可理解为root数据库。若将一个用户添加至该数据库,则此用户自动继承所有数据库的权限。一些特定的服务器端命令只能通过该数据库运行,例如列出所有的数据库或关闭服务器。 (2) local: 该数据库永远不会被复制,可用来存储限于本地单服务器的任何集合。 (3) config: 当MongoDB用于分片设置时,该数据库在内部使用,用来保存分片相关信息。 5.2.3集合 集合就是MongoDB的文档组,类似于关系数据库管理系统中的表格。集合存在于数据库中,同时集合没有固定的结构,这意味着可以对集合插入不同格式和类型的数据,但通常情况下插入集合的数据都会存在一定的关联性。 使用MongoDB可以自定义集合,当第一个文档插入时,集合就会被创建。 5.2.4文档 文档是若干键值对(BSON)。MongoDB的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与传统关系数据库存在很大的区别,也是MongoDB非常突出的特点之一。 一个简单的文档例子为: {"name": "小明","age": 21,"major": "计算机科学与技术"}。 文档中还存在以下5点注意事项: (1) 文档中的键值对绝对是有序的。 (2) 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。 (3) 区分数据类型和大小写。 (4) 文档中不能出现重复的键。 (5) 文档中的键是字符串。除少数例外情况外,键可以使用任意UTF8字符。 5.2.5MongoDB的数据类型 MongoDB的主要数据类型如表52所示。 表52MongoDB的主要数据类型 数 据 类 型描述 String字符串。存储数据常用的数据类型。在MongoDB中,只有UTF8编码的字符串才是合法的 Integer整型数值。根据采用的服务器,可分为32位或64位 Boolean布尔值。用于存储布尔值(真/假) Double双精度浮点数。MongoDB中默认浮点数存储格式为双精度浮点数 Array用于将数组、列表或多个值存储为一个键或值 Object用于内嵌文档 Null用于创建空值 Date日期时间。可以指定日期时间: 创建Date对象,传入年、月、日信息 ObjectId对象ID。用于创建文档的ID 下面重点说明ObjectId,ObjectId类似于唯一主键,可以很快地生成和排序,包含12字节,分别表示的含义如下: (1) 前4字节表示创建时间戳,默认为格林尼治时间(UTC时间)。 (2) 接下来3字节是机器标识码。 (3) 紧接着2字节表示PID(由进程ID组成)。 (4) 最后3字节是随机数。 ObjectId各字节的具体含义如图51所示。 图51ObjectId各字节的具体含义 MongoDB中存储的文档必须有一个_id键。这个键的值可以是任何类型,若不特殊指定,则默认为一个ObjectId对象,例如"_id":ObjectId("620e07e539f098ae9e114fd3")。 5.3MongoDB的安装和配置方法 为便于读者理解与掌握,本节将引入一个具体实例(情节均为虚构),以实例驱动,讲解MongoDB的安装与具体的使用细节。 目前,已有大部分有志青年放弃大城市的高薪工作,开始服务于农村家乡,借助互联网平台,以亲身经历教会父老乡亲出售本地农产品特产,提高家乡人民的生活水平和生活质量。某高校计算机专业的学生小波作为一名资深的“剁手党”,经常在各大电商平台购物,也希望能够通过专业知识为农村发展贡献自己的微薄之力,他深知传统关系数据库对数据有着严格的规范,如建表前需要严格指定数据类型等,这对初创公司不太友好。 而MongoDB以文档结构存储数据,面向集合存储,易存储对象类型的数据。同时,MongoDB支持快速查询以及动态查询,能够高效地获取数据信息,且可扩展性较强,方便后续加大数据规模,并且商品信息、订单信息的事务性要求也不是很高,使用MongoDB搭建农产品电商数据库(agr_products_ecommerce_db)当然是一个不错的选择。于是小波开始发挥他的专业特长,着手设计农产品电商数据库,希望未来能够有机会服务于农村、农民。 数据库设计的第一步是下载MongoDB数据库。MongoDB支持主流操作系统,包括Windows、macOS、Linux等。用户只需存在相应的存储空间,便可下载并安装使用,对软硬件的要求极低。 MongoDB官方网站(网址详见前言二维码)提供了很多版本,包括企业版、社区版等。为了便于理解与使用,本节所有操作均在Windows操作系统的社区版下进行(社区版下载网址详见前言二维码)。编写时,MongoDB社区版已更新至5.0.6版本。前面的章节已经介绍了基于Docker方式安装的优点,本节除介绍传统方式安装配置外,还将介绍基于Docker方式的安装配置方法。 5.3.1传统方式的安装与配置 下载相应版本的安装包,直接双击安装包,即可进行具体的安装操作。操作过程较简单,可以单击Custom按钮,设置安装目录。自定义设置安装目录,如图52所示。 图52自定义设置安装目录 选择安装路径,如图53所示。 图53选择安装路径 随后会显示数据库Data目录和Log目录的位置,默认为安装目录下的data和log子文件夹。选择Data和Log目录安装路径,如图54所示。 图54选择Data和Log目录安装路径 接下来会提示是否安装MongoDB Compass工具(安装过程可能较慢,后续也可在MongoDB官方网站自行下载,离线安装)。MongoDB Compass是一个图形化界面管理工具,后续将具体介绍。安装MongoDB Compass工具,如图55所示。 图55安装MongoDB Compass工具 安装成功后,根目录下存在bin、data和log三个子文件夹。 (1) bin文件夹: 存放MongoDB相关工具组件,如mongod.exe(服务器应用程序)、mongo.exe(客户端应用程序)等。 (2) data文件夹: 存储MongoDB数据信息。 (3) log文件夹: 存储MongoDB日志信息。 在bin文件夹中存在mongod.cfg文件,用来存储MongoDB配置信息,默认有storage(数据存储信息)、systemLog(系统日志信息)和net(默认IP和端口信息),具体信息如下。 (1) storage: 默认数据库存储位置为data文件夹。 (2) systemLog: 系统日志信息默认存储至log文件夹的mongod.log文件中,且以追加的方式写入日志信息。 (3) net: 默认IP为127.0.0.1,默认端口为27017。 读者可以根据自己的需要修改mongod.cfg文件的相应配置信息,也可以在命令行修改当前连接的配置信息,具体后续将进行介绍。 5.3.2基于Docker方式的安装与配置 Windows Docker的安装在第3章已详细介绍。本小节将介绍Docker方式的MongoDB的安装与配置。 首先拉取MongoDB镜像。笔者撰写本书时MongoDB社区版已更新至5.0.6版本,于是拉取MongoDB镜像的具体命令为: docker pull mongo:5.0.6 安装完成后,输入docker images指令即可查看Docker中的所有镜像信息,并验证MongoDB是否拉取成功。查看Docker本地镜像信息,如图56所示。 图56查看Docker本地镜像信息 5.3.3MongoDB的连接 MongoDB分为服务端和客户端,只有启动MongoDB服务端后,客户端才可连接MongoDB,进一步进行数据库、集合和文档的操作。 1. MongoDB服务启动 基于传统方式安装的MongoDB,启动服务主要有以下3种方法: (1) 在Windows任务管理器中的服务栏,找到MongoDB对应的服务打开即可(默认为开机自动打开)。 (2) 在bin目录路径下打开cmd(命令提示符)窗口,直接输入mongod命令也可打开MongoDB服务,当然也可输入其他配置信息,如mongod dbpath=/data port=27017(自定义数据库目录和端口信息)。 (3) 输入net start mongodb命令,由于安装过程中MongoDB已加入Windows服务中,因此可直接在cmd窗口输入net start mongodb命令打开服务。 基于Docker方式安装的MongoDB启动服务较为简单,具体命令如下: docker run -d --name mongo -p 27017:27017 mongo:5.0.6 【参数说明】 d: 表示后台运行。 name: 为容器分配一个名称。 p: 向主机发布容器端口集合。 2. MongoDB客户端连接 基于传统方式安装的MongoDB,客户端连接方法主要有以下2种: (1) 直接运行mongo.exe。 (2) 在bin文件夹下打开cmd窗口输入mongo命令(也可指定连接的配置信息,如端口、日志信息等)。 基于Docker方式安装的MongoDB,客户端连接的具体命令如下: docker exec -it mongo mongo 【参数说明】 第一个mongo表示容器名,第二个mongo表示镜像名。 5.4MongoDB的基本操作 5.4.1数据库的创建 通过5.3节的学习,小波已成功安装MongoDB。接下来开始创建农产品电商数据库(agr_products_ecommerce_db)。MongoDB创建数据库的语法格式为: use database_name(数据库名)。其中,use代表创建并使用,若数据库不存在,则自动创建数据库。 小波已迫不及待开始操作。创建农产品电商数据库(agr_products_ecommerce_db),代码如下: > use agr_prducts_ecommerce_db switched to db agr_prducts_ecommerce_db 5.4.2数据库的查询与删除 1. 数据库的查询 数据库查询指令主要有2种,分别为show dbs和db。 1) show dbs指令 查询MongoDB中的所有数据库,但若数据库为空数据库,则不会被查询显示。 小波小试牛刀,想要证实刚刚创建的农产品电商数据库(agr_products_ecommerce_db)的确没有任何数据信息。查询所有数据库,如图57所示。因为刚刚创建的农产品电商数据库没有数据,是空数据库,所以并未显示。 2) db指令 查询当前所在数据库,如图58所示。可以发现,此时所在数据库的确为小波刚刚创建的农产品电商数据库(agr_products_ecommerce_db)。 图57查看所有数据库 图58查询当前所在数据库 2. 数据库的删除 数据库删除指令为db.dropDatabase(),表示删除当前所在的数据库。例如,删除agr_products_ecommerce_db数据库,如例51所示。 【例51】删除agr_products_ecommerce_db数据库 > use agr_products_ecommerce_db switched to db agr_products_ecommerce_db > db.dropDatabase(); {"ok": 1} 5.4.3集合的创建 相比于传统关系数据库中的库由若干表组成,MongoDB数据库由若干集合组成。 集合创建指令为db.createCollection(name,options)。具体参数说明如下。 name: 必选参数,创建集合的名称。 options: 可选参数,指定有关内存大小及索引。 options参数表示如表53所示。 表53options参数表示 字段类型描述 capped布尔如果该值为true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。该值为true时,必须指定size参数 size数值为固定集合指定一个最大值,即字节数 max数值指定固定集合中包含文档的最大数量 小波开始设计农产品电商数据库(agr_products_ecommerce_db)所需的集合结构,其中不可或缺的便是商品集合(products)。创建包含文档最大数量为100、集合空间最大值为5000字节的products集合,如例52所示。 【例52】创建包含文档最大数量为100、集合空间最大值为5000字节的products集合 > db.createCollection("products",{max:100,capped:true,size:5000}); {"ok": 1} 当然,MongoDB中也可以不需要创建集合。当你插入一些文档时,MongoDB会自动创建集合,读者可自行操作。 5.4.4集合的查询与删除 1. 集合的查询 集合查询指令为show collections或show tables。查询当前数据库(agr_products_commerce_db)的所有集合,如例53所示。 【例53】查询当前数据库(agr_products_commerce_db)的所有集合 > show collections; products > show tables; products 2. 集合的删除 集合删除指令为db.collection.drop()。具体参数说明如下。 collection表示需要删除的集合名。若成功删除选定集合,则drop()方法返回true,否则返回false。 例如,删除products集合,如例54所示。 【例54】删除products集合 > db.products.drop(); true 5.4.5文档添加 MongoDB为文档数据库,由一个个文档组成,所以文档的操作为MongoDB的核心,具体可分为文档插入指令、文档查询指令、文档更新指令、文档删除指令。本小节将重点介绍文档的添加操作。 1. MongoDB的文档格式 添加文档之前,需要简单了解文档的格式。MongoDB的文档为BSON格式(一种类似于JSON的格式),由若干键值对组成。其中key、value均可为任何数据类型格式,包括文档子对象。MongoDB文档示例如例55所示。 【例55】MongoDB文档示例 { "_id": ObjectId("62d6c1443064879e0bad6d86"), "product_name": "西红柿", "variety_name": "普罗旺斯番茄", "producing_area": "山东省海阳市", "price": 3, "unit": "斤", "shipping_address": "山东省海阳市" } 2. MongoDB文档添加操作 简单导入农产品信息,通过命令行有两种插入数据形式,分别为insert(插入单个文档)和insertMany(插入多个文档)。插入单个文档如例56所示。 【例56】插入单个文档 > db.products.insert({ product_name: "胡萝卜", variety_name: "秤杆红萝卜", producing_area: "陕西省渭南市大荔县", leaf_length: "15cm以上", price: 0, unit: "斤", shipping_address: "陕西省渭南市大荔县" }) WriteResult({ "nInserted" : 1 }) 插入多个文档如例57所示。除使用insertMany指令插入多个文档外,也可通过文件形式导入多个文档内容,具体操作将在5.7.2节进行介绍。 【例57】插入多个文档 db.products.insertMany( [{ product_name: "菠菜", variety_name: "大叶菠菜", producing_area: "山东省聊城市", leaf_length: "25-30cm", price: 0.6, unit: "斤", shipping_address: "山东省聊城市东昌府区" }, { product_name: "山药", variety_name: "河北铁棍山药", producing_area: "河北省保定市", specifications: { "家庭实惠装-长30-40cm-5斤装 二级,5.00斤/箱": 24, "优良装.长45-50cm.5斤装 二级,5.00斤/箱": 26, "优选装-长55-60cm-5斤装 一级,5.00斤/箱": 28, "精选装-长65-70cm-5斤装 一级,5.00斤/箱": 30 }, unit: "斤", shipping_address: "河北省保定市蠡县" }]); { "acknowledged" : true, "insertedIds" : [ ObjectId("62d6c4d22fca6e0caacdefca"), ObjectId("62d6c4d22fca6e0caacdefcb") ] } 3. 文档添加指令详细介绍 文档添加指令为db.collection.insert()或db.collection.insertMany()。其中collection表示插入文档所在的集合名。 在MongoDB中,每个文档都会有一个_id作为唯一标识(类似于传统关系数据库的主键),_id默认会自动生成,由12字节组成,具体含义5.2.4小节已介绍。若手动指定_id的值,则需要确保当前集合中没有出现过此_id,否则会报错。 5.4.6文档查询 1. 文档查询概述 在数据库操作中,查询操作最为普遍,而MongoDB为文档数据库,对于文档的查询更加常见。具体指令为db.collection.find(query,projection)。其中collection表示查询的集合名。 参数说明如下。 query: 可选,使用查询操作符指定查询条件。 projection: 可选,使用投影操作符指定返回的键。若想要查询所有的键值,则只需省略该参数即可(默认省略)。 如果需要以易读的方式读取数据,可以使用pretty()方法,语法格式为db.collection.find().pretty()。例如,查询农产品电商数据库中products集合的所有数据,如例58所示。 【例58】查询农产品电商数据库中products集合的所有数据 > db.products.find().pretty(); { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "specifications" : { "家庭实惠装-长30-40cm-5斤装 二级,5.00斤/箱" : 24, "优良装.长45-50cm.5斤装 二级,5.00斤/箱" : 26, "优选装-长55-60cm-5斤装 一级,5.00斤/箱" : 28, "精选装-长65-70cm-5斤装 一级,5.00斤/箱" : 30 }, "unit" : "斤", "shipping_address" : "河北省保定市蠡县" } MongoDB和传统关系数据库一样,支持的查询操作相当丰富,例如条件查询、索引、聚合、排序、AND、OR、模糊查询等。MongoDB文档查询类型如表54所示。接下来将对各个查询类型分别进行介绍。 表54MongoDB文档查询类型 文档查询类型说明 条件查询条件判断查询,类似于传统关系数据库的where语句 AND、OR查询与传统关系数据库中的AND、OR查询类似 $type查询通过数据类型查询 排序查询使用sort()方法对查询结果指定排序顺序 分页查询使用limit()和skip()方法实现分页查询 索引查询建立索引,并通过索引查询 聚合查询主要用于处理数据(如统计平均值、求和、最值等) 其他查询如数组中的查询、模糊查询、去重、指定返回字段等 2. 条件查询 条件操作符即为条件判断,类似于关系数据库中的where语句。首先将MongoDB数据库中的各种条件操作符与相同含义的where语句进行对比,便于进一步理解,如表55所示。 表55MongoDB条件操作符与传统关系数据库where语句对比 操作格式示例关系数据库中类似语句 等于{<key>:<value>}db.products.find({"product_name":"菠菜"})where product_name ="菠菜" 小于{<key>:{$lt:<value>}}db.products.find({"price":{$lt:0.6}})where price < 0.6 小于或等于{<key>:{$lte:<value>}}db.products.find({"price":{$lte:0.6}})where price <= 0.6 大于{<key>:{$gt:<value>}}db.products.find({"price":{$gt:0.6}})where price > 0.6 大于或等于{<key>:{$gte:<value>}}db.products.find({"price":{$gte:0.6}})where price >= 0.6 不等于{<key>:{$ne:<value>}}db.products.find({"price":{$ne:0.6}})where price != 0.6 在此将对农产品电商数据库中的products集合进行查询,以便更好地理解与掌握条件操作符。查询products集合中product_name为“菠菜”的数据,如例59所示。 【例59】查询products集合中product_name为“菠菜”的数据 > db.products.find({"product_name": "菠菜"}).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 查询products集合中price小于0.6的数据,如例510所示。 【例510】查询products集合中price小于0.6的数据 > db.products.find({"price":{$lt:0.6}}).pretty(); { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } 查询products集合中price小于或等于0.6的数据,如例511所示。 【例511】查询products集合中price小于或等于0.6的数据 > db.products.find({"price":{$lte:0.6}}).pretty(); { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 查询products集合中price大于0.6的数据,如例512所示。 【例512】查询products集合中price大于0.6的数据 > db.products.find({"price":{$gt:0.6}}).pretty(); { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } 查询products集合中price大于或等于0.6的数据,如例513所示。 【例513】查询products集合中price大于或等于0.6的数据 > db.products.find({"price":{$gte:0.6}}).pretty(); { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 查询products集合中price不等于0.6的数据,如例514所示。 【例514】查询products集合中price不等于0.6的数据 > db.products.find({"price":{$ne:0.6}}).pretty(); { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "specifications" : { "家庭实惠装-长30-40cm-5斤装 二级,5.00斤/箱" : 24, "优良装.长45-50cm.5斤装 二级,5.00斤/箱" : 26, "优选装-长55-60cm-5斤装 一级,5.00斤/箱" : 28, "精选装-长65-70cm-5斤装 一级,5.00斤/箱" : 30 }, "unit" : "斤", "shipping_address" : "河北省保定市蠡县" } 3. AND、OR查询 MongoDB中的find()方法支持传入多个键,每个键以逗号隔开,即为关系数据库中的AND条件。语法格式为db.collection.find({key1:value1,key2:value2})。 同样以农产品电商数据库中的products集合操作为例,查询products集合中price大于或等于0.6且producing_area(产地)为“山东省聊城市”的数据,如例515所示。 【例515】查询products集合中price大于或等于0.6且producing_area(产地)为“山东省聊城市”的数据 > db.products.find({"price": {$gte: 0.6},"producing_area":"山东聊城"}).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 但是注意,如果key相同,则后面的条件会把前面的条件覆盖。若想要实现传统关系数据库中类似于OR的查询,则需要使用OR操作符,具体语法格式为db.collection.find({$or:[{key1:value1},{key2:value2}]})。查询products集合中product_name为“菠菜”或price等于0.5的数据,如例516所示。 【例516】查询products集合中product_name为“菠菜”或price等于0.5的数据 > db.products.find({$or:[{"product_name":"菠菜"},{"price":0.5}]}).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 同时也可实现AND和OR的联合查询,读者可自行选择数据进行实验。 3. $type查询 $type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。$type查询可使用的类型如表56所示。 表56$type查询可使用的类型 类型数字备注 Double1 String2 Object3 Array4 Binary data5 Undefined6已废弃 Object id7 Boolean8 Date9 Null10 Regular Expression11 JavaScript13 Symbol14 JavaScript(with scope)15 32bit Integer16 Timestamp17 64bit Integer18 Min key255Query with 1 Max key127 查询products集合中product_name为String的数据,如例517所示。 【例517】查询products集合中product_name为String的数据 > db.products.find({"product_name":{$type:'string'}}).pretty(); { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "specifications" : { "家庭实惠装-长30-40cm-5斤装 二级,5.00斤/箱" : 24, "优良装.长45-50cm.5斤装 二级,5.00斤/箱" : 26, "优选装-长55-60cm-5斤装 一级,5.00斤/箱" : 28, "精选装-长65-70cm-5斤装 一级,5.00斤/箱" : 30 }, "unit" : "斤", "shipping_address" : "河北省保定市蠡县" } 4. 排序查询 MongoDB中可使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1指定排序的方式,其中1为升序排列,而-1为降序排列。sort()方法的基本语法为db.collection.find().sort({key:1})。products集合中数据按字段price升序排序,如例518所示。 【例518】products集合中数据按字段price升序排序 > db.products.find().sort({"price":1}).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "specifications" : { "家庭实惠装-长30-40cm-5斤装 二级,5.00斤/箱" : 24, "优良装.长45-50cm.5斤装 二级,5.00斤/箱" : 26, "优选装-长55-60cm-5斤装 一级,5.00斤/箱" : 28, "精选装-长65-70cm-5斤装 一级,5.00斤/箱" : 30 }, "unit" : "斤", "shipping_address" : "河北省保定市蠡县" } { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } 5. 分页查询 若需要读取指定数量的数据记录,MongoDB提供了limit()方法,limit()方法的基本语法为db.collection.find().limit(number)。 参数说明如下: number: 指定从MongoDB中读取的记录条数。 MongoDB除提供了limit()方法读取指定数量的数据外,还支持skip()方法来跳过指定数量的数据,skip()方法的基本语法为db.colletion.find().limit(number1).skip(number2)。 参数说明如下。 number1: limit()方法中的参数。 number2: 表示跳过的记录条数。 结合limit()和skip()方法即可实现传统关系数据库中的分页查询。例如,只显示products集合中的第3条数据,如例519所示。 【例519】只显示products集合中的第3条数据 > db.products.find().limit(1).skip(2).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 6. 索引查询 索引通常能够极大地提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询需要花费几十秒甚至几分钟,这对网站的性能是非常致命的。 索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。从根本上说,MongoDB中的索引与其他数据库系统中的索引类似。MongoDB在集合层面上定义了索引,并支持对MongoDB集合中的任何字段或文档的子字段进行索引。MongoDB提供了createIndex()方法来创建索引。其基本语法格式为db.collection.createIndex(key,options)。 参数说明如下。 key: 表示需要创建的索引字段(1为指定按升序创建索引,-1为指定按降序创建索引)。 options: 可选参数。 options可选参数列表,如表57所示。 表57options可选参数列表 参数类型说明 backgroundBoolean建索引过程会阻塞其他数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。"background" 默认值为false uniqueBoolean建立的索引是否唯一。指定为true可创建唯一索引。默认值为false nameString索引的名称。如果未指定,MongoDB通过连接索引的字段名和排序顺序生成一个索引名称 dropDupsBoolean3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定为true可创建唯一索引。默认值为 false sparseBoolean对文档中不存在的字段数据不启用索引。这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档。默认值为 false expireAfterSecondsInteger指定一个以秒为单位的数值,完成 TTL(Time To Live,生存时间)设定,设定集合的生存时间 vIndex Version索引的版本号。默认的索引版本取决于MongoDB创建索引时运行的版本 weightsDocument索引权重值,数值的取值范围为1~99999,表示该索引相对于其他索引字段的得分权重 default_languageString对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 language_overrideString对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language 相关操作有如下4点。 (1) 查看集合所有索引: db.collection.getIndexes()。 (2) 查看集合索引大小: db.collection.totalIndexSize()。 (3) 删除集合所有索引: db.collection.dropIndexes()。 (4) 删除集合指定索引: db.collection.dropIndex("索引名称")。 索引还存在更加复杂的操作,如复合索引等。若读者感兴趣,可自行查阅相关资料。 7. 聚合查询 MongoDB中的聚合(Aggregate)主要用于处理数据(如统计平均值、求和等),并返回计算后的数据结果,类似于传统关系数据库中的count(*)语句。常见的聚合表达式如表58所示。 表58常见的聚合表达式 表达式说明 $sum计算总和 $avg计算平均值 $min获取集合中所有文档对应值的最小值 $max获取集合中所有文档对应值的最大值 $push将值加入一个数组中,不会判断是否有重复的值 $addToSet将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在,则不加入 $first根据资源文档的排序获取第一个文档的数据 $last根据资源文档的排序获取最后一个文档的数据 8. 其他特殊查询 除上述查询外,MongoDB还支持其他特殊查询,主要有数组中查询、模糊查询、去重、指定返回字段等。在MongoDB中可以使用正则表达式实现近似模糊查询功能。例如,查询products集合中products_name包含“菜”的数据,如例520所示。 【例520】查询products集合中products_name包含“菜”的数据 > db.products.find({"product_name":/菜/}).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } 若想要在查询输出时只显示部分字段,MongoDB同样支持。基本语法格式为db.collection.find(query,{字段1:1,字段2:1})。 参数说明如下。 query表示查询条件。 参数2中的1表示该字段返回,0表示不返回(若不指定_id字段是否显示,则默认为_id字段显示)。注意: 1和0不能同时使用。 例如,查询products集合所有数据中的product_name字段,如例521所示。 【例521】查询products集合所有数据中的product_name字段 > db.products.find({},{"product_name":1}); { "_id" : ObjectId("62d6c1443064879e0bad6d86"), "product_name" : "西红柿" } { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药" } 其他特殊操作,读者可自行查阅相关资料了解并实验。 5.4.7文档更新 文档更新语法格式如下: db.collection.update(query,update,{upset:<boolean>,multi:<boolean>,writeConcern:<document>}) 参数说明如下。 query: 必选,查询条件。 update: 必选,待更新的对象和一些更新的操作符(如$)等。 upset: 可选,表示如果不存在update的记录,是否插入新的对象,true表示插入,false表示不插入,默认为false。 multi: 可选,MongoDB默认为false,表示只更新找到的第一条数据,如果为true,则表示对查询到的所有数据进行更新。 writeConcern: 可选,表示抛出异常的级别。 例如,修改products集合中所有product_name为“西红柿”的价格为2.5,如例522所示。 【例522】修改products集合中所有product_name为“西红柿”的价格为2.5 > db.products.update({"product_name":"西红柿"},{$set:{"price":2.5}},{multi:true}); WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) 其他更新操作此处并不演示,读者可自行实验。 5.4.8文档删除 文档删除语法格式如下: db.collection.remove(query,{justOne:<boolean>,writeConcern:<document>}) 参数说明如下。 query: 可选,查询条件。 justOne: 可选,如果设为true或1,则只删除一个文档,如果不设置此参数或使用默认值false,则删除所有匹配条件的文档。 writeConcern: 可选,抛出异常的级别。 例如,删除products集合中product_name为“西红柿”的文档数据,如例523所示。 【例523】删除products集合中product_name为“西红柿”的文档数据 > db.products.remove({"product_name":"西红柿"}); WriteResult({ "nRemoved" : 1 }) 5.4.9文档结构修改 MongoDB作为文档数据库,除能像传统关系数据库一样实现基础的增、删、改、查外,还能轻松地实现修改文档字段、删除文档字段、添加文档字段等操作。 1. 重命名文档字段 小波发现之前设计的数据库字段名存在一些问题,只要存在长度特性的农产品,均使用的是leaf_length字段,这显然对于根茎类农产品是不对的。于是小波思考能否将字段名leaf_length修改为length。MongoDB显然能够轻松实现。例如,将products集合中的所有leaf_length字段修改为length,如例524所示。 【例524】将products集合中的所有leaf_length字段修改为length > db.products.update({},{$rename:{"leaf_length":"length"}},{multi:true}) WriteResult({ "nMatched" : 4, "nUpserted" : 0, "nModified" : 3 }) > db.products.find().pretty(); { "_id" : ObjectId("62d6c4ba2fca6e0caacdefc9"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "price" : 0, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县", "length" : "15cm以上" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefca"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区", "length" : "25-30cm" } { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "specifications" : { "家庭实惠装-长30-40cm-5斤装 二级,5.00斤/箱" : 24, "优良装-长45-50cm-5斤装 二级,5.00斤/箱" : 26, "优选装-长55-60cm-5斤装 一级,5.00斤/箱" : 28, "精选装-长65-70cm-5斤装 一级,5.00斤/箱" : 30 }, "unit" : "斤", "shipping_address" : "河北省保定市蠡县", } { "_id" : ObjectId("626fdb4e430ed4347b1cd182"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } 2. 删除文档字段 近年受到天气影响,小波家乡山药的长度相比往年有所减短,只有30~40cm这一种规模可以出售,于是不得不删除product_name为“山药”的文档中原有的specifications字段,MongoDB同样能够轻松实现。例如,删除products集合中product_name为山药的文档的specifications字段,如例525所示。 【例525】删除products集合中product_name为“山药”的文档的specifications字段 > db.products.update({"product_name":"山药"},{"$unset":{"specifications":1}}); WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.products.find({"product_name":"山药"}).pretty(); { "_id" : ObjectId("62d6c4d22fca6e0caacdefcb"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "unit" : "斤", "shipping_address" : "河北省保定市蠡县", } 3. 新增文档字段 删除specifications字段后,山药没有了价格和长度信息,于是需要新增price和length字段。对于文档数据库的MongoDB实现起来仍然十分轻松。例如,在products集合中product_name为山药的文档中新增price和length字段,如例526所示。 【例526】在products集合中product_name为“山药”的文档中新增price和length字段 > db.products.update({"product_name":"山药"},{$set:{"price":4.0,"length":"30-40cm"}}); WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.products.find({"product_name":"山药"}).pretty(); { "_id" : ObjectId("62d7ae7c2fca6e0caacdefcd"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "unit" : "斤", "shipping_address" : "河北省保定市蠡县", "length" : "30-40cm", "price" : 4 } 此处仅简单介绍了MongoDB作为文档数据库的灵活之处,具体操作读者可自行查阅资料了解并学习,进一步感受MongoDB的便捷与灵活。 5.4.10小结 5.4节为本章的重难点内容,读者需要认真理解与掌握,可以通过本章所给的示例进行实验。本节仅介绍了MongoDB中数据库、集合和文档的简单操作,更深入的操作读者可自行查阅资料学习了解。 视频讲解 5.5MongoDB基于图形化管理工具的图数据库查询方法 在5.3.1节MongoDB数据库的安装中,已简单提及MongoDB Compass图形化界面管理工具。MongoDB Compass工具为MongoDB官方自带的图形化管理工具,相比于命令行界面更加便捷易用。 5.5.1MongoDB Compass的简单使用 MongoDB Compass登录主页面如图59所示。 图59MongoDB Compass登录主页面 使用MongoDB Compass新建连接,如图510所示。 图510MongoDB Compass新建连接 此处默认使用本机27017端口连接MongoDB数据库,同时可以通过展开Advanced Connection Options(高级连接选项)设置更多的信息。 MongoDB Compass连接后主页面如图511所示。 图511MongoDB Compass连接后主页面 左侧导航栏显示了MongoDB数据库的版本信息,以及存在的数据库信息等。上方导航栏分别为My Queries(搜索)、Databases(数据库)、Performance(可视化展示)功能。需要注意的是,在页面下方存在_MONGOSH,即5.5节重点介绍的命令行模式下操作MongoDB,将其内嵌至MongoDB Compass中,便于切换使用。 小波的农产品电商数据库设计又更进了一步,打开农产品电商数据库(agr_products_ecommerce_db),在5.5节中已经创建了products集合,打开products集合,可看到相关数据。products集合部分数据可视化如图512所示。 图512products集合部分数据可视化 5.5.2MongoDB Compass的数据库操作 MongoDB Compass支持5.4节基于命令行的所有操作指令,且更加简单易用,此处首先介绍数据库操作。 单击左侧导航栏的Databases按钮即可跳转至数据库操作界面。数据库操作界面如图513所示。 图513数据库操作界面 1. 创建数据库操作 单击Create Database按钮,即可创建数据库。MongoDB Compass创建数据库时,除需要指定创建的数据库名外,还需要指定其中的一个集合名。使用MongoDB Compass创建数据库,如图514所示。读者还可展开Advanced Collection Options选项卡,对创建的集合进行高级设置。 图514使用MongoDB Compass创建数据库 2. 删除数据库操作 在MongoDB Compass中,只需单击某个数据库右上角的删除图标,即可实现删除数据库操作。为了防止误操作,MongoDB Compass还需要输入删除的数据库名进行二重验证。 5.5.3MongoDB Compass的集合操作 与数据库操作类似,单击Create collection按钮即可创建集合,单击某个集合右上角的删除图标即可实现删除集合操作,同样需要二重验证。单击集合即可查看当前集合中的所有文档,具体文档操作将在5.5.4节介绍。 5.5.4MongoDB Compass的文档操作 1. 文档添加操作 单击ADD DATA按钮,可选择Import File(导入文件)和Insert Document(插入文档)选项。此处以Insert Document(插入文档)选项为例。MongoDB Compass添加文档操作如图515所示。 图515MongoDB Compass添加文档操作 此处_id会自动生成,可自行修改,但需要确保唯一性。 2. 文档更新操作 鼠标光标浮于文档上方,右侧会显示4个操作按钮,其中第一个按钮为Edit document(更新文档)。单击Edit document按钮便可直接在其上方进行更新操作。 3. 文档删除操作 与文档更新操作类似,文档删除(Remove document)按钮在右侧4个操作按钮中的第4个。单击Remove document按钮便可直接删除当前文档。 4. 文档查询操作 文档可视化上面有一个搜索框,即为文档查询功能,直接输入查询条件即可。例如,查询products集合中product_name为“菠菜”的数据,如图516所示。 图516查询products集合中product_name为“菠菜”的数据 展开右侧的OPTIONS按钮,可输入更多的查询条件,实现5.5.4节中的各种查询功能。MongoDB Compass高级查询选项如图517所示。 图517MongoDB Compass高级查询选项 灵活使用OPTIONS中的各种查询条件,可实现基于命令行界面的所有查询操作。例如,查询products集合中price大于或等于0.6的文档的product_name和price信息,且以price升序显示,如图518所示。 图518查询products集合中price大于或等于0.6的文档的product_name和price信息,且以price升序显示 5.6MongoDB基于Python的数据库连接和查询 5.1节MongoDB数据库的简介中已提及MongoDB支持多种语言,如Python、Java等。随着近年来人工智能的火热,Python的热度也推上顶峰。Python凭借其简单易用、高可读性等特点,近年来被人们广泛使用。Python的设计具有很高的可读性,同时又是一种交互式语言,对于初学者极其友好。又因其也是面向对象语言,能够很好地对代码和对象进行封装。因此,本节将重点介绍Python如何连接MongoDB数据库,并进行相应的操作。 注意: 本节的所有实验均基于Python 3.9进行,Python 2.x版本可能存在相关操作无法使用的情况。使用的编译器为VS Code,读者也可使用其他编译器在自己的实验环境下运行相应的代码。 5.6.1PyMongo PyMongo是Python提供的操作MongoDB数据库的安装包,基于PyMongo能够非常方便地操作MongoDB数据库。 首先安装PyMongo,可直接通过命令pip3 install pymongo进行安装。若通过Anaconda管理Python,则可以通过conda/pip install pymongo命令安装。 5.6.2Python连接MongoDB PyMongo通过MongoClient()方法对MongoDB数据库进行连接。所有连接均为本地连接,且端口为27107(默认端口)。 #无密码连接 import pymongo client = pymongo.MongoClient(host='127.0.0.1', port=27017) #有密码连接 import pymongo client = pymongo.MongoClient(host='127.0.0.1', port=27017, username='用户名', password='密码') 使用print(client.server_info())判断是否连接成功。 成功连接MongoDB数据库后,可直接通过此前的client对象获取Database(数据库)和Collection(集合)。PyMongo提供了两种获取Database和Collection的方式,若MongoDB中没有输入同名的Database和Collection,则会自动创建。 第一种方式: mongo_db = mongo_client['数据库名'] mongo_collection = mongo_db['集合名'] 第二种方式: mongo_db = mongo_client.数据库名 mongo_collection = mongo_db.集合名 【例527】连接MongoDB并获取农产品电商数据库(agr_products_ecommerce_db)的信息 import pymongo client=pymongo.MongoClient('127.0.0.1',27017) agr_products_ecommerce_db=client['agr_products_ecommerce_db'] print(agr_products_ecommerce_db) 【例528】连接MongoDB并获取products集合信息 import pymongo client=pymongo.MongoClient('127.0.0.1',27017) agr_products_ecommerce_db=client['agr_products_ecommerce_db'] products_col=agr_products_ecommerce_db['products'] print(products_col) 5.6.3Python对文档的CURD操作 MongoDB数据库的核心为文档,文档为MongoDB数据库的最小组成部分。因此,本小节将基于Python实现对文档的CURD操作,即Create(创建)、Update(更新)、Retrieve(读取)和Delete(删除)。 1. 基于Python的文档创建 与命令行界面相同,PyMongo同样支持单条数据的插入和多条数据的插入,分别由insert_one()函数和insert_many()函数实现。 基本语法为: col.insert_one(document)和col.insert_many(documents)。 col: 待插入文档所在的集合。 document: 待插入的单条文档。 documents: 待插入的多条文档。 【例529】在products中插入单条数据。 import pymongo client=pymongo.MongoClient('127.0.0.1',27017) agr_products_ecommerce_db=client['agr_products_ecommerce_db'] products_col=agr_products_ecommerce_db['products'] doc={ "product_name": "菠菜", "variety_name": "大叶菠菜", "producing_area": "山东省聊城市", "leaf_length": "25-30cm", "price": 0.6, "unit": "斤", "shipping_address": "山东省聊城市东昌府区" } res=products_col.insert_one(doc) # 返回插入的对象 print(res) 2. 基于Python的文档查询 文档插入同样支持查询多条数据和一条数据,查询条件可随意设置,通过有效的查询条件可实现5.5.4节中所有的文档查询指令。PyMongo主要提供了两种方法: find_one()和find()。 find_one(): 匹配第一条满足查询条件的数据,结果以Dict(字典)形式返回。若没有查询到结果,则返回None。 find(): 返回所有满足查询条件的数据。 基本语法为: find_one(query,options)和find(query,options)。 参数说明如下。 query: 查询条件。 options: 可选参数,如投影、排序等。 【例530】查询products集合中product_name为“西红柿”的数据 import pymongo client=pymongo.MongoClient('127.0.0.1',27017) agr_products_ecommerce_db=client['agr_products_ecommerce_db'] products_col=agr_products_ecommerce_db['products'] query={'product_name':'西红柿'} res=products_col.find(query) # 查询到的所有数据 for x in res: print(x) 文档查询操作相当丰富,读者可查阅相关资料进行更加复杂的查询。 3. 基于Python的文档更新 对于更新操作,PyMongo提供了update_one()和update_many()函数,通过传入相关参数,可以实现非常丰富的操作,例如更新时,若没有满足条件,则插入数据等。 update_one(): 只会更新满足条件的第一条数据。 update_many(): 更新所有满足条件的数据。 基本语法为: update_one(query,values)和update_many(query,values)。 参数说明如下。 query: 查询条件。 values: 修改后的数值。 【例531】更新products集合中product_name为“西红柿”的第一条文档的price字段值为2.6 import pymongo client=pymongo.MongoClient('127.0.0.1',27017) agr_products_ecommerce_db=client['agr_products_ecommerce_db'] products_col=agr_products_ecommerce_db['products'] query={'product_name':'西红柿'} new_data={'$set':{'price':2.6}} res=products_col.update_one(query,new_data) print(res.modified_count)# 返回修改的条数 4. 基于Python的文档删除 关于文档删除操作,PyMongo同样提供了两种方式,分别为delete_one()和delete_many()。 delete_one(): 删除满足条件的第一条数据。 delete_many(): 删除满足条件的所有数据。 基本语法为: delete_one(query)和delete_many(query)。 参数说明如下。 query: 查询条件。 【例532】删除products集合中product_name为“菠菜”的所有文档 import pymongo client=pymongo.MongoClient('127.0.0.1',27017) agr_products_ecommerce_db=client['agr_products_ecommerce_db'] products_col=agr_products_ecommerce_db['products'] query={'product_name':'菠菜'} res=products_col.delete_many(query) print(res.deleted_count)# 成功删除的文档条数 视频讲解 5.7MongoDB的维护 5.7.1MongoDB Database Tools的安装与使用 1. MongoDB Database Tools的安装 MongoDB新版安装包并不包含相关工具包,如mongoimport、mongoexport、mongodump等,但是MongoDB官方提供了MongoDB Database Tools,支持对MongoDB进行各种维护操作。 MongoDB Database Tools的下载地址详见前言二维码。MongoDB提供了两种下载方式,分别为.msi文件和.zip文件。本节针对.msi文件进行安装介绍。下载并安装后,直接用鼠标双击安装包即可开始安装,其间支持自定义安装路径。MongoDB Database Tools自定义安装路径如图519所示。此处为默认安装路径。 图519MongoDB Database Tools自定义安装路径 MongoDB Database Tools包含的工具如图520所示。MongoDB的数据导入和导出、数据备份、数据恢复等都将借助其中的工具。 图520MongoDB Database Tools包含的工具 2. MongoDB Database Tools的使用 MongoDB Database Tools的使用方式有如下两种。 (1) 用命令提示符(CMD)窗口打开MongoDB Database Tools安装路径下的bin文件,输入相关命令。 (2) 将安装路径下的bin文件添加至path环境变量,之后可在任意位置打开命令提示符(CMD)窗口输入相关命令。 本节后续操作将针对第一种使用方式进行演示与讲解。 5.7.2MongoDB数据导入和导出 1. MongoDB数据导入 MongoDB借助mongoimport命令实现对数据的批量导入。具体操作指令如下: mongoimport -d 数据库名 -c 集合名 --type csv|json --file 文件路径 参数说明如下。 d: 需要导入数据的数据库,如果数据库不存在,则会自动创建。 c: 需要导入数据的集合,如果不存在于数据库中,则会自动创建。 type: 导入数据的文件格式,支持CSV和JSON格式。 file: 存储数据的文件路径。 【例533】向agr_products_ecommerce_db数据库的products集合中导入E:\MongoDB\input\products_input.json文件存储的数据 C:\Program Files\MongoDB\Tools\100\bin> mongoimport -d agr_products_ecommerce_db -c products --type json --file E:\MongoDB\input\products_input.json 2022-07-20T09:35:04.969+0800 connected to: mongodb://localhost/ 2022-07-20T09:35:04.986+0800 2 document(s) imported successfully. 0 document(s) failed to import. 2. MongoDB数据的导出 MongoDB借助mongoexport命令实现对数据的批量导出。具体操作指令如下: mongoexport -d 数据库名 -c 集合名 -f field1,field2... -q 查询条件 -o 导出的文件路径 参数说明如下。 d: 需要导出数据的数据库。 c: 需要导出数据的集合。 f: 需要导出数据的字段。 q: 导出数据的查询条件。 o: 导出数据存储的文件路径。 【例534】将agr_products_ecommerce_db数据库中products集合所有数据导出并存储至E:\MongoDB\output\products_output.json中 C:\Program Files\MongoDB\Tools\100\bin>mongoexport -d agr_products_ecommerce_db -c products -o E:\MongoDB\output\products.json 2022-07-20T09:39:34.106+0800 connected to: mongodb://localhost/ 2022-07-20T09:39:34.130+0800 exported 5 records 5.7.3MongoDB数据备份 MongoDB借助mongodump命令实现对MongoDB数据的备份。该命令可以导出所有数据到指令目录中。同时,mongodump命令可以指定需要转存的服务器等。具体操作指令如下: mongodump -h dbhost -d dbname -o dbdirectory 参数说明如下。 h: 需要备份的服务器IP及端口(若不指定,则默认为本地服务器的27017端口)。 d: 需要备份的数据库名。 o: 备份数据存储的文件夹路径。 【例535】将本地服务器中的agr_products_ecommerce_db数据库备份至E:\MongoDB\mongodump文件夹中 C:\Program Files\MongoDB\Tools\100\bin>mongodump -h 127.0.0.1 -d agr_products_ecommerce_db -o E:\MongoDB\mongodump 2022-07-20T09:48:14.407+0800 writing agr_products_ecommerce_db.products to E:\MongoDB\mongodump\agr_products_ecommerce_db\products.bson 2022-07-20T09:48:14.418+0800 done dumping agr_products_ecommerce_db.products (5 documents) 除上述指令外,mongodump命令还支持其他参数。mongodump命令可选参数列表如表59所示。 表59mongodump可选参数列表 语法描述示例 mongodump host host_name port port_number备份服务器名为host_name的所有MongoDB数据mongodump host 127.0.0.1 port 27017 mongodump dbpath db_path out backup_dir将db_path路径下所有MongoDB数据备份至backup_dir中mongodump dbpath/data/db/ out /data/backup/ mongodump collection col_name db db_name备份db_name数据中的col_name集合mongodump collection products db agr_products_ecommerce_db 5.7.4MongoDB数据恢复 MongoDB使用mongorestore命令恢复备份的数据。具体操作指令如下: mongorestore -h <hostname><:port> -d dbname <path> 参数说明如下。 host<:port> ,h<:port>: MongoDB所在服务器地址,默认为localhost:27017。 db ,d: 需要恢复的数据库名(这个名词可以和备份时的名词不相同)。 drop: 恢复的时候,先删除当前数据,然后恢复备份的数据。 <path>: 指定备份数据所在目录,例如E:\MongoDB\mongodump\agr_products_ecommerce_db。注意: 不能同时指定<path>和dir选项,dir也可以设置备份目录。 dir: 指定备份数据的目录。 其中,<>表示的选项为可选选项。 【例536】恢复agr_products_ecommerce_db数据库 C:\Program Files\MongoDB\Tools\100\bin>mongorestore -h localhost:27017 -d agr_products_ecommerce_db E:\MongoDB\mongodump\agr_products_ecommerce_db 2022-07-20T10:04:08.239+0800 The --db and --collection flags are deprecated for this use-case; please use --nsInclude instead, i.e. with --nsInclude=${DATABASE}.${COLLECTION} 2022-07-20T10:04:08.249+0800 building a list of collections to restore from E:\MongoDB\mongodump\agr_products_ecommerce_db dir 2022-07-20T10:04:08.251+0800 reading metadata for agr_products_ecommerce_db.products from E:\MongoDB\mongodump\agr_products_ecommerce_db\products.metadata.json 2022-07-20T10:04:08.268+0800 restoring agr_products_ecommerce_db.products from E:\MongoDB\mongodump\agr_products_ecommerce_db\products.bson 2022-07-20T10:04:08.292+0800 finished restoring agr_products_ecommerce_db.products (5 documents, 0 failures) 2022-07-20T10:04:08.293+0800 no indexes to restore for collection agr_products_ecommerce_db.products 2022-07-20T10:04:08.296+0800 5 document(s) restored successfully. 0 document(s) failed to restore. 5.8MongoDB的拓展知识 5.8.1MongoDB的注意事项 MongoDB作为文档数据库,具有简单易用、可扩展性强等特点。本小节以农产品电商数据库(agr_products_ecommerce_db)的设计与实现展开,在添加农产品信息时不难发现,不同类型的农产品具有不同的字段,MongoDB无须像传统关系数据库一样,提前声明各个字段名称以及数据类型,直接以文档形式添加即可。同时,也可轻松实现添加字段、字段重命名、修改字段类型、删除字段等操作。 但MongoDB具有灵活便捷等优点的同时,也存在相关不足,具体为如下3点。 (1) 事务隔离等级不足: 目前版本支持副本集事务和分布式事务。MongoDB的事务隔离等级对于一些NoSQL的应用是够的,但对一些关键业务场景(例如银行)是不够的。 (2) 占用内存过大: 每次空间不足时,会申请一大块硬盘空间。同时,删除记录并不释放空间,而只是标记为“已删除”。 (3) 没有像MySQL一样有成熟的维护工具。 5.8.2其他类似数据库 MongoDB作为文档数据库的代表,其文档完善、社区活跃,受到广大开发者的学习与探索。但除此之外,还存在一些其他的文档数据库也被应用,具体有以下4种。 1. Amazon DynamoDB DynamoDB由亚马逊团队开发,是一个完全托管的NoSQL数据库服务。在开发的易用角度,DynamoDB没有MongoDB强大,但是从运维的角度来看,DynamoDB省去了开发者部署、监控、维护数据库环境,节约了大量时间,同时强大的扩展能力又减轻了后续运维的压力。 2. Microsoft Azure Cosmos DB Azure Cosmos DB由微软开发,是第一个全球分布式的多模型数据库服务,用于构建全球范围规模的应用。Azure Cosmos DB无须复杂的多数据库中心配置,就可以构建全球分布式应用程序,但是只支持在云端Azure使用,且目前不支持关系数据库模型和SQL。 3. Couchbase Couchbase是一个用于交互式Web应用的NoSQL数据库。它是一个易于扩展的数据库,具有高度灵活的数据模型,可提供高性能。 4. Firebase Realtime Database Firebase由Google在2012年开发。它是一个用于实时存储和同步数据的数据库。它是个由云托管的实时文档存储,可以灵活地从任何设备访问数据。 5.9本章习题 1. MongoDB数据库中的()类比于关系数据库中的行或记录。 A. 表格B. 集合C. 文档D. 字段 2. (多选)下面哪几种类型是MongoDB支持的类型()。 A. 字符串B. 日期C. 数组D. 正则表达式E. 对象 3. (多选)与数据库备份直接相关的指令为()。 A. mongodumpB. mongorestore C. mongoimportD. mongoexport 4. MongoDB的默认数据库有哪些,分别代表什么含义? 5. 请简述MongoDB的特点和应用场景。 6. 要将服务器名为test的所有数据备份到/data目录(其中开放端口为27017),需要执行的命令是什么? 7. 假设目前存在products集合信息如下: { "_id" : ObjectId("6352605a1d56b3803df4cd9d"), "product_name" : "胡萝卜", "variety_name" : "秤杆红萝卜", "producing_area" : "陕西省渭南市大荔县", "leaf_length" : "15cm以上", "price" : 0.5, "unit" : "斤", "shipping_address" : "陕西省渭南市大荔县" } { "_id" : ObjectId("6352605a1d56b3803df4cd9e"), "product_name" : "菠菜", "variety_name" : "大叶菠菜", "producing_area" : "山东省聊城市", "leaf_length" : "25-30cm", "price" : 0.6, "unit" : "斤", "shipping_address" : "山东省聊城市东昌府区" } { "_id" : ObjectId("6352605a1d56b3803df4cd9f"), "product_name" : "山药", "variety_name" : "河北铁棍山药", "producing_area" : "河北省保定市", "unit" : "斤", "shipping_address" : "河北省保定市蠡县", "length" : "30-40cm", "price" : 4 } { "_id" : ObjectId("6354f5d3fd31334cec82b877"), "product_name" : "西红柿", "variety_name" : "普罗旺斯番茄", "producing_area" : "山东省海阳市", "leaf_length" : "15cm以上", "price" : 3, "unit" : "斤", "shipping_address" : "山东省海阳市" } 参考5.4.6节的文档查询操作,实现查询products集合中所有商品价格的总和、平均值、最大值和最小值的语句。 8. 假设目前products集合如题7所示,参考5.4.9节的文档结构修改操作,实现将price字段修改为unit_price,集合所有文档新增others字段(值为null)。 9. MongoDB中的分片是什么意思? 10. MongoDB有哪些索引类型?