1 简介
说明: 支持的数据结构非常松散,是一种类似于JSON叫BSON;MongoDB中的记录是一个文档,由字段和值(field:value)组成的数据结构。一个文档就相当于一个JSON对象。field是字符型,value除了基本类型外还可以是文档、普通数组和文档数组。
作用: 解决关系型数据库(如MySQL),面对三高(高并发读写、海量数据高效率存储和访问、数据库高扩展性和高可用)需求的力不从心。
应用场景:
- 社交场景,存储用户及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。
- 游戏场景,存储用户及用户装备积分等信息,以内嵌文档的形式存储,方便查询、高效率存储和访问。
- 物流场景,存在订单信息,订单状态是不断更新的,以内嵌数据的形式存储,一次查询就能读取订单的所有变更记录。
- 视频直播,存储用户及点赞互动信息。
以上应用场景共同点:
- 数据量大
- 读写频繁
- 数据价值较低,对事务要求不高
什么时候选择
- 应用不需要事务支持
- 项目需求无法确定,数据模型会变(即字段不固定)
- 存储量大
- 需要99.999%高可用
- 需要大量地理位置、文本查询
1.1 与MySQL对比
docker安装mongodb
Navicat显示默认系统数据库
1.2 默认的三个数据库
- admin: root数据库,如果将用户添加到该数据库,则默认继承所有数据库权限。相当于用户添加到该数据库则自动成为管理员。
- local: 集群时,该数据库不会被其他数据库所复制,可以用于存储仅限于本台服务器的任意集合。
- config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
数据库名.dropDatabase()
用于删除数据库。
1.3 基本命令
- docker进入mongodb界面
docker exec -it mongodb mongosh
- 用户登录
use admin
,db.auth(“root”, “root”)
- 列出数据库
show dbs
- 新建/打开数据库
use testdb
(如果有就打开,没有就新建) - 查看当前正在使用的数据库
db
- 查看当前库中的表
show collections
或者show tables
- 集合显式创建
db.createCollection("集合名")
- 隐式创建是,向一个集合插入文档时,如果集合不存在,会自动创建(常用)
- 删除集合
db.集合名.drop()
2 文档的CRUD
2.1 新增文档
insert()
和save()
等价
db.collection.insert(
<document or array of documents>,
{
writeConcern: 性能级别,
orderd: <bolean>按顺序插入文档
}
)
eg:
向comment集合中插入一条文档:
db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明
媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null})
注:
1)comment集合如果不存在,则会隐式创建
2)mongo中的数字,默认情况下是double类型,如果要存整型,必须使用函数NumberInt(整型数字),否则取出来就有问题了。
3)插入当前日期使用 new Date()
4)插入的数据没有指定 _id ,会自动生成主键值
5)如果某字段没值,可以赋值为null,或不写该字段。
批量插入,用[]
包含多个文档,文档间用,
分隔
db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我
他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-
05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
{"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔
悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
{"_id":"3","articleid":"100001","content":"我一直喝凉开水,冬天夏天都喝。","userid":"1004","nickname":"杰克船
长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
{"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭,影响健康。","userid":"1003","nickname":"凯
撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
{"_id":"5","articleid":"100001","content":"研究表明,刚烧开的水千万不能喝,因为烫
嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-
06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
注:如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。
2.2 删除文档
db.collection.remove(条件)
eg:
- 删除集合中所有的文档
db.comment.remove({})
- 删除
name
为张三
的记录
db.comment.remove({name:"张三"})
2.3 修改文档
db.collection.update(query, update, options)
//或
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2
}
)
Parameter | Type | Description |
---|---|---|
query | document | 更新的选择条件。可以使用与find()方法中相同的查询选择器,类似sql update查询内where后面部分。在3.0版中进行了更改:当使用upsert:true执行update()时,如果查询使用点表示法在_id字段上指定条件,则MongoDB将拒绝插入新文档。 |
update | document 或者pipeline | 更新的内容 |
upsert | boolean | 可选。如果设置为true,则在没有与查询条件匹配的文档时创建新文档。默认值为false,如果找不到匹配项,则不会插入新文档。 |
multi | boolean | 可选。如果设置为true,则更新符合查询条件的多个文档。如果设置为false,则更新一个文档。默认值为false。 |
eg:
- 覆盖的修改
将_id为1的文档的点赞量修改为1001。
执行后,会发现这条文档除了likenum字段其它字段都不见了。
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
- 局部修改
为了解决这个问题,我们需要使用修改器$set
来实现,这样其他未修改的字段就不会被覆盖。
将_id为2的记录的浏览量修改为888
db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
- 批量修改(
multi
)
更新所有age为19的用户名为张三
//默认只修改第一条数据
db.comment.update({age:NumberInt(19)},{$set:{name:"张三"}})
//修改所有符合条件的数据
db.comment.update({age:NumberInt(19)},{$set:{name:"张三"}},{multi:true})
- 原有值的基础上做增量修改
使用$inc
运算符实现
将_id为3的点赞数增加3个
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
2.4 查询文档
db.collection.find(<query>, [projection])
Parameter | Type | Description |
---|---|---|
query | document | 可选。使用查询运算符指定选择筛选器。若要返回集合中的所有文档,请省略此参数或传递空文档( {} )。 |
projection | document | 可选。指定要在与查询筛选器匹配的文档中返回的字段(投影)。若要返回匹配文档中的所有字段,请省略此参数。 |
eg:
查询所有
db.comment.find()
或
db.comment.find({})
eg:
条件查询
db.comment.find({userid:'1003'})
eg:
条件查询只想要第一条数据
db.comment.findOne({userid:'1003'})
投影查询
查询结果不显示所有字段,只显示指定字段(_id
默认显示)
eg:
查询所有数据,但只显示_id、userid、name
db.comment.find({},{userid:1,name:1})
eg:
查询userid为1001的文档,但只显示userid、name
,不显示_id
db.comment.find({userid:"1001"},{userid:1,nickname:1,_id:0})
3 文档分页查询
3.1 count查询
db.collection.count(query, options)
Parameter | Type | Description |
---|---|---|
query | document | 查询条件 |
options | document | 可选项,暂时不可用 |
eg:
- 统计集合的所有文档数
db.comment.count()
- 统计集中中age为19的文档数
db.comment.count({age:NumberInt(19)})
3.2 分页查询
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
使用limit()
方法来读取指定数量的数据,使用skip()
方法来跳过指定数量的数据。
eg:
- 返回前7条数据(
limit(TopN)
为空,默认值为20)
db.comment.find().limit(3)
- 前N条记录跳过,默认值为0
db.comment.find().skip(3)
- 分页查询:每页显示2条数据
//第一页,每页显示2条,跳过0条
db.comment.find().limit(2).skip(0)
//第二页,每页显示2条,跳过整个文档的前两条
db.comment.find().limit(2).skip(2)
//第三页,每页显示2条,跳过整个文档的前4条
db.comment.find().limit(2).skip(4)
3.3 排序查询
db.COLLECTION_NAME.find().sort({KEY:1})
或
db.COLLECTION_NAME.find().sort(排序方式)
sort()内指定排序字段,1表示升序,-1表示降序
eg:
对userid降序排序,如果相同则使用likenum升序排序
db.comment.find().sort({userid:-1,likenum:1})
注:skip、limit、sort的优先级:sort()>skip()>limit(),该优先级和语句的顺序无关。
4 文档其他查询
4.1 模糊查询
正则可以实现模糊查询,使用的js语法,格式为:
db.collection.find({field:/正则表达式/})
或
db.集合.find({字段:/正则表达式/})
eg:
- 查询content中包含"开水"的所有文档,类似于
%开水%
db.comment.find({content:/开水/})
- 查询content中以"开水”开头的所有文档
db.comment.find({content:/^开水/})
4.2 比较查询
db.集合名称.find({ field : { $gt: value }}) // 大于: field > value
db.集合名称.find({ field : { $lt: value }}) // 小于: field < value
db.集合名称.find({ field : { $gte: value }}) // 大于等于: field >= value
db.集合名称.find({ field : { $lte: value }}) // 小于等于: field <= value
db.集合名称.find({ field : { $ne: value }}) // 不等于: field != value
4.3 包含查询
包含使用$in
操作符,不包含使用$nin
操作符。
eg:
- 查询userid中包含1003和1004的所有文档
db.comment.find({userid:{$in:["1003","1004"]}})
- 查询userid中不包含1003和1004的所有文档
db.comment.find({userid:{$nin:["1003","1004"]}})
4.4 and和or查询
相当于SQL的where xxx and xxx
$and:[ { },{ },{ } ]
eg:
查询likenum>=700并且<2000的所有文档
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
$or:[ { },{ },{ } ]
eg:
查询userid为1003,或者likenum<1000的所有文档
db.comment.find({$or:[ {userid:"1003"} ,{likenum:{$lt:1000} }]})
5 索引
索引提高查询效率,MongoDB使用B-Tree。
5.1 单字段索引
对单个字段建立升序/降序索引(具体是ASC还是DESC不重要,Mongo可以从任何方向遍历索引)
5.2 复合索引
多个字段来进行排序索引,比如{userid:1,score:-1}
5.3 其他索引
地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(Hashed Indexes)。
地理空间索引(Geospatial Index)
为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引。
文本索引(Text Indexes)
MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如“the”、“a”、“or”),而将集合中的词作为词干,只存储根词。
哈希索引(Hashed Indexes)
为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支持相等匹配,不支持基于范围的查询。
5.4 索引语句
- 创建索引
db.collection.createIndex(keys, options)
Parameter | Type | Description |
---|---|---|
keys | document | 包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段上的升序索引,请指定值1;对于降序索引,请指定值-1。 |
options | document | 可选 |
eg:
单个索引
db.comment.createIndex({userid:1})
返回结果:
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
复合索引
db.comment.createIndex({userid:1,nickname:-1})
返回结果:
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
- 查看索引
db.collection.getIndexes()
注:MongoDB 3.0+才行
- 移除索引
移除指定索引:
db.collection.dropIndex(index)
Parameter | Type | Description |
---|---|---|
index | String 或者 ducoment | 指定删除的索引。可以是索引名或者创建时的索引文档(删除文本索引需要指定索引名) |
eg:
删除comment集合中userid字段上的升序索引
db.comment.dropIndex({userid:1})
移除集合中所有的索引:
db.comment.dropIndexes()
注:_id 的字段的索引是无法删除的,只能删除非 _id 字段的索引。
6 MongoTemplate
Spring Boot 使用 MongoTemplate 操作 MongoDB
springboot MongoTemplate 之Criteria+Query实现常见操作
MongoDB学习02:使用MongoTemplate操作MongoDB
MongoTemplate的基本使用方法