学习目标

  • 理解mongoDB的场景业务,熟悉体系结构,数据类型等
  • 能够在Linux下安装,启动MongoDB,Compass的安装使用
  • 掌握MongDB,基本常用命令,实现数据的CRUD
  • 掌握MongoDB的索引类型,索引管理,执行计划
  • 使用Spring Data MongoDB完成文章评论业务的开发

1. MongoDB的业务应用场景

传统的关系数据库(Mysql),在数操作的三高要求以及应对Web2.0的网站需求面前力不从心
三高:

  • High performance- 对高并发读写的需求
  • Huge Storage- 大数据的储存以及访问需求
  • High Scalability && High Availability- 对数据库的高扩展性及高可用性的需求

具体场景如下:

  • 社交场景: 用户朋友圈信息,地理位置索引等
  • 游戏场景: 用户的游戏信息,如装备,积分等
  • 物流场景: 用户的订单信息,运送状态等
  • 物联网场景: 所有接入设备信息,如连接的设备电量,日志等
  • 直播: 用户点赞,礼物等信息

以上共同特点:

  • 数据量大
  • 读写频繁
  • 大量低价值数据

什么时候选择MongoDB?

除了上述三个特点外,不需要复杂的join关联查询
新应用,应用需要2000-3000以上的读写QPS
需要TB甚至PB级别的数据存储
要求存储数据不丢失
需要大量地理位置信息等等

MongoDB简介

MongoDB是一个开源,高性能,无模式的文档型数据库,当初的设计就是用与简化开发和方便扩展,是NoSQL数据库的一种。
它支持的数据结构非常松散,类似于JSON的格式叫做BSON,所以它既可以存储比较复杂的数据类型没有相当的灵活,它是由字段和值(field。value)组成的数据结构。
image.png
image.png
image.png
image.png

2. Windows 系统中的单机部署

第一步:安装下载包
https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-5.0.8.zip
image.png
第二部:
解压缩到本地的目录里面
image.png
配置文件启动方式:
一定要在bin目录下执行
image.png

  1. mongod -f ../conf/mongod.conf

2.2 Shell 连接(mongo命令)

再已经连接的状态下,再次在bin目录下启动cmd

  1. mongo
  2. mongo --host=127.0.0.1 --port=27017
  3. mongo --host=localhost --port=27017
  4. show dbs

图形化界面操作Compass

下载: https://downloads.mongodb.com/compass/mongodb-compass-1.31.3-win32-x64.zip
image.png

  1. show dbs #查看数据库
  2. show users #查看当前数据库用户和权限
  3. db.dropUser('username') #删除用户
  4. db.updateUser('username) #更新用户
  5. use accesslog #创建数据库

Linux系统的安装启动和连接

image.png
image.png
image.png
IP使用ifconfig查看
image.png
image.png

3. 基本命令

image.png
image.png
image.png

  1. use articledb
  2. #展示当前数据库
  3. db
  4. #删除
  5. db.dropDatabase()

3.1 集合操作

  1. #基本语法:
  2. db.createCollection(name)
  3. #example:
  4. db.createCollection('mycollection')
  5. #查看表
  6. show tables
  7. show collections
  8. #删除表
  9. db.mycollection.drop()
  10. #return true就是删除成功

3.2 文档的CRUD增删改查

单个文档插入

使用insert()或者save()

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

image.png

  1. #实例:
  2. db.comment.insert({
  3. "articleid":'100000',"content":"今天天气真好,阳光明媚",
  4. "userid":"1001","nickname":"Rose", "createdatetime":new Date(),
  5. "likenum":NumberInt(10), "state":null
  6. })

查询文档

image.png

  1. db.comment.find()
  2. 或者
  3. db.comment.find({userid:'1002'})
  4. db.comment.find({nickname:'Rose'})
  5. #t投影查询
  6. #查询结果只返回部分字段:
  7. db.comment.find(
  8. {userid:'1005'},
  9. {userid:1,nickname:1}
  10. )
  11. #以上查询结果为userid=1005并且只显示_id,userid和nickname
  12. #不想显示_id,可以加上(id:0)

批量插入

image.png

  1. db.comment.insertMany([
  2. {"_id":"1","articleid":"1000001","content":"健康很重要s","userid":"1002",
  3. "nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),
  4. "likenum":NumberInt(1000),"state":"1"},
  5. {"_id":"2","articleid":"1000001","content":"夏天喝凉水","userid":"1005",
  6. "nickname":"伊人憔悴","createdatetime":new Date('2019-08-05'),
  7. "likenum":NumberInt(888),"state":"1"},
  8. {"_id":"3","articleid":"1000001","content":"不能空腹吃饭","userid":"1002",
  9. "nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),
  10. "likenum":NumberInt(666),"state":"1"}
  11. ]);
  12. #由于多条数据插入失败不会回滚,可以加上try,catch
  13. try{
  14. db.comment.insertMany([
  15. {"_id":"1","articleid":"1000001","content":"健康很重要s","userid":"1002",
  16. "nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),
  17. "likenum":NumberInt(1000),"state":"1"},
  18. {"_id":"2","articleid":"1000001","content":"夏天喝凉水","userid":"1005",
  19. "nickname":"伊人憔悴","createdatetime":new Date('2019-08-05'),
  20. "likenum":NumberInt(888),"state":"1"},
  21. {"_id":"3","articleid":"1000001","content":"不能空腹吃饭","userid":"1002",
  22. "nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),
  23. "likenum":NumberInt(666),"state":"1"}
  24. ]);
  25. } catch(e){
  26. print(e);
  27. }
  28. try{
  29. db.comment.insertMany([
  30. {"_id":"4","articleid":"1000001","content":"2023上岸广东省定向选调生!","userid":"1003",
  31. "nickname":"蒙特卡罗树正在找图灵的猫","createdatetime":new Date('2019-08-05T22:08:15.522Z'),
  32. "likenum":NumberInt(888),"state":"1"},
  33. {"_id":"5","articleid":"1000001","content":"刚烧开的水烫嘴","userid":"1004",
  34. "nickname":"杰克船长","createdatetime":new Date('2019-08-05T22:08:15.522Z'),
  35. "likenum":NumberInt(666),"state":"1"}
  36. ]);
  37. }catch(e){
  38. print(e);
  39. }

文档的更新

  1. #覆盖更新:会删除原来的所有记录只保留新添加的
  2. #例如:修改_id:1的数据,修改点赞数量
  3. db.comment.update({_id:'1'},{likenum:NumberInt(1001)})
  4. #结果只剩下id和likenum

image.png

  1. #局部修改:保留原来的数据下添加新修改
  2. db.comment.update({_id:'2'},{$set:{likenum:NumberInt(889)}})

image.png

  1. #批量修改
  2. #修改所有userid:1002的用户的nick为凯撒
  3. #默认只修改第一条
  4. db.comment.update({userid:'1002'},{$set:{nickname:"凯撒大帝"}})
  5. #修改所有符合条件的数据
  6. db.comment.update({userid:'1002'},{$set:{nickname:"凯撒"}},{multi:true})

image.png

  1. #列值增长的修改
  2. #对于某列原有的数值进行增增加、减少,使用$inc
  3. db.comment.update(
  4. {_id:'3'},
  5. {
  6. $inc:{likenum:NumberInt(1)}
  7. }
  8. )
  9. #减少
  10. db.comment.update({_id:'3'},{$inc:{likenum:NumberInt(-1)}})

image.png

image.png

删除文档

image.png

  1. #删除_id:0的数据
  2. db.comment.remove({_id:'0'})
  3. #ObjectId("628d18d18829eb518937da40")

文档的统计查询

image.png

  1. #记录所有
  2. db.comment.count()
  3. #按照条件,统计userid为1002的数量
  4. db.comment.count({userid:"1002"})

分页列表查询

image.png

  1. #查找前两个
  2. db.comment.find().limit(2)
  3. #跳过前两条,也就是查找的第三条和第四条
  4. db.comment.find().skip(2).limit(2)
  5. #分页查询,每页2条数据
  6. db.comment.find().skip(0).limit(2)
  7. db.comment.find().skip(2).limit(2)
  8. db.comment.find().skip(4).limit(2)

排序查询

image.png

  1. #db.collection_name.find().sort({排序方法})
  2. #投影选择只显示userid和likenum下,找到userid倒序的前提下,likenum正序的数据
  3. db.comment.find({},{userid:1,likenum:1,_id:0}).sort({userid:-1,likenum:1})

正则的复杂条件查询

image.png

  1. #只显示content和nickname下找到content中包含‘水’的数据
  2. db.comment.find({content:/水/},{content:1,nickname:1})
  3. #只显示content和nickname下找到nickname以‘蒙特’开头的
  4. db.comment.find({nickname:/^蒙特/},{content:1,nickname:1})

比较查询

image.png

  1. #大于用gt(greater than),小于lt(less than),大于等于gte(greater than equal)
  2. #小于等于lte(less than equal), 不等于ne(not equal)
  3. #只显示nickname,content,likenum下,找到点赞数大于700的数据
  4. db.comment.find(
  5. {likenum:{$gt:NumberInt(700)}},
  6. {nickname:1,content:1,likenum:1}
  7. )

包含查询

image.png

  1. #只显示nickname下,找到nickname中==‘凯撒’或者==’杰克船长‘的数据
  2. db.comment.find(
  3. {nickname:{$in:["凯撒","杰克船长"]}},
  4. {nickname:1}
  5. )
  6. #找到nickname!=‘凯撒的数据’
  7. db.comment.find({nickname:{$nin:['凯撒']}},{nickname:1})

条件连接查询

  1. #如果查询需要满足两个条件以上,使用$and,或者使用$or
  2. $and:[{},{},{}]
  3. $or:[{},{},{}]
  4. #查询userid=1005的数据或者点赞数小于889并且大于800
  5. db.comment.find({
  6. $or:[
  7. {userid:'1005'},
  8. {$and:[
  9. {likenum:{$lt:NumberInt(889)} },
  10. {likenum:{$gt:NumberInt(800)} }
  11. ]}
  12. ]
  13. })

image.png

常用命令小结

image.png

4. 索引

4.1 概述

image.png

4.2 索引类型

单字段索引

image.png

复合索引

image.png

其他索引

image.png

4.3 索引的管理操作

索引查看

image.png

  1. #查看所有索引
  2. > db.comment.getIndexes()
  3. #[ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ]
  4. v代表mongodb的索引版本, 1表示升序, name表示索引对象

索引的创建

image.png
image.png

  1. 语法:
  2. db.collection_name.createIndex(keys,options)
  3. #单子段索引:对userid建立升序索引
  4. db.comment.createIndex({userid:1})
  5. #复合索引
  6. db.comment.createIndex({userid:1,nickname:-1})
  7. #查看索引
  8. db.comment.getIndexes()

索引的移除

image.png

  1. #可以用条件去删除,也可以用索引名字删除
  2. #按照条件删除
  3. db.comment.dropIndex({userid:1})
  4. #按照名字删除,getIndex()可以看到索引名字
  5. db.comment.dropIndex("userid_1_nickname_-1")
  6. #删除所有索引
  7. db.comment.dropIndexes()

4.4 索引的使用

执行计划

image.png

  1. db.comment.find({userid:1003}).explain()

image.png
image.png

  1. #因此我们加上索引再看
  2. db.comment.createIndex({userid:1})
  3. #check
  4. db.comment.find({userid:'1003'}).explain()

image.png
image.png

涵盖的查询

image.png

  1. example:
  2. db.comment.find({userid:'1002'},{userid:1,_id:0})

image.png
image.png

5. 文章评论的开发

5.1 需求分析

image.png
评论中还有子评论

需要实现功能:

  1. 基本增删改查API
  2. 根据文章id查询评论
  3. 评论点赞

5.2 表结构分析

image.png

5.3 技术选型

用java开发需要使用mongodb-driver
image.png

5.4 文章微服务模块搭建

下载idea,建立新项目
image.png
image.png

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <version>2.1.6.RELEASE</version>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <relativePath/>
  6. </parent>
  7. <groupId>cn.itcast</groupId>
  8. <artifactId>article</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <dependencies>
  11. <dependency>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-test</artifactId>
  14. <scope>test</scope>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-data-mongodb</artifactId>
  19. </dependency>
  20. </dependencies>

image.png

  1. spring:
  2. #数据源配置
  3. data:
  4. mongodb:
  5. #主机地址
  6. host: 192.168.56.1
  7. #数据库
  8. database: articledb
  9. #默认端口27017
  10. port: 27017

image.png

  1. cn.itcast.article.ArticleApplication
  2. package cn.itcast.article;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. @SpringBootApplication
  6. public class ArticleApplication{
  7. public static void main(String[] args){
  8. SpringApplication.run(ArticleApplication.class, args);
  9. }
  10. }

5.5 文章评论实体类的编写

image.png

  1. cn.itcast.article.po.Comment
  1. package cn.itcast.article.po;
  2. import org.springframework.data.annotation.Id;
  3. import org.springframework.data.mongodb.core.index.Indexed;
  4. import org.springframework.data.mongodb.core.mapping.Document;
  5. import org.springframework.data.mongodb.core.mapping.Field;
  6. import java.io.Serializable;
  7. import java.time.LocalDateTime;
  8. import java.util.Date;
  9. /**
  10. * 文章评论实体类
  11. */
  12. //把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
  13. //@Documentcollection=’mongodb‘对应collection名字)
  14. //若未加@Document,该been save mongo comment collection
  15. //若添加@Document, save comment collection
  16. @Document(collection = "comment") //如果省略,则默认使用类名小写映射集合
  17. //复合索引
  18. //@CompoundIndexdef = "{"userid":1, "nickname":-1}"
  19. public class Comment implements Serializable{
  20. //主键表示,该属性的值会自动对应mongodb的主键_id,如果该属性就是“id”则可以省略
  21. @Id
  22. private String id;//主键
  23. @Field("content")
  24. private String content; //评论内容
  25. private Date publishtime; //发布日期
  26. //添加一个单子段索引
  27. @Indexed
  28. private String userid;//发布人id
  29. private String nickname; //昵称
  30. private LocalDateTime createdatetime; //评论日期
  31. private Integer likenum; //点赞数
  32. private Integer replynum; //回复数
  33. private String state; //状态
  34. private String parentid;//上级id
  35. private String articleid;
  36. //getter and setter...
  37. public String getId(){
  38. return id;
  39. }
  40. public void setId(String id){
  41. this.id = id;
  42. }
  43. public String getContent(){
  44. return content;
  45. }
  46. public void setContent(String content){
  47. this.content = content;
  48. }
  49. public Date getPublishtime(){
  50. return publishtime;
  51. }
  52. public void setPublishtime(Date publishtime){
  53. this.publishtime = publishtime;
  54. }
  55. public String getUserid(){
  56. return userid;
  57. }
  58. public void setUserid(String userid){
  59. this.userid = userid;
  60. }
  61. public String getNickname(){
  62. return nickname;
  63. }
  64. public void setNickname(String nickname){
  65. this.nickname = nickname;
  66. }
  67. public LocalDateTime getCreatedatetime(){
  68. return createdatetime;
  69. }
  70. public void setCreatedatetime(LocalDateTime createdatetime){
  71. this.createdatetime = createdatetime;
  72. }
  73. public Integer getLikenum(){
  74. return likenum;
  75. }
  76. public void setLikenum(Integer likenum){
  77. this.likenum = likenum;
  78. }
  79. public Integer getReplynum(){
  80. return replynum;
  81. }
  82. public void setReplynum(Integer replynum){
  83. this.replynum = replynum;
  84. }
  85. public String getState(){
  86. return state;
  87. }
  88. public void setState(String state){
  89. this.state = state;
  90. }
  91. public String getParentid(){
  92. return parentid;
  93. }
  94. public void setParentid(String parentid){
  95. this.parentid = parentid;
  96. }
  97. public String getArticleid(){
  98. return articleid;
  99. }
  100. public void setArticleid(String articleid){
  101. this.articleid = articleid;
  102. }
  103. @Override
  104. public String toString(){
  105. return "Comment{" +
  106. "id='" + id + "\'" +
  107. ", content='" + content + "\'" +
  108. ", publishtime=" + publishtime +
  109. ", userid='" + userid + "\'" +
  110. ", nickname='" + nickname + "\'" +
  111. ", createdatetime=" + createdatetime +
  112. ", likenum=" + likenum +
  113. ", replynum=" + replynum +
  114. ", state='" + state + "\'" +
  115. ", parentid='" + parentid + "\'" +
  116. ", articleid'" + articleid + "\'" +
  117. "}";
  118. }
  119. }

5.6文章评论的增删改查

image.png
image.png

#Comment.java:
package cn.itcast.article.service;

import cn.itcast.article.dao.CommentRepository;
import cn.itcast.article.po.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;

    /**
     * 保存每一个评论
     * @param comment
     */
    public void saveComment(Comment comment){
        //如果去要自定义主键,可以在这里自定义,不指定主键,Mongodb会自动生成
        //设置一些默认初始值
        //调用dao
        commentRepository.save(comment);
    }

    /**
     * 更新评论
     * @param comment
     */
    public void updateComment(Comment comment){
        //调用dao
        commentRepository.save(comment);
    }

    /**
     * 根据id删除评论
     * @param id
     */
    public void deleteCommentById(String id){
        //调用dao
        commentRepository.deleteById(id);
    }

    /**
     * 查询所有评论
     * @return
     */
    public List<Comment> findCommentList(){
        //调用dao
        return commentRepository.findAll();
    }

    /**
     * 根据id查询评论
     * @param id
     * @return
     */
    public Comment findCommentById(String id){
        //调用dao
        return commentRepository.findById(id).get();
    }

}

6. 练习题

插入

// 1. 进入my_test数据库
use my_test

// 2. 向数据库users插入一个文档
db.users.insert({
    username: "leon"
});

// 3. 查询users集合中的文档

// 4. 向users在插入一个文档

//