MongoDB(一)

目录

认识数据库

MongoDB

安装和配置mongoDB

启动和停止mongodb后台服务

启动和停止mongo shell

常用的mongo shell命令

show databases 或 show dbs命令

db命令

use命令

mongo shell支持ECMAScript

MongoDB数据库基本概念

数据库,集合,文档

MongoDB数据库基本操作

数据库的创建

数据库的查询

数据库的删除

集合的创建

集合的查询

集合的删除

文档的CRUD

向集合中插入文档

关于_id

查询集合中的文档

查询操作符

统计查询

分页查询

排序查询

更新集合中的文档

更新操作符

删除集合中的文档

索引

啥是索引

常用的索引类型

创建索引

查询索引

删除索引

预测引入的索引的执行效率


认识数据库

Web应用包含两个部分:前端和后端。

前端主要负责和用户之间的交互,以及与后端的Web服务器的交互。

后端,其实不单单只有Web服务器,Web服务器除了负责处理前端的请求并回复响应,还需要将一些用户数据持久化到数据库中,或者从数据库中读取出一些配置数据。

所以一个Web应用的逻辑架构应该分为三部分:

前端网页  ↔   后端服务器  ↔  后端数据库

数据库分类

目前数据库分为了两类:关系型数据库 和 非关系型数据库

常用的关系型数据库有:PLSQL,MySQL

常用的非关系数据库有:MongoDB,Redis

MongoDB是一种非关系型数据库。

MongoDB适合应对高并发读写,大数据量存取,数据模型更新扩展的场景,而这些场景也是传统的关系型数据库的弱项。

MongoDB

安装和配置mongoDB

在MongoDB官方网址下载安装包,这里选择ZIP包。

MongoDB Community Download | MongoDB

MongoDB(一)_第1张图片

下载完成后,将ZIP包解压到自己想要的安装目录,比如D:\Program Files\mongodb

MongoDB(一)_第2张图片

解压后,包含如上文件及目录,其中mongodb的核心文件都在bin目录中

MongoDB(一)_第3张图片bin目录中,mongod.exe用于启动mongodb后台服务,mongo.exe是启动mongo shell服务。

需要注意的是:

数据库软件本身就需要搭建在一个服务器上,而mongod.exe就是用来启动搭载数据库软件的服务器的,也只有启动了数据库服务器,数据库才能对外提供服务。

mongo.exe就是用来访问数据库服务器的,mongo.exe默认会开启一个mongo shell命令窗口,在mongo shell命令窗口中,我们可以通过mongo shell命令以及相关方法完成数据库的相关操作。比如创建数据库,创建集合,CRUD文档等。

通过解释mongod.exe和mongo.exe的功能,我们可以分析出,必须要先执行mongod.exe,再执行mongo.exe,因为必须要先启动数据库服务器,才能去访问数据库服务器。

为了方便我们使用bash命令执行mongd.exe和mongo.exe,我们需要将MongoDB的bin目录配置到计算机的系统环境变量中

MongoDB(一)_第4张图片

这样,我们就可以在任意目录下开一个命令窗口,运行mongod和mongo了。

验证MongoDB安装情况:

MongoDB(一)_第5张图片

通过上述命令检查MongoDB的版本,若可以打印出信息,即表示mongodb安装成功。 

启动和停止mongodb后台服务

此时我们在任意目录下,都可以使用mongod命令启动mongodb后台服务了。

MongoDB(一)_第6张图片

但是直接运行mongod命令会报错并退出

,"msg":"DBException in initAndListen, terminating","attr":{"error":"NonExistentPath: Data directory C:\\data\\db\\ not found. Create the missing directory or specify another path using (1) the --dbpath command line option, or (2) by adding the 'storage.dbPath' option in the configuration file."} 

报错提示C:\\data\\db文件夹找不到,当我们在C盘下创建对应目录后,该错误就会解决。而C:\\data\\db目录就是存放mongodb数据库数据的目录。我们一般不喜欢将大量的数据放在C盘,所以我们需要自定义数据库数据存放目录,

此时需要使用命令 mongod --dbpath="数据库数据存放目录",

比如将存放目录设为D:\Program Files\mongodb\data

此时可以发现mongodb后台服务已经启动成功,并占用了27017端口,并处于等待连接状态

但是每次启动mongodb后台都写这么多命令实在不是程序员所为,所以我们通常将mongod开启数据库服务器的命令配置为系统服务,并设置电脑开机自启动。

// 使用管理员身份打开CMD命令窗口,运行如下命令
mongod --dbpath="D:\Program Files\mongodb\data\db"  --logpath="D:\Program Files\mongodb\data\log\mongod.log" --install --serviceName "MongoDB"

--dbpath:设置MongoDB数据库存放数据的目录,

--logpath:设置MongoDB数据库服务器启动后的日志信息,即mongod命令执行时产生的日志信息

--install --service "MongoDB" :是将前面的命令注册为系统服务,服务名为MongoDB

当以上命令执行完成后,我们打开系统服务,搜索MongoDB,将其设置为开机自启即可。

MongoDB(一)_第7张图片

这样以后就不需要管MongoDB的启动了。

如果我们想停止MongoDB服务,则在管理员权限CMD窗口下,执行

MongoDB(一)_第8张图片

如果我们想开启MongoDB服务,则在管理员权限CMD窗口下,执行

MongoDB(一)_第9张图片

此时启动停止系统服务MongoDB,就相当于启动停止MongoDBs忽聚酷服务器。

启动和停止mongo shell

当我们启动好MongoDB数据库服务器后,我们才能使用mongo命令开启mongo shell窗口。

mongo命令是用于连接数据库服务器的,而连接一个服务器时,必须要要指定服务器的IP和Port,所以mongo命令需要指定如下参数 

MongoDB(一)_第10张图片

 --host用于指定服务器的IP或域名,如果不指定则使用默认IP 127.0.0.1

--port用于指定服务器的端口,如果不指定则使用默认端口27017

所以上面的命令可以简化为

MongoDB(一)_第11张图片

当mongo命令执行后,在MongoDB数据库服务器的日志中就会产生一条连接数据库成功的日志。

MongoDB(一)_第12张图片

提示mongo shell连接数据库服务器成功。

mongo shell连接服务器成功后,会自动开启一个mongo shell命令行,等待用户输入mongo shell命令来操作数据库。 

常用的mongo shell命令

show databases 或 show dbs命令

该命令用于显示数据库服务器上所有的数据库

MongoDB(一)_第13张图片

db命令

该命令用于显示mongo shell当前正在操作的数据库 

MongoDB(一)_第14张图片

use命令

该命令用于创建或切好到一个数据库

MongoDB(一)_第15张图片

use命令后面需要跟一个数据库名字,比如my

如果该数据库my存在,则use命令表示将mongo shell正在操作的数据库切换为指定的数据库my;

如果该数据库my不存在,则use命令表示创建该数据库my。

mongo shell支持ECMAScript

而mongo shell除了以上内置命令外,还支持ECMAScript语法,即支持运行JS程序。

MongoDB(一)_第16张图片

mongo shell还提供了数据库对象db,我们可以通过db对象来操作数据库

MongoDB数据库基本概念

数据库,集合,文档

MongoDB数据库将数据保存在文档中,文档保存在集合中,集合保存在数据库中。

而一个数据库中可以保存多个集合,一个集合中可以保存多个文档。

在上面的描述中,出现了几个关键名词:文档,集合,数据库。而这三个名词正是MongoDB数据库的基本概念。

为了更好的理解MongoDB数据库的文档,集合,数据库,我们可以将其类比到关系型数据库的基本概念中:

MySQL数据库将数据保存在表的行Row中,Row保存在表Table中,Table保存在数据库中。

而一个数据库中可以保存多张表,一张表中可以保存多个行。

即类比关系如下

MongoDB MySQL
数据库 数据库
集合
文档 表的行

在了解了MongoDB的基本概念后,我们需要知道MongoDB如何组织保存数据?

其实MongoDB会将数据保存在文档中,文档在MongoDB中使用BSON来构造,而所谓BSON其实就是Binary JSON,即JSON的二进制。为了方便理解,我们完全可以将文档看成一个JSON对象。

而集合可以保存多个文档,MongoDB将集合看成一个数组,文档就是集合的数组元素。MongoDB将数据库看成一个JS对象,它的键就是集合名,值就是集合对应的数组。

MongoDB(一)_第17张图片

MongoDB数据库基本操作

数据库的创建

MongoDB的数据库创建使用命令use ,但是需要注意的此时创建的数据库只存在于内存中,而没有被持久化到磁盘中,而只有当数据库中插入了文档,内存中的数据库才会被持久化到磁盘中。

相应地,show dbs命令,只会将持久化到磁盘中的数据库展示出来,而不会将内存中的数据库展示出来。

MongoDB(一)_第18张图片

需要注意的是,如果使用use 创建了一个数据库,但是没有向该数据库中插入数据,则在数据库连接断开后,内存中的空数据库会被丢弃。

mongo shell在连接到数据库后,会自动为使用者创建一个test数据库,并切换到该数据库上。同样地,test数据库也只是内存上的,只有我们向其中加入文档后,test数据库才会被持久化到磁盘中。

MongoDB(一)_第19张图片

数据库的查询

数据库的查询分为两种,一是查询MongoDB服务器上所有的数据库(磁盘上的),二是查询当前数据库连接正在操作的数据库(内存上的)

查询MongoDB服务器上所有的数据库(磁盘上的)使用命令 show databases 或简写 show dbs

MongoDB(一)_第20张图片

查询当前数据库连接正在操作的数据库(内存上的) 使用命令 db

MongoDB(一)_第21张图片

数据库的删除

MongoDB的数据库使用 db.dropDatabase() 删除当前正在操作的数据库。

其中db是数据库对象,是一个JS Object。dropDatabase是db对象的方法,用于删除db对象对应的数据库。

MongoDB(一)_第22张图片

需要注意的是使用db.dropDatabase删除一个数据库,必须先使用use切换到该数据库上,因为我们只能通过use命令来指定操作的数据库db

另外db.dropDatabase本质是用来删除磁盘上的数据库,而不是内存上的数据库,内存上的空数据库没有删除的必要,也无法使用db.dropDatabase删除。

MongoDB(一)_第23张图片

集合的创建

在MongoDB中,有两种方式创建集合:

显示创建集合(手动):db.createCollection(collectionName, options)

隐式创建集合(自动):db.

显示创建的集合,会被直接持久化到磁盘中,并且除了指定集合名字外,还可以指定options配置参数,这里我们不赘述options配置,因为我们一般不使用显示创建集合。

隐式创建的集合,会被加入到内存中,只有向集合中插入文档,内存中的集合才会被持久化到磁盘中。隐式创建的集合只能指定名字,集合的配置都是默认的。

集合的查询

当我们切换指定的数据库后,可以使用show collections查询数据库下所有的集合,但是这里的集合指的是持久化到磁盘中的集合,不包括内存中的集合。 

MongoDB(一)_第24张图片

集合的删除

删除集合使用 db..drop()

和数据库类似,内存上的空集合没有删除的必要,当数据库连接结束时,mongo shell占用的内存数据都会被释放,所以内存上的空集合会被丢弃。这里的drop只会删除磁盘上的集合。

MongoDB(一)_第25张图片

文档的CRUD

通过前面关于数据库和集合的描述,我们知道了数据库和集合都支持隐式创建,即使用时临时创建,此时数据库和集合都保存在内存中,只有向数据库和集合中插入文档,才能将隐式创建的数据库和集合持久化到磁盘中。

文档可以被插入到集合中,也可以从集合中删除,大部分时候,我们都是查询集合中的文档,以及更新集合中的文档。

向集合中插入文档

db..insert(doc|docs)

db..insertOne(doc)

db..insertMany(docs)

MongoDB提供了三种方法向集合中插入文档。插入场景主要分为两种:插入一个文档,插入多个文档。

其中insert方法既支持插入一个文档,又支持插入多个文档。insertOne只支持插入一个文档,insertMany只插入多个文档。

当插入一个文档时,传入的文档其实就是一个JS对象。

MongoDB(一)_第26张图片

 当插入多个文档时,则需要将多个JS对象存放到一个数组后传入。

MongoDB(一)_第27张图片

 insert是MongoDB早期提供的插入方法,insertOne和insetMany是最新提供的插入方法。

二者区别主要是:insert的返回值只包含了插入结果,即插入是否成功,插入几个文档。而insertOne和insertMany会返回被插入文档的_id。即上面两图中的insertedId和insertedIds。

关于_id

当使用insert,insertOne,insertMany插入文档到集合时,MongoDB会自动为插入的文档添加一个_id字段,该字段的作用是用来唯一标识一个文档。即_id字段的值是唯一的。MongoDB使用了ObjectId类来构造_id值。

ObjectId会基于:

  • 时间戳记
  • 客户端机器ID
  • 客户端进程ID
  • 3字节递增计数器

来生成一个唯一标识。

集合中的每个文档都会被加入_id字段,并且有_id的唯一性,我们可以将_id当成集合的主键。我们可以通过_id值精确匹配到唯一的文档。

需要注意的是,当我们插入的文档中不包含_id字段时,系统会自动为文档加入_id,但是如果我们插入的文档中已经包含了_id,则系统将不再为我们自动加入_id。

此时,我们只需要保证两点:

1、_id的值是一个非数组类型的值

2、_id值要保证唯一MongoDB(一)_第28张图片

查询集合中的文档

在讨论查询集合中的文档之前,我们需要知道有几种查询策略:

1、单集合查询(单表查询)

2、多集合查询(多表查询)

其中单表查询主要关注:查询条件,查询结果字段

        多表查询主要关注:表与表之间数据的关系:一对一,一对多,多对多

db..find(query, projection)

db..findOne(query, projection)

MongoDB提供了两种方法来支持查询集合中的文档,分别是find和fineOne,其中find支持查询出多个匹配结果,findOne只会查询出第一个匹配的结果。

find和findOne方法的两个参数query,projection的含义分别对应 查询条件 和 查询结果字段(投影),类比到关系型数据库的查询

select from table where

query和projection都是一个JS对象,我们可以通过键值来完成对应的语义,比如:

MongoDB(一)_第29张图片

find不传任何参数,相当于find({},{}),即query为空对象,表示查询所有文档,project为空对象,表示展示所有字段。 

query = {name:'swk'},projection = {_id:0, name:1}

相当于 select name from user where name = 'swk',其中query表示只查询name='swk'的文档,projection表示查询结果(值1表示显示)显示name字段,(值0表示不显示)不显示_id字段,age没有指定,默认不显示。

query对象除了支持精确匹配外,还可以通过查询操作符来进行其他情况的匹配

查询操作符

比较操作符 query写法 含义
$eq
{ : { $eq:  } }
等于
$ne
{ : { $ne:  } }
不等于
$gt
{ : { $gt:  } }
大于
$gte
{ : { $gte:  } }
大于等于
$lt
{ : { $lt:  } }
小于
$lte
{ : { $lte:  } }
小于等于
$in
{ field: { $in: [, , ...  ] } }

The $in operator selects the documents where the value of a field equals any value in the specified array.

If the field holds an array, then the $in operator selects the documents whose field holds an array that contains at least one element that matches a value in the specified array

$nin
{ field: { $nin: [, , ...  ] } }

$nin selects the documents where:

  • the field value is not in the specified array or
  • the field does not exist.

MongoDB(一)_第30张图片

select * from user where age = 20

select * from user where age != 20 

MongoDB(一)_第31张图片

 select * from user where age < 20

select * from user where age <= 20

MongoDB(一)_第32张图片

select * from user where age > 20

select * from user where age >= 20 

MongoDB(一)_第33张图片

select * from user where name in ('swk', 'zbj', 'swj')

select * from user where name not in  ('swk', 'zbj', 'swj')

比较操作符都是直接判断值

逻辑操作符 query写法 含义
$and
{ $and: [ {  }, {  }, ... , {  } ] }
The $or operator performs a logical OR operation on an array of two or more  and selects the documents that satisfy at least one of the 
$or
{ $or: [ {  }, {  }, ... , {  } ] }
The $or operator performs a logical OR operation on an array of two or more  and selects the documents that satisfy at least one of the 
$not
{ field: { $not: {  } } } 
$not performs a logical NOT operation on the specified  and selects the documents that do not match the . This includes documents that do not contain the field.
$nor
{ $nor: [ {  }, {  }, ...  {  } ] }
$nor performs a logical NOR operation on an array of one or more query expression and selects the documents that fail all the query expressions in the array. 

select * from user where age < 30 and age > 20

$and后面跟一个数组,数组元素就是逻辑与的参与条件

MongoDB(一)_第34张图片select * from user where age < 20 or age > 30

$or后面跟一个数组,数组元素就是逻辑或的参与条件

MongoDB(一)_第35张图片返回age字段不存在,或者age字段存在且age>=20的文档

$not的作用是影响其他操作符,而$ne是直接判断值。

MongoDB(一)_第36张图片$nor和$not都表示逻辑非,但是$not只能影响一个条件,而$nor可以影响多个条件

上面$nor语句表示:

1、如果age和name字段都存在,则取条件相反的文档

2、如果age存在,name不存在,则取age条件相反的文档

3、如果name存在,age不存在,则取name条件相反的文档

4、如果name,age都不存在,则也符合要求

元素操作符 query写法 含义
$exists
{ field: { $exists:  } }
When  is true, $exists matches the documents that contain the field, including documents where the field value is null. If  is false, the query returns only the documents that do not contain the field.
$type
{ field: { $type:  |  } }
$type returns documents where the BSON type of the field matches the BSON type passed to $type.

 

查询不存在age字段的文档

MongoDB(一)_第37张图片查询_id字段类型为number的文档

计算操作符 query写法 含义
$mod
{ field: { $mod: [ divisor, remainder ] } }
Select documents where the value of a field divided by a divisor has the specified remainder (i.e. perform a modulo operation to select documents).
$regex
{ : /pattern/ }
Provides regular expression capabilities for pattern matching strings in queries. MongoDB uses Perl compatible regular expressions (i.e. “PCRE” ) version 8.39 with UTF-8 support.
$where
{ $where: function() { return this.credits == this.debits; } }
Use the $where operator to pass either a string containing a JavaScript expression or a full JavaScript function to the query system. The $where provides greater flexibility, but requires that the database processes the JavaScript expression or function for each document in the collection. Reference the document in the JavaScript expression or function using either this or obj .

 这里只试了$regex和$where 

MongoDB(一)_第38张图片$regex可以实现比关系型数据库更加强大的模糊匹配。

另外使用$regex时,可以简写

MongoDB(一)_第39张图片

MongoDB(一)_第40张图片$where可以实现自由度更高的匹配,$where后面跟一个函数,函数最好使用普通函数,因为这样函数的this就指向了文档对象,我们将匹配逻辑作为函数的返回值,当返回true时,表示符合匹配,对应文档会作为find返回值。

另外如果find中只有一个$where,则可以简写为

或者更加简写为

 突然就感觉前面的比较运算符和逻辑运算符不用记了,但是$where无法成为主流操作,原因是:

虽然$where操作符功能强大且灵活,它可以将JavaScript表达式的字符串或 JavaScript函数作为查询语句的一部分。在JavaScri pt表达式和函数中,可以 使用this或obj来引用当前操作的文档。 JavaScript表达式或函数返回值为true时,才会返回当前的文档。
查询时,$where操作符不能使用索引,每个文档需要从BSON对象转换成 JavaSript对象后,才可以通过$where表达式来运行。因此,它比常规查询要慢很多,一般情况下,要避免使用$where查询。

数组操作符 query写法 用法
$all
{ : { $all: [  ,  ... ] } }
The $all operator selects the documents where the value of a field is an array that contains all the specified elements.
$eleMatch
{ : { $elemMatch: { , , ... } } }
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
$size
db.collection.find( { field: { $size: 2 } } )
The $size operator matches any array with the number of elements specified by the argument.

MongoDB(一)_第41张图片

查询tags既包含mongoose又包含back的文档

$all后面跟一个数组,只有$all指定数组的元素都存在于文档中,才能匹配

统计查询

集合还可以使用count方法进行文档数量统计

MongoDB(一)_第42张图片

分页查询

说到查询一般就少不了分页查询,说到分页查询,自然而然地就能想到skip和limit,MongoDB也提供了skip和limit方法,skip用于跳过几条文档,limit用于限定每次查询地文档条数

MongoDB(一)_第43张图片比如上面地分页,通过count得到数据总数9条,每页显示条数3是limit,页码就是skip,skip跳过的条数 = limit * (页码 - 1) 

排序查询

MongoDB(一)_第44张图片

find方法返回的数组可以进行sort排序,sort方法可以指定排序的字段,值可以设置为1或-1,1表示升序,-1表示降序 

更新集合中的文档

更新语法使用如下方法

db..update(query, update, options)

db..updateOne(query, update, options)

db..updateMany(query, update, options)

db..replaceOne(query, replacement, options)

参数解析

query参数表示查询条件,功能和find,findOne的query参数相同,可以使用查询操作符。

update|replacement参数表示更新目标,即将query匹配到的文档按照update|replacement参数对象指定的字段进行更新,更新目标受到方法本身以及options参数的影响,存在局部更新和覆盖更新两种更新情况。

options参数用来设置一些更新配置,比如局部更新还是覆盖更新,或者是否对不存在的文档进行更新时,执行创建操作。

方法解析

update方法默认只更新query匹配到的第一个文档,如果需要更新所有匹配到的文档,则需要设置options.multi为true。默认覆盖更新。

updateOne方法默认只更新query匹配到的第一个文档。默认覆盖更新。

updateMany方法默认更新query匹配的所有文档。默认覆盖更新。

replaceOne方法默认只覆盖query匹配到的第一个文档。

options参数解析

options参数 含义
upsert
Boolean类型,当值为true时,表示如果query未匹配到文档,则基于update|replacement创建文档,当值为false时,则不创建。默认为false。
multi
Boolean类型。只对update方法有用,当值为true时,表示更新所有query匹配到的文档,默认值为false,表示只更新匹配到的第一个文档。

MongoDB(一)_第45张图片

update方法默认是覆盖更新,所以上面update参数加了$set更新操作符使其局部更新。

上面主要验证了option.multi的作用

MongoDB(一)_第46张图片

 上面主要验证了option.upsert的作用

更新操作符

字段更新操作符 update参数样例 含义
$set
{ $set: { : , ... } }

The $set operator replaces the value of a field with the specified value.

实现局部更新

$unset
{ $unset: { : "", ... } }

The $unset operator deletes a particular field. 

实现删除字段,由于是删除字段,所以值是什么都可以,一般使用""

$inc
{ $inc: { : , : , ... } }

The $inc operator increments a field by a specified value

实现自增,可以指定自增值

$mul
{ $mul: { field:  } }

Multiply the value of a field by a number. 

实现自乘,可以指定自乘值

$rename
{$rename: { : , : , ... } }

The $rename operator updates the name of a field

实现字段重命名

$setOnInsert
{ $setOnInsert: { : , ... } }
如果使用 upsert: true 的更新操作导致插入文档,则$setOnInsert会将指定的值分配给文档中的字段。如果更新操作未导致插入,$setOnInsert不执行任何操作。
$currentDate
{ $currentDate: { : , ... } }

The $currentDate operator sets the value of a field to the current date, either as a Date or a timestamp. The default type is Date.

 can be either:

  • a boolean true to set the field value to the current date as a Date, or
  • a document { $type: "timestamp" } or { $type: "date" } which explicitly specifies the type. The operator is case-sensitive and accepts only the lowercase "timestamp" or the lowercase "date".
$min
{ $min: { : , ... } }
The $min updates the value of the field to a specified value if the specified value is less than the current value of the field.
$max
{ $max: { : , ... } }
The $max operator updates the value of the field to a specified value if the specified value is greater than the current value of the field.

MongoDB(一)_第47张图片

 $min 指定一个最小值,如果字段值比$min大的话,则将字段值改为$min值,否则不变

MongoDB(一)_第48张图片

$max指定一个最大值,如果字段值比$max小的话,则将字段值改为$max,否则不变

即比最大值小,则变为$max,比最小值大,则变为$min

MongoDB(一)_第49张图片

$set可以更新已有字段的值,也可以新增不存在的字段,$unset用于删除匹配文档指定的字段,字段值可以是任意,无影响。

 

$rename用于改变匹配到的文档的字段名字。

需要注意的是$unset,$rename这些操作字段的操作符,貌似表的列操作,但是实际不同,因为MongoDB中集合并不像表一样,集合是松散结构,类似于数组,对于文档默认没有约束作用,即集合没有列设置,每个文档的字段都是独立的,互补影响。

MongoDB(一)_第50张图片

在更新操作中,有一些特殊但必要的操作,比如自增操作,如果没有自增操作,我们需要将字段值取出来,计算后,再更新到文档中

MongoDB(一)_第51张图片

我们可以直接使用更新操作符$inc来代替

上面$inc:{age:2}表示让age自增2,使用$inc操作符避免了将值取出后使用JS计算。

同理,$mul就是让字段值自乘。

 在数据库字段中,还有一种常用类型就是当前时间,我们使用更新操作符$currentDate来生成对应的当前时间,说到时间肯定离不开时间格式,一般就两种Date类型时间对象和Number类型时间戳。我们可以使用$currentDate:{field:{$type:'Date'}来指定,或者$currentDate:{field:{$type:'timestamp'}

MongoDB(一)_第52张图片

这里的ISODate对应JavaScript的Date类型

$setOnInsert比较鸡肋,就不介绍了。

数组更新操作符 用法 含义
$(update)
db.collection.update(
   { : value ... },
   { : { ".$" : value } }
)
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array. To project, or return, an array element from a read operation, see the $ projection operator.
$addToSet
{ $addToSet: { : , ... } }
The $addToSet operator adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array.
$pop
{ $pop: { : <-1 | 1>, ... } }
The $pop operator removes the first or last element of an array. Pass $pop a value of -1 to remove the first element of an array and 1 to remove the last element in an array.
$pull
{ $pull: { : , : , ... } }
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
$push
{ $push: { : , ... } }
The $push operator appends a specified value to an array.
$each
{ $push|$addToSet: { : { $each: [ ,  ... ] } } }

The $each modifier is available for use with the $addToSet operator and the $push operator.

$slice
{
  $push: {
     : {
       $each: [ , , ... ],
       $slice: 
     }
  }
}

The $slice modifier limits the number of array elements during a $push operation. To project, or return, a specified number of array elements from a read operation, see the $slice projection operator instead.

To use the $slice modifier, it must appear with the $each modifier. You can pass an empty array [] to the $each modifier such that only the $slice modifier has an effect.

$sort
{
  $push: {
     : {
       $each: [ , , ... ],
       $sort: 
     }
  }
}

The $sort modifier orders the elements of an array during a $push operation.

To use the $sort modifier, it must appear with the $each modifier. You can pass an empty array [] to the $each modifier such that only the $sort modifier has an effect.

$position
{
  $push: {
    : {
       $each: [ , , ... ],
       $position: 
    }
  }
}

The $position modifier specifies the location in the array at which the $push operator insert elements. Without the $position modifier, the $push operator inserts elements to the end of the array. See $push modifiers for more information.

To use the $position modifier, it must appear with the $each modifier.

MongoDB(一)_第53张图片

  $(update),即表示用在update参数上的$,这个$作用是,取得被query匹配到的数组元素的位置,然后精确修改对应数组位置上的元素

MongoDB(一)_第54张图片

MongoDB(一)_第55张图片

 $push可以向一个数组中添加一个或多个元素,但是添加多个元素时,需要借助$each解析出每个数组元素。如果直接将数组作为$push的值的话,会导致$push将整个数组当成一个元素。

MongoDB(一)_第56张图片

$push默认只能实现数组尾部插入,但是在$push中使用$position就可以实现在数组指定索引位置插入元素,需要注意的是$position必须和$each结合使用。即被插入的元素必须由$each指定。 

MongoDB(一)_第57张图片

$push插入元素到数组后,默认不对数组进行排序,若想排序,需要使用$sort,若指定$sort值1,则表示插入后进行整体升序,若指定$sort值-1,则表示插入后进行整体降序 

MongoDB(一)_第58张图片

$slice可以对$push后的数组进行截取,$slice指定的值是左闭值。 

MongoDB(一)_第59张图片

$addToSet顾名思义,将数组当成Set,Set特点是元素不可重复。用法上和$push差不多,但是不会添加重复元素到数组中。

 MongoDB(一)_第60张图片

 $pop是移除数组首尾元素的,设置$pop值1,则表示尾部移除,为-1则表示首部移除。

MongoDB(一)_第61张图片

$pull可以按条件移除多个数组元素。 

删除集合中的文档

删除集合中文档的方法如下:

db..remove(query, options)

db..deleteOne(query)

db..deleteMany(query)

参数解析

query参数就是查询条件

options.jusetOne参数用于remove方法

方法解析

remove方法用于删除被query匹配到的一个或多个文档,可以通过设置options.justOne为true,来限制只删除匹配到的第一个文档

deleteOne方法用于删除被query匹配到的第一个文档

deleteMany方法用于删除被query匹配到的所有文档

MongoDB(一)_第62张图片

 MongoDB(一)_第63张图片

 MongoDB(一)_第64张图片

 MongoDB(一)_第65张图片

索引

啥是索引

当我们需要查找集合中符合条件的文档时,MongoDB数据库底层会将集合中所有文档取出来进行条件匹配,类似于for循环遍历数组,并取出数组元素进行匹配。

当集合中存在大量文档时,这种查找策略是极其耗时,且浪费性能的。

比如 db.user.find({age:{$gt:20}})

其实只需要查询age>20的文档,但是实际上,底层将所有文档都取出与20比较。

所以,一种高效的查询策略就应运而生了,索引。

所谓索引,就是针对集合中文档的那些常被用做查询条件的字段,进行排序,然后加入BTree数据结构中。BTree的查询方式是二分查找,即折半查找,时间复杂度为O(logN)

比如现在user集合中存在如下文档

{name:'swk', age:10}

{name:'zbj', age:15}

{name:'swj', age:20}

{name:'ts', age:25}

{name:'rl', age:30}

如果针对age字段建立索引的话,则会生成一个BTree

MongoDB(一)_第66张图片

B-Tree Visualization (usfca.edu)

当BTree建立后,后面查询集合文档时,如果查询条件刚好是age,即索引字段,则直接去BTree中折半查找,查询效率会有显著的提升,因为不用查询集合中所有文档。

常用的索引类型

所谓索引的类型,其实就是索引的内容,一般而言,索引的内容就是文档的字段。

而字段常作为集合查询条件,有时候是单字段查询条件,有时候是多字段查询条件。

所以索引类型包括了 单字段索引 以及 复合索引。

单字段建立索引,其实步骤就两部:1、根据单字段进行集合文档排序(升序降序都可以,主要保证有序即可) 2、建立BTree

复合索引,同理,将集合文档依次按照给定的条件字段进行排序(升序降序都可以,主要保证有序),然后建立BTree

创建索引

db..createIndex(keys, options)

参数解析

keys:是一个对象,其中需要排序的索引字段为键,值为1或-1,分别表示升序和降序

options:是一个配置对象,常用配置有

  • options.name,用来设置索引名字,
  • options.v,系统自动设置mongodb版本号进去,
  • options.weight,用来设置索引的权重,范围1~999,即当集合中存在多个索引时,并且发生索引冲突时,以哪个索引为主
  • options.unique,值为true或false,设置建立的索引是否唯一

例子

我们先创建一个包含10万条文档的集合

MongoDB(一)_第67张图片

 这里需要注意,建议只调用一次insert插入一个数组,而不是调用10万次insert,每次插入一个对象。我们要知道mongodb数据库底层其实也是将数据持久化到文件中,每次insert调用都会产生一次文件IO操作。

然后,我们查询num=50000的文档

MongoDB(一)_第68张图片

一共查询了0.193秒

我们建立一个索引

MongoDB(一)_第69张图片

 花了0.296秒,建立索引的时间要比单次查询时间慢,因为要进行排序和建立BTree

MongoDB(一)_第70张图片

 

 

之后,在进行相同的查询

MongoDB(一)_第71张图片

牛,只用了0.009s。查询效率的提升显而易见。

那么我们是否就进入了疯狂建立索引的模式呢?

我们需要认识到索引的弊端:占用内存。为什么这么说呢

才基于10万数据建立的索引,就占用了超过1M的内存。

为什么建立的索引会占用如此多的内存呢?

因为数据库底层会将集合中所有文档排序后,加入BTree,后使用索引字段作为查询条件查询时,就会去BTree上查,查到则取出文档。

即所有文档被拷贝了一份到索引对应的BTree上。

那么何时才需要建立索引,何时不需要建立索引呢?

其实对于数据量小于2000的集合,并没有太大的必要建立索引,因为2000的数据量,就算使用全局扫描也耗费不了多少时间,只有当数据量超过2000后,随着数据量的增加,全局扫描的效率会逐渐降低,此时就需要牺牲内存,来增加查询效率了。

 

查询索引

db..getIndexes()

MongoDB(一)_第72张图片

可以发现,其实集合存在一个默认索引_id_,该索引是基于_id字段建立的。

如果我们未指定索引名字的话,则索引名字为 “字段名_排序值”,比如num字段建立的升序索引的名字就是“num_1”

这里有个奇怪的地方,就是索引对象的key中索引字段的值取得是NumberInt("1"),其实这是一种保险措施,防止外界传入“1“字符串。

删除索引

不考虑磁盘和内存占用的问题,索引需要删除吗?

需要。

这涉及到索引的维护,数据库集合中的文档不可能总是固定的,往往建立完索引后,对于集合的文档立马就发生了增删改查,那么是否意味着索引就无意义了呢?因为索引是按照老数据建立的。

数据库为了解决这个问题,都有一套自动维护索引的机制,即集合文档改变时,集合索引也会发生相应的改变。这样就避免索引刚被创建就失去意义的情况。

但是当我们向具有索引的集合中插入大量数据时,索引的自动维护机制会频繁触发,即频繁进行BTree的构建。这是极其浪费性能的。

所以,通常情况下,在具有索引的集合中插入大量数据时,我们需要先删除集合的索引。

db..dropIndex(indexName)

需要注意的是_id_索引是无法被删除的。

MongoDB(一)_第73张图片

db..dropIndexed()

该方法是删除集合上所有的索引(不包括_id_) 

预测引入的索引的执行效率

有时候,我们建立的索引可不能带来查询效率的提升,反而会因为占用内存或者不合理的索引,导致查询效率降低,我们又无法等到上线后,基于大量业务数据测试,我们需要一种可以预测索引能力的工具。

mongodb提供了explain方法来预测索引的能力

MongoDB(一)_第74张图片这里stage是FETCH,表示基于了索引查询 

MongoDB(一)_第75张图片

 这三个executionTimeMillis时间值越小,查询效率越高

 

你可能感兴趣的:(数据库,mongodb,nosql,数据库)