MongoDB

本文最后更新于:2021年6月8日 下午

信息

MongoDB 是一个由C++编的 写基于分布式文件存储的数据库。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的

存储

MongoDB 将数据存储为一个文档,数据结构由键值对组成
MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组

特点

  • MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易
  • MongoDB记录中设置任何属性的索引来实现更快的排序
  • 可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性
  • 如果负载的增加 ,它可以分布在计算机网络中的其他节点上这就是所谓的分片
  • Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组
  • 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段
  • Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理
  • Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作
  • GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件
  • MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用

链接

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

安装

Docker安装

拉取镜像

1
docker pull mongo

如果想要安装非最新版,可以查询Docker镜像信息信息
或者使用docker search mongo来查看可用版本

在拉取完毕后,可以使用docker images来查看镜像信息

运行容器

1
docker run -itd --name mongo -p 27018:27017 mongo --auth

參數説明:

  • -p 27018:27017
    映射容器服务的27017端口到宿主机的27018端口。外部可以直接通过ip:27018 访问到mongo的服务
  • –auth
    需要密码才能访问容器服务
  • -itd
    实际上是-i -t -d的简写
    • -i 以交互模式运行容器,通常与 -t 同时使用
    • -t 为容器重新分配一个伪输入终端,通常与 -i 同时使用
    • -d 后台运行容器,并返回容器ID

可以通过docker ps命令查看容器的运行信息

初始化

进入容器控制台

1
docker exec -it mongo mongo admin

创建一个超级账户

1
db.createUser({ user:'用户名',pwd:'密码',roles:[ { role:'root', db: 'admin'}]});

在创建完用户以后尝试使用新用户进行连接

1
db.auth('用户名', '密码')

如果正确可行的话,会返回1,这也意味着账户切换成功

其它创建账户

1
db.createUser({user: "账户名", pwd: "密码", roles: [{ role: "角色类型", db: "数据库名称" }]})   

可用角色类型:

  • 数据库用户角色(Database User Roles)
    • read:授予User只读数据的权限
    • readWrite:授予User读写数据的权限
  • 数据库管理角色(Database Administration Roles)
    • dbAdmin:在当前dB中执行管理操作
    • dbOwner:在当前DB中执行任意操作
    • userAdmin:在当前DB中管理User
  • 备份和还原角色(Backup and Restoration Roles)
    • backup
    • restore
  • 跨库角色(All-Database Roles)
    • readAnyDatabase:授予在所有数据库上读取数据的权限
    • readWriteAnyDatabase:授予在所有数据库上读写数据的权限
    • userAdminAnyDatabase:授予在所有数据库上管理User的权限
    • dbAdminAnyDatabase:授予管理所有数据库的权限

MongoDB名词概念

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

数据库 database

一个mongodb中可以建立多个数据库
MongoDB的默认数据库为”db”,该数据库存储在data目录中
MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中

数据库命名

数据库名为utf-8字符串,符合以下标准:

  • 非空
  • 不含(空格)、.、$、/、\和\0 (空字符)
  • 字母全部小写
  • 小于64字节

常用操作

命令 信息
show dbs 查询所有含有文档的数据库信息
db 显示当前数据库信息
use 数据库名 连接到指定数据库
如果数据库不存在则创建数据库
db.dropDatabase() 删除当前数据库

保留的数据库

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

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

集合 Collection

常用操作

创建集合

1
db.createCollection(name, options)
  • name:集合名

  • option:可选参数

    • capped(bool)
      如果为 true,则创建固定集合。为 true 时,必须指定 size 参数

      固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档

    • size (int)
      为固定集合指定一个最大值,即字节数
      如果 capped 为 true,也需要指定该字段

    • max (int)
      指定固定集合中包含文档的最大数量

案例: 创建集合

1
2
3
4
use test
>>>switched to db test
db.createCollection("runoob")
>>>{ "ok" : 1 }

查看集合

命令有两个,随便一个都可以

1
2
show collections
show tables

删除集合

1
db.collection.drop()

案例: 删除集合

1
2
3
4
5
6
7
8
9
10
11
12
13
use mydb
>>>switched to db mydb

show collections
>>>mycol
>>>mycol2
>>>system.indexes
>>>runoob

show collections
>>>mycol
>>>system.indexes
>>>runoob

文档 Document

文档的数据结构和 JSON 基本一样
所有存储在集合中的数据都是 BSON 格式
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称

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

文档键命名规范

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

常用操作

插入

db.collection.insertOne

作用:如果主键存在则更新数据,如果不存在就插入数据

1
2
3
4
5
6
db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)
  • document:要写入的文档
  • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求
  • ordered:指定是否按顺序写入,默认 true,按顺序写入

案例: 插入文档

1
db.products.insertOne( { item: "card", qty: 15 } );

db.collection.insertMany

作用:向集合插入一个多个文档

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

参数与db.collection.insertOne一致

案例 往 products集合 插入一些数据

1
2
3
4
5
6
db.products.insertMany( [
{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps" , qty: 30 }
] );
}

db.collection.insert()

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

案例:向 runoob 数据库 的 col 集合中插入数据

1
2
3
4
5
6
7
db.col.insert({title: 'MongoDB 教程', 
description: 'MongoDB 是一个 Nosql 数据库',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})

删除

  • db.collection.deleteOne
    删除集合中的一个文档

    1
    2
    3
    4
    5
    6
    7
    8
    db.collection.deleteOne(
    <filter>,
    {
    writeConcern: <document>,
    collation: <document>,
    hint: <document|string> // Available starting in MongoDB 4.4
    }
    )
    • filter
      指定删除条件,如果设置为{}则删除第一个文档
    • writeConcern
      可选参数,写入关注,其值为一个文档
    • collation
      可选参数,指定用于操作的集合
    • hint
      可选参数,指定用于支持查询谓词的索引的文档或字符串

    假设现在有一个order集合,集合中有这么个文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    _id: ObjectId("563237a41a4d68582c2509da"),
    stock: "Brent Crude Futures",
    qty: 250,
    type: "buy-limit",
    limit: 48.90,
    creationts: ISODate("2015-11-01T12:30:15Z"),
    expiryts: ISODate("2015-11-01T12:35:15Z"),
    client: "Crude Traders Inc."
    }

    案例: 根据删除_id233的文档

    1
    db.orders.deleteOne( { "_id" : ObjectId("233") } );
  • db.collection.deleteMany
    删除集合中所有符合filter的文档
    文档地址

    1
    2
    3
    4
    5
    6
    7
    db.collection.deleteMany(
    <filter>,
    {
    writeConcern: <document>,
    collation: <document>
    }
    )

    参数同db.collection.deleteOne

    案例
    假设现在有一个order集合,集合中有这么个文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    _id: ObjectId("563237a41a4d68582c2509da"),
    stock: "Brent Crude Futures",
    client: "Crude Traders Inc."
    }
    {
    _id: ObjectId("123437a41a423423444509da"),
    stock: "Brent Crude Futures",
    client: "Crude Traders Inc."
    }

    案例: 根据删除clientCrude Traders Inc.的文档

    1
    db.orders.deleteMany( { "client" : "Crude Traders Inc." } );

    更改

    db.collection.update

    作用:更新已存在的文档

使用格式

1
2
3
4
5
6
7
8
9
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
  • query : update的查询条件,类似sql update查询内where后面的
  • update : update的对象和一些更新的操作符(如$,$inc…)等,也可以理解为sql update查询内set后面的
  • upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
  • multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
  • writeConcern :可选,抛出异常的级别

案例:更改titlemysql

假设现在有一个order集合,集合中有这么个文档

1
2
3
4
5
{
"_id" : ObjectId("1"),
"title" : "MongoDB",
"description" : "MongoDB 是一个 Nosql 数据库",
}

更改titlemysql

1
db.col.update({'title':'MongoDB'},{$set:{'title':'mysql'}})

需要注意的是,这语句只会修改第一个发现的文档
如果想要修改多个,需要添加 {multi:true}

1
db.col.update({'title':'MongoDB'},{$set:{'title':'mysqls'}},{multi:true})

官方文档説明:https://docs.mongodb.com/master/reference/command/update/

db.collection.save

作用:通过传入的文档来替换已有文档,主键存在就更新,不存在就插入

1
2
3
4
5
6
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
  • document : 文档数据
  • writeConcern :可选,抛出异常的级别

实际上就是用一个新的文档来覆盖旧的,关键是要让_id主键一致

1
2
3
4
db.col.save({
"_id" : ObjectId("1"),
"title" : "MongoDB",
})

查询

db.collection.find

1
db.collection.find(query, projection)
  • query :可选,使用查询操作符指定查询条件
  • projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)

如果你希望返回比较好看的形式,可以用pretty

1
db.col.find().pretty()

实际上还有一个 findOne 方法专门用来找单个文档

限制查询数量 Limit

如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDBLimit方法
limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数
未指定limit方法中的参数则显示集合中的所有数据

1
db.COLLECTION_NAME.find().limit(NUMBER)

案例:查找前两条数据

1
db.col.find({},{"title":1,_id:0}).limit(2)

跳过查询数量 Skip()

除了可以使用limit方法来读取指定数量的数据外,还可以使用skip方法来跳过指定数量的数据
skip方法同样接受一个数字参数作为跳过的记录条数。默认参数为 0

案例:查找第二条数据

1
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
1
db.col.find({},{"title":1,_id:0}).limit(1).skip(1)

排序 sort()

sort 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式
1 为升序排列,而 -1 是用于降序排列

1
db.COLLECTION_NAME.find().sort({KEY:1})

Filter

比较逻辑

操作 格式 范例 RDBMS中的类似语句
等于 {<key>:<value>} db.col.find({“by”:”123”}).pretty() where by = ‘123’
小于 {<key>:{$lt:<value>}} db.col.find({“likes”:{$lt:50}}).pretty() where likes < 50
小于或等于 {<key>:{$lte:<value>}} db.col.find({“likes”:{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.col.find({“likes”:{$gt:50}}).pretty() where likes > 50
大于或等于 {<key>:{$gte:<value>}} db.col.find({“likes”:{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.col.find({“likes”:{$ne:50}}).pretty() where likes != 50

逻辑运算

and 与逻辑

同时传入多个键即可

1
db.col.find({key1:value1, key2:value2}).pretty()

or 或逻辑

使用关键词$or

1
2
3
db.col.find({
$or: [{key1: value1}, {key2:value2}]
}).pretty()

not 非逻辑

使用关键词$not

1
2
3
db.col.find(
{ key: { $not: { $gt: value } } }
)

$type 操作符

$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果

类型 数字 备注
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined 6 已废弃
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255 Query with -1
Max key 127

案例:寻找标题为字符串类型的文档

1
2
db.col.find({"title" : {$type : 2}})
db.col.find({"title" : {$type : 'string'}})

多集合查询

实际上,多集合查询是利用聚合的 $lookup多表关联 实现的
具体见后

索引

索引通常能够极大的提高查询的效率
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的

常用操作

创建

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

案例:以title键建立索引

1
db.col.createIndex({"title":1})

案例:以titlename键在后台建立索引

1
db.values.createIndex({title: 1, name: 1}, {background: true})

通过在创建索引时加 background:true 的选项,让创建工作在后台执行

查看

  • 查看集合索引
    1
    db.col.getIndexes()
  • 查看集合索引大小
    1
    db.col.totalIndexSize()

    删除

  • 删除集合所有索引
    1
    db.col.dropIndexes()
  • 删除集合指定索引
    1
    db.col.dropIndex("索引名称")

聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果

aggregate() 方法

MongoDB中聚合的方法使用 aggregate()

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

案例:计算每个作者所写的文章数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
_id: ObjectId(7df78ad8902c)
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'runoob.com',
},
{
_id: ObjectId(7df78ad8902d)
title: 'NoSQL Overview',
description: 'No sql database is very fast',
by_user: 'runoob.com',
},
{
_id: ObjectId(7df78ad8902e)
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
},
1
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])

管道的概念

管道在UnixLinux中一般用于将当前命令的输出结果作为下一个命令的参数。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

聚合框架中常用操作:

运算符 信息
$project 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档
$match 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作
$limit 用来限制MongoDB聚合管道返回的文档数
$skip 在聚合管道中跳过指定数量的文档,并返回余下的文档
$unwind 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
$group 将集合中的文档分组,可用于统计结果
$sort 将输入文档排序后输出
$geoNear 输出接近某一地理位置的有序文档
$lookup 多表关联

案例:结果中只有_id,tilte和author三个字段

1
2
3
4
5
6
db.article.aggregate(
{ $project : {
title : 1 ,
author : 1 ,
}}
);

实际上有还有一个默认添加的字段_id,如果想要取消指定_id:0即可

$lookup多表查询

主要功能
处理每个输入的文档,输出的新文档中会包含一个新生成的数组列(户名可根据需要命名新key的名字),如果没有内容适配,集合为空(即[ ]

语法

1
2
3
4
5
6
7
8
9
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
语法值 解释说明
from 同一个数据库中等待被 Join 的集合
localField Join集合中的 键
如果集合中的某个文档没有这个键,那么就认为其实这个文档有这个键,只是个键的值为 null
foreignField Join的集合的 键
如果集合中的某个文档没有这个键,那么就认为其实这个文档有这个键,只是个键的值为 None
as 为输出文档的新增键命名
如果输入的集合中已存在该值,则会覆盖掉

例: 查询武器的所有信息

1
2
3
4
5
6
7
8
集合 items
{ "_id":1, "item":"大剑", "price":1200, "quality":100 },
{ "_id":2, "item":"弓箭", "price":800, "quality":80 },

集合 effect
{ "_id":1, "name":"弓箭", "attack": 90, "scope" : 300 },
{ "_id":2, "name":"大剑", "attack": 200, "scope" : 16 },
{ "_id":3, "name":"大炮", "attack": 360, "scope" : 900 },

进行聚合计算

1
2
3
4
5
6
7
8
9
10
11
db.items.aggregate([
{
$lookup:
{
from: "effect",
localField: "item",
foreignField: "name",
as: "item_info"
}
}
])

得到结果

1
2
3
4
[
{'_id': 1, 'item': '大剑', 'price': 1200, 'quality': 100, 'item_info': []},
{'_id': 2, 'item': '弓箭', 'price': 800, 'quality': 80, 'item_info': []}
]

未完成:https://www.runoob.com/mongodb/mongodb-replication.html


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!