MongoDB


MongoDB 相关概念


传统的关系型数据库(如MySQL),在数据操作的 “三高” 需求以及应对 Web2.0 的网站需求面前,显得力不从心。

  • 解释 “三高” 需求:
    • High performance:对数据库 高并发 读写的需求
    • Huge Storage:对海量数据的 高效率存储和访问 的需求
    • High Scalability && High Availability:对数据库的 高可扩展性和高可用性 的需求

而 MongoDB可应对“三高”需求

具体的应用场景如:

  • 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
  • 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、高效率存储和访问
  • 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将 订单所有的变更读取出来
  • 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
  • 视频直播,使用 MongoDB 存储用户信息、点赞互动信息等

这些应用场景中,数据操作方面的共同特点是:

  1. 数据量大
  2. 写入操作频繁(读写都很频繁)
  3. 价值较低的数据,对 事务性 要求不高

对于这样的数据更适合使用 MongoDB 来实现数据的存储。

选用MongoDB的场景:

在架构选型上,除了上述的三个特点外,可以考虑以下的一些问题:

  • 应用 不需要事务 及复杂 join 支持
  • 新应用,需求会变,数据模型无法确定,想快速迭代开发
  • 应用需要 2000-3000 以上的读写QPS(更高也可以)
  • 应用需要 TB 甚至 PB 级别数据存储
  • 应用发展迅速,需要能快速水平扩展
  • 应用要求存储的数据不丢失
  • 应用需要 99.999% 高可用
  • 应用需要大量的地理位置查询、文本查询

MongoDB 简介


MongoDB 是一个 开源、高性能、无模式 的 文档型数据库,当初的设计就是用于简化开发和方便扩展,是 NoSQL数据库产品中的一种。是 最像关系型数据库(MySQL)的非关系型数据库

它支持的数据结构非常松散,是一种类似于 JSON 的 格式叫 BSON,所以它 既可以存储比较复杂的数据类型,又相当的灵活

MongoDB中的记录是一个文档,它是一个由 字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。字段的数据类型是 字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组

MySQLvsMongoDB

MySQL与MongoDB之间的关系


MongoDB 数据模型


MongoDB的最小存储单位就是 文档(document)对象

  • 文档(document)对象对应于关系型数据库的行
  • 数据在MongoDB中以 BSON(Binary-JSON)文档 的格式存储在磁盘上

BSON(Binary Serialized Document Format)是一种 类 json 的一种二进制形式的存储格式,简称 Binary JSON

  • BSON 和 JSON 一样,支持 内嵌的文档对象和数组对象,但是 BSON 有 JSON 没有的一些数据类型,如Date 和 BinData 类型
  • BSON 采用了 类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的三个特点,可以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高,但它的缺点是空间利用率不是很理想
  • BSON 中,除了基本的JSON类型:string、integer、boolean、double、null、array 和 object,mongo 还使用了特殊的数据类型。这些类型包括:date、object id、binary data、regular expression 和 code。每一个驱动都 以特定语言的方式 实现了这些类型,查看你的驱动的文档来获取详细信息

BSON数据类型参考列表


MongoDB 特点


  1. 高性能:MongoDB提供 高性能的数据持久性
    • 特别是, 对 嵌入式数据模型 的支持减少了数据库系统上的 I/O 活动
    • 索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地理位置索引可用于构建各种 O2O 应用)
    • mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求。
    • Gridfs 解决文件存储的需求
  2. 高可用性:MongoDB的复制工具称为副本集(replica set),它可提供自动故障转移和数据冗余
  3. 高扩展性:MongoDB提供了水平可扩展性作为其核心功能的一部分。 分片将数据分布在一组集群的机器上。(海量数据存储,服务能力水平扩展) 从3.4开始,MongoDB支持基于片键创建数据区域。在一个平衡的集群中,MongoDB将一个区域所覆盖的读写只定向到该区域内的那些 片
  4. 丰富的查询支持:MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如:数据聚合、文本搜索和地理空间查询等
  5. 其他特点:如 无模式(动态模式)、灵活的文档模型

基本常用命令


举例:数据库 articledb

articledb


选择 和 创建 数据库


基本语法:

1
use 数据库名称
  • 如果 数据库不存在 则 自动创建,但是 use 创建的数据库并没有真正创建,而是在内存中,并没有持久化到磁盘,只有在创建数据表后要再插入一个文档(记录),数据库才会真正创建

eg:

1
2
3
4
5
use articledb


> use articledb
switched to db articledb

查看 所有的数据库 命令:

1
2
3
4
5
6
7
8
9
10
show dbs
或者
show databases



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

查看 当前正在使用的数据库 命令:

1
db

eg:

1
2
> db
articledb

另外: 数据库名可以是满足以下条件的任意 UTF-8 字符串

  • 不能是空字符串("")
  • 不得含有 ' '(空格)、.、$、/、\ 和 \0 (空字符)
  • 应全部小写
  • 最多 64 字节

删除 数据库


基本语法:

1
db.dropDatabase()
  • 提示:主要用来 删除已经持久化的数据库

eg:

1
2
> db.dropDatabase()
{ "ok" : 1 }

集合 操作


集合,类似关系型数据库中的 表


集合 的 显式创建


基本语法:

1
db.createCollection(name)

参数说明:

  • name:要创建的集合名称

eg:

1
2
> db.createCollection("mycollection")
{ "ok" : 1 }

集合 的 删除


基本语法:

1
2
3
db.collection.drop()
或者
db.集合名称.drop()
  • 返回值说明:如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false

eg:

1
2
> db.mycollection.drop()
true

文档基本 CRUD


文档(document)的数据结构和 JSON 基本一样

所有存储在集合中的数据都是 BSON 格式


文档 的 插入


单个文档的插入:

使用 insert() 或 save() 方法向集合中插入文档

基本语法:

1
2
3
4
5
6
7
db.collection.insert(
<document or array of documents>,
{
writeConcern: <document>,
ordered: <boolean>
}
)
  • 参数说明:参数说明

eg:

1
2
> db.comment.insert({"articleid":100000, "content":"今天天气真不错", "userid":"1001","nickname":"阿三","createtime":new Date(), "likenum":NumberInt(10),"state":null})
WriteResult({ "nInserted" : 1 })

查询是否插入成功:

1
2
> db.comment.find()
{ "_id" : ObjectId("669f14e2a7618ffe66c5ede6"), "articleid" : 100000, "content" : "今天天气真不错", "userid" : "1001", "nickname" : "阿三", "createtime" : ISODate("2024-07-23T02:26:42.701Z"), "likenum" : 10, "state" : null }

说明:

  1. comment 集合如果不存在,则会隐式创建
  2. mongo 中的数字,默认情况下是 double 类型,如果要存整型,必须使用函数 NumberInt (整型数字),否则取出来就有问题了
  3. 插入当前日期使用 new Date()
  4. 插入的数据没有指定 _id ,会自动生成主键值
  5. 如果某字段没值,可以赋值为 null,或不写该字段

批量插入:

基本语法:

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

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> 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"}
... ]);
{
"acknowledged" : true,
"insertedIds" : [
"1",
"2",
"3",
"4",
"5"
]
}
  • 插入时指定了 _id ,则主键就是该值
  • 如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉
  • 因为批量插入由于数据较多容易出现失败,因此,可以使用 try catch 进行异常捕捉处理,测试的时候可以不处理

文档 的 基本查询


基本语法:

1
db.collection.find(<query>, [projection])
  • 参数说明:参数说明

eg:

  • 查询所有:

    1
    2
    3
    db.comment.find()
    或者
    db.comment.find({})
  • 按一定条件来查询

    1
    db.comment.find({userid:"1003"})
  • 查询并只需要返回符合条件的第一条数据

    1
    db.comment.findOne({userid:"1003"})
  • 投影查询

    1
    2
    3
    4
    5
    # 查询结果只显示 _id、userid、nickname
    # 默认 _id 会显示
    > db.comment.find({userid:"1003"},{userid:1,nickname:1})
    { "_id" : "4", "userid" : "1003", "nickname" : "凯撒" }
    { "_id" : "5", "userid" : "1003", "nickname" : "凯撒" }
    • 如果 要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)
    1
    2
    3
    4
    # 查询结果只显示 userid、nickname,不显示 _id
    > db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0})
    { "userid" : "1003", "nickname" : "凯撒" }
    { "userid" : "1003", "nickname" : "凯撒" }

注意:

批量插入由于数据较多容易出现失败,因此,可以使用 try catch 进行异常捕捉处理,测试的时候可以不处理

eg:

1
2
3
4
5
6
7
8
9
10
11
12
try {
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"}
]);
} catch (e) {
print (e);
}

文档 的更新


基本语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
db.collection.update(query, update, options)
# query:查询条件
# update:更新条件

或者

db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> # Available starting in MongoDB 4.2
}
)

eg:

覆盖修改:

1
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
  • 执行后会发现,这条文档 除了 likenum 字段 其它字段都不见了

局部修改:

1
db.comment.update({_id:"1"},{$set:{likenum:NumberInt(1001)})
  • 解决覆盖修改的问题,需要使用修改器 $set 来实现

批量修改:

1
2
# 修改所有符合条件的数据
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒大帝"}},{multi:true})
  • 提示:如果不加后面的参数,则只更新符合条件的第一条记录
  • MongoDB 修改默认只修改 第一条数据

列值增长的修改

1
2
# 对3号数据的点赞数,每次递增1
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
  • 实现对某列值在原有值的基础上进行增加或减少,可以使用 $inc 运算符来实现

文档 的 删除


基本语法:

1
db.集合名称.remove(条件)

eg:

数据全部删除:

1
db.comment.remove({})

删除 _id=1的记录:

1
db.comment.remove({_id:"1"})

文档 的 分页查询


统计查询


统计查询使用 count() 方法,基本语法:

1
db.collection.count(query, options)

参数说明:参数说明

eg:

统计所有记录数:

1
db.comment.count()

按条件统计记录数:

1
2
> db.comment.count({articleid:"100001"})
5

分页列表查询


使用 limit() 方法来 读取指定数量的数据,使用 skip() 方法来 跳过指定数量的数据

基本语法:

1
db.collection.find().limit(NUMBER).skip(NUMBER)

eg:

要返回指定条数的记录,可以在 find 方法后调用 limit 来返回结果(TopN),默认值 20

1
db.comment.find().limit(3)

skip 方法接受一个数字参数作为跳过的记录条数。(前N个不要),默认值是 0

1
2
3
> db.comment.find().skip(3)
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T11:01:02.521Z"), "likenum" : 3000, "state" : "1" }

排序查询


sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中:1 为 升序 排列,而 -1 是于 降序 排列

基本语法:

1
2
3
db.collection.find().sort({KEY:1})
或者
db.集合名称.find().sort(排序方式)

eg:

对 userid 降序排列,并对 访问量 进行升序排列:

1
db.comment.find().sort({userid:-1, likenum:1})

注意:

skip()、limilt()、sort() 三个放在一起执行的时候,执行的顺序是 先 sort(),然后是 skip(),最后是显示的 limit(),和命令编写顺序无关


文档 的 复杂查询


正则的复杂条件查询


MongoDB 的模糊查询是通过 正则表达式 的方式实现的

基本语法:

1
2
3
db.collection.find({field:/正则表达式/})
或者
db.集合.find({字段:/正则表达式/})

eg:

查询评论内容包含 “开水” 的所有文档:

1
2
3
> db.comment.find({content:/开水/})
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }

比较查询


<、<=、>、>= 这种 操作符 是很常用的,基本语法:

1
2
3
4
5
6
7
8
9
10
# 大于: field > value
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 }})

eg:

查询评论点赞数大于2500的记录:

1
2
> db.comment.find({likenum:{$gt:NumberInt(2500)}})
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T11:01:02.521Z"), "likenum" : 3000, "state" : "1" }

包含查询


包含 使用 $in 操作符

eg:查询评论的集合中 userid 字段包含1003 或 1004 的文档

1
db.comment.find({userid:{$in:["1003", "1004"]}})

不包含 使用 $nin 操作符

eg:查询评论集合中 userid 字段不包含 1003 和 1004 的文档

1
db.comment.find({userid:{$nin:["1003", "1004"]}})

条件连接查询


如果需要查询 同时满足两个以上条件,需要使用 $and 操作符将条件进行关联。(相当于 SQL 的 and)

基本语法:

1
$and:[ { },{ },{ } ]

eg:

查询评论集合中 likenum 大于等于 700 并且小于 2000 的文档

1
db.comment.find({$and:[{likenum:{$gte: NumberInt(700)}},{likenum:{$lt: NumberInt(2000)}}]})

如果 两个以上条件之间是 或者 的关系,需要使用 $or 操作符将条件进行关联

1
$or:[ { },{ },{ }]

eg:

查询评论集合中 userid 为 1003,或者点赞数小于 1000 的文档记录

1
db.comment.find({$or:[{userid: "1003"}, {likenum:{$lt: NumberInt(1000)}]})

总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 选择切换数据库
use articledb
# 插入数据
db.comment.insert({bson数据})
# 查询所有数据
db.comment.find();
# 条件查询数据
db.comment.find({条件})
# 查询符合条件的第一条记录
db.comment.findOne({条件})
# 查询符合条件的前几条记录
db.comment.find({条件}).limit(条数)
# 查询符合条件的跳过的记录
db.comment.find({条件}).skip(条数)
# 修改数据
db.comment.update({条件},{修改后的数据}) 或 db.comment.update({条件},{$set:{要修改部分的字段:数据})
# 修改数据并自增某字段值
db.comment.update({条件},{$inc:{自增的字段:步进值}})
# 删除数据
db.comment.remove({条件})
# 统计查询
db.comment.count({条件})
# 模糊查询
db.comment.find({字段名:/正则表达式/})
# 条件比较运算
db.comment.find({字段名:{$gt:值}})
# 包含查询
db.comment.find({字段名:{$in:[值1,值2]}})或db.comment.find({字段名:{$nin:[值1,值2]}})
# 条件连接查询
db.comment.find({$and:[{条件1},{条件2}]})或db.comment.find({$or:[{条件1},{条件2}]})

索引


MongoDB 索引使用 B树 数据结构(确切的说是 B-Tree,MySQL是 B+Tree)


索引的分类


  1. 单字段索引
    • MongoDB 支持在文档的单个字段上创建用户定义的 升序/降序 索引
  2. 复合索引
    • 支持多个字段的用户定义索引
  3. 地理空间索引(Geospatial Index)
  4. 文本索引(Text Indexes)
  5. 哈希索引(Hashed Indexes)

索引的管理操作


查看索引


返回 一个集合中 的 所有索引 的 数组

基本语法:

1
db.collection.getIndexes()

eg:

1
2
> db.comment.getIndexes()
[ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ]
  • 结果中显示的是默认 _id 索引

注意:该索引是唯一索引,因此值不能重复,即 _id 值不能重复的。在分片集群中,通常使用 _id 作为片键


创建索引


基本语法:

1
db.collection.createIndex(keys, options)
  • 参数说明:keys

    options

eg:

对 userid 字段建立一个升序的索引

1
2
3
4
5
6
7
> db.comment.createIndex({userid:1})
{
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"createdCollectionAutomatically" : false,
"ok" : 1
}

对 userid 和 nickname 同时建立 复合索引

1
2
3
4
5
6
7
> db.comment.createIndex({userid:1, nickname:-1})
{
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"createdCollectionAutomatically" : false,
"ok" : 1
}

删除索引


基本语法:

1
db.collection.dropIndex(index)
  • 参数说明:index

eg:

删除 comment 集合中 userid 字段上的升序索引:

1
2
> db.comment.dropIndex({userid:1})
{ "nIndexesWas" : 3, "ok" : 1 }

所有索引的移除:

1
2
3
4
5
6
> db.comment.dropIndexes()
{
"nIndexesWas" : 2,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
  • 提示: _id 的字段的索引是无法删除的,只能删除非 _id 字段的索引

索引的使用


执行计划


分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如:查询耗费的时间、是否基于索引查询等

基本语法:

1
db.collection.find(query, options).explain(options)

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
> db.comment.find({userid:"1003"}).explain()
{
"explainVersion" : "1",
"queryPlanner" : {
"namespace" : "test.comment",
"indexFilterSet" : false,
"parsedQuery" : {
"userid" : {
"$eq" : "1003"
}
},
"queryHash" : "37A12FC3",
"planCacheKey" : "4A7D843D",
"maxIndexedOrSolutionsReached" : false,
"maxIndexedAndSolutionsReached" : false,
"maxScansToExplodeReached" : false,
"winningPlan" : {
"stage" : "COLLSCAN", # "stage" : "COLLSCAN":表示全集合扫描
# "stage" : "IXSCAN":表示基于索引的扫描
"filter" : {
"userid" : {
"$eq" : "1003"
}
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"command" : {
"find" : "comment",
"filter" : {
"userid" : "1003"
},
"$db" : "test"
},
"serverInfo" : {
"host" : "0ffd0a5d537d",
"port" : 27017,
"version" : "5.0.5",
"gitVersion" : "d65fd89df3fc039b5c55933c0f71d647a54510ae"
},
"serverParameters" : {
"internalQueryFacetBufferSizeBytes" : 104857600,
"internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
"internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
"internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
"internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
"internalQueryProhibitBlockingMergeOnMongoS" : 0,
"internalQueryMaxAddToSetBytes" : 104857600,
"internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
},
"ok" : 1
}

涵盖的查询


当查询条件和查询的投影仅包含索引字段时,MongoDB 直接从索引返回结果,而不扫描任何文档或将文档带入内存,这些覆盖的查询可以非常有效

详情见连接:查询优化 - MongoDB 手册 v7.0


SpringDataMongoDB


SpringData 家族成员之一,用于操作 MongoDB 的持久层框架,封装了底层的 mongodb-driver

官网主页: https://projects.spring.io/spring-data-mongodb/

使用流程

  1. 导入相关依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    </dependencies>
  2. 创建 application.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    spring:
    #数据源配置
    data:
    mongodb:
    # 主机地址
    host: 192.168.88.129
    # 数据库
    database: test
    # 默认端口是27017
    port: 27017
    #也可以使用uri连接
    #uri: mongodb://192.168.88.129:27017/test
  3. 创建启动类

    1
    2
    3
    4
    5
    6
    @SpringBootApplication
    public class ArticleApplication {
    public static void main(String[] args) {
    SpringApplication.run(ArticleApplication.class, args);
    }
    }
  4. 创建一个 article 的实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    /**
    * 文章评论实体类
    */
    // 把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
    // @Document(collection="mongodb 对应 collection 名")
    // 若未加 @Document ,该 bean save 到 mongo 的 comment collection
    // 若添加 @Document ,则 save 到 comment collection
    @Document(collection = "comment") // 可以省略,如果省略,则默认使用类名小写映射集合
    // 复合索引
    @CompoundIndex(def = "{'userid': 1, 'nickname': -1}")
    public class Comment implements Serializable {
    // 主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
    @Id
    private String id; //主键
    // 该属性对应mongodb的字段的名字,如果一致,则无需该注解
    @Field("content")
    private String content; //吐槽内容
    private Date publishtime; //发布日期
    // 添加了一个单字段的索引
    @Indexed
    private String userid; // 发布人ID
    private String nickname; // 昵称
    private LocalDateTime createdatetime; // 评论的日期时间
    private Integer likenum; // 点赞数
    private Integer replynum; // 回复数
    private String state; // 状态
    private String parentid; // 上级ID
    private String articleid;
    // getter and setter.....

    public String getId() {
    return id;
    }

    public void setId(String id) {
    this.id = id;
    }

    public String getContent() {
    return content;
    }

    public void setContent(String content) {
    this.content = content;
    }

    public Date getPublishtime() {
    return publishtime;
    }

    public void setPublishtime(Date publishtime) {
    this.publishtime = publishtime;
    }

    public String getUserid() {
    return userid;
    }

    public void setUserid(String userid) {
    this.userid = userid;
    }

    public String getNickname() {
    return nickname;
    }

    public void setNickname(String nickname) {
    this.nickname = nickname;
    }

    public LocalDateTime getCreatedatetime() {
    return createdatetime;
    }

    public void setCreatedatetime(LocalDateTime createdatetime) {
    this.createdatetime = createdatetime;
    }

    public Integer getLikenum() {
    return likenum;
    }

    public void setLikenum(Integer likenum) {
    this.likenum = likenum;
    }

    public Integer getReplynum() {
    return replynum;
    }

    public void setReplynum(Integer replynum) {
    this.replynum = replynum;
    }

    public String getState() {
    return state;
    }

    public void setState(String state) {
    this.state = state;
    }

    public String getParentid() {
    return parentid;
    }

    public void setParentid(String parentid) {
    this.parentid = parentid;
    }

    public String getArticleid() {
    return articleid;
    }

    public void setArticleid(String articleid) {
    this.articleid = articleid;
    }

    @Override
    public String toString() {
    return "Comment{" +
    "id='" + id + '\'' +
    ", content='" + content + '\'' +
    ", publishtime=" + publishtime +
    ", userid='" + userid + '\'' +
    ", nickname='" + nickname + '\'' +
    ", createdatetime=" + createdatetime +
    ", likenum=" + likenum +
    ", replynum=" + replynum +
    ", state='" + state + '\'' +
    ", parentid='" + parentid + '\'' +
    ", articleid='" + articleid + '\'' +
    '}';
    }
    }

    说明:

    索引可以大大提升查询效率,一般在查询字段上添加索引,索引的添加可以通过 Mongo 的命令来添加,也可以在 Java 的实体类中通过注解添加

    • 单字段索引注解 @Indexed
    • 复合索引注解 @CompoundIndex

未完待续 …