MongoDB(一)

本文详细介绍了MongoDB的安装、配置、数据库和集合的操作,包括创建、查询、删除,以及文档的CRUD操作。此外,还探讨了MongoDB的索引类型、创建、查询和删除,以及如何评估索引的执行效率。通过实例展示了MongoDB在实际应用中的使用和优化策略。
摘要由CSDN通过智能技术生成

目录

认识数据库

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

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

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

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目录配置到计算机的系统环境变量中

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

验证MongoDB安装情况:

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

启动和停止mongodb后台服务

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

但是直接运行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的启动了。

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

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

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

启动和停止mongo shell

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

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

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

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

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

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

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

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

常用的mongo shell命令

show databases 或 show dbs命令

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

db命令

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

use命令

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

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

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

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

mongo shell支持ECMAScript

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

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

MongoDB数据库基本概念

数据库,集合,文档

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

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

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

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

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

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

即类比关系如下

MongoDBMySQL
数据库数据库
集合
文档表的行

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

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

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

MongoDB数据库基本操作

数据库的创建

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

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

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

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

数据库的查询

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

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

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

数据库的删除

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

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

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

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

集合的创建

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

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

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

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

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

集合的查询

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

集合的删除

删除集合使用 db.<collectionName>.drop()

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

文档的CRUD

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

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

向集合中插入文档

db.<collection>.insert(doc|docs)

db.<collection>.insertOne(doc)

db.<collection>.insertMany(docs)

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

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

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

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

 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值要保证唯一

查询集合中的文档

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

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

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

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

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

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

db.<collection>.findOne(query, projection)

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

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

select <projection> from table where <query>

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

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
{ <field>: { $eq: <value> } }
等于
$ne
{ <field>: { $ne: <value> } }
不等于
$gt
{ <field>: { $gt: <value> } }
大于
$gte
{ <field>: { $gte: <value> } }
大于等于
$lt
{ <field>: { $lt: <value> } }
小于
$lte
{ <field>: { $lte: <value> } }
小于等于
$in
{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }

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: [<value1>, <value2>, ... <valueN> ] } }

$nin selects the documents where:

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

select * from user where age = 20

select * from user where age != 20 

 select * from user where age < 20

select * from user where age <= 20

select * from user where age > 20

select * from user where age >= 20 

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

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

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

逻辑操作符query写法含义
$and
{ $and: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
The $or operator performs a logical OR operation on an array of two or more <expressions> and selects the documents that satisfy at least one of the <expressions>
$or
{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
The $or operator performs a logical OR operation on an array of two or more <expressions> and selects the documents that satisfy at least one of the <expressions>
$not
{ field: { $not: { <operator-expression> } } } 
$not performs a logical NOT operation on the specified <operator-expression> and selects the documents that do not match the <operator-expression>. This includes documents that do not contain the field.
$nor
{ $nor: [ { <expression1> }, { <expression2> }, ...  { <expressionN> } ] }
$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后面跟一个数组,数组元素就是逻辑与的参与条件

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

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

返回age字段不存在,或者age字段存在且age>=20的文档

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

$nor和$not都表示逻辑非,但是$not只能影响一个条件,而$nor可以影响多个条件

上面$nor语句表示:

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

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

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

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

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

 

查询不存在age字段的文档

查询_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
{ <field>: /pattern/<options> }
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 

$regex可以实现比关系型数据库更加强大的模糊匹配。

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

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

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

或者更加简写为

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

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

数组操作符query写法用法
$all
{ <field>: { $all: [ <value1> , <value2> ... ] } }
The $all operator selects the documents where the value of a field is an array that contains all the specified elements.
$eleMatch
{ <field>: { $elemMatch: { <query1>, <query2>, ... } } }
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.

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

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

统计查询

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

分页查询

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

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

排序查询

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

更新集合中的文档

更新语法使用如下方法

db.<collection>.update(query, update, options)

db.<collection>.updateOne(query, update, options)

db.<collection>.updateMany(query, update, options)

db.<collection>.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,表示只更新匹配到的第一个文档。

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

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

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

更新操作符

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

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

实现局部更新

$unset
{ $unset: { <field1>: "", ... } }

The $unset operator deletes a particular field. 

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

$inc
{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }

The $inc operator increments a field by a specified value

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

$mul
{ $mul: { field: <number> } }

Multiply the value of a field by a number. 

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

$rename
{$rename: { <field1>: <newName1>, <field2>: <newName2>, ... } }

The $rename operator updates the name of a field

实现字段重命名

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

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.

<typeSpecification> 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: { <field1>: <value1>, ... } }
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: { <field1>: <value1>, ... } }
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.

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

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

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

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

 

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

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

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

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

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

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

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

这里的ISODate对应JavaScript的Date类型

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

数组更新操作符用法含义
$(update)
db.collection.update(
   { <array>: value ... },
   { <update operator>: { "<array>.$" : 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: { <field1>: <value1>, ... } }
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: { <field>: <-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: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
$push
{ $push: { <field1>: <value1>, ... } }
The $push operator appends a specified value to an array.
$each
{ $push|$addToSet: { <field>: { $each: [ <value1>, <value2> ... ] } } }

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

$slice
{
  $push: {
     <field>: {
       $each: [ <value1>, <value2>, ... ],
       $slice: <num>
     }
  }
}

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: {
     <field>: {
       $each: [ <value1>, <value2>, ... ],
       $sort: <sort specification>
     }
  }
}

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: {
    <field>: {
       $each: [ <value1>, <value2>, ... ],
       $position: <num>
    }
  }
}

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.

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

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

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

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

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

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

 

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

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

删除集合中的文档

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

db.<collection>.remove(query, options)

db.<collection>.deleteOne(query)

db.<collection>.deleteMany(query)

参数解析

query参数就是查询条件

options.jusetOne参数用于remove方法

方法解析

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

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

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

 

 

 

索引

啥是索引

当我们需要查找集合中符合条件的文档时,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

B-Tree Visualization (usfca.edu)

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

常用的索引类型

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

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

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

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

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

创建索引

db.<collection>.createIndex(keys, options)

参数解析

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

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

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

例子

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

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

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

一共查询了0.193秒

我们建立一个索引

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

 

 

之后,在进行相同的查询

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

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

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

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

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

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

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

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

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

 

查询索引

db.<collection>.getIndexes()

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

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

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

删除索引

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

需要。

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

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

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

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

db.<collection>.dropIndex(indexName)

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

db.<collection>.dropIndexed()

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

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

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

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

这里stage是FETCH,表示基于了索引查询 

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿甘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值