学习目标
- 理解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)组成的数据结构。



2. Windows 系统中的单机部署
第一步:安装下载包
https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-5.0.8.zip
第二部:
解压缩到本地的目录里面
配置文件启动方式:
一定要在bin目录下执行
mongod -f ../conf/mongod.conf
2.2 Shell 连接(mongo命令)
再已经连接的状态下,再次在bin目录下启动cmd
mongo或mongo --host=127.0.0.1 --port=27017或mongo --host=localhost --port=27017show dbs
图形化界面操作Compass
下载: https://downloads.mongodb.com/compass/mongodb-compass-1.31.3-win32-x64.zip
show dbs #查看数据库show users #查看当前数据库用户和权限db.dropUser('username') #删除用户db.updateUser('username) #更新用户use accesslog #创建数据库
Linux系统的安装启动和连接
3. 基本命令



use articledb#展示当前数据库db#删除db.dropDatabase()
3.1 集合操作
#基本语法:db.createCollection(name)#example:db.createCollection('mycollection')#查看表show tables或show collections#删除表db.mycollection.drop()#return true就是删除成功
3.2 文档的CRUD增删改查
单个文档插入
使用insert()或者save()
db.collection.insert(<document or array of documents>,{writeConcern: <document>,ordered: <boolean>})

#实例:db.comment.insert({"articleid":'100000',"content":"今天天气真好,阳光明媚","userid":"1001","nickname":"Rose", "createdatetime":new Date(),"likenum":NumberInt(10), "state":null})
查询文档

db.comment.find()或者db.comment.find({userid:'1002'})db.comment.find({nickname:'Rose'})#t投影查询#查询结果只返回部分字段:db.comment.find({userid:'1005'},{userid:1,nickname:1})#以上查询结果为userid=1005并且只显示_id,userid和nickname#不想显示_id,可以加上(id:0)
批量插入

db.comment.insertMany([{"_id":"1","articleid":"1000001","content":"健康很重要s","userid":"1002","nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),"likenum":NumberInt(1000),"state":"1"},{"_id":"2","articleid":"1000001","content":"夏天喝凉水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date('2019-08-05'),"likenum":NumberInt(888),"state":"1"},{"_id":"3","articleid":"1000001","content":"不能空腹吃饭","userid":"1002","nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),"likenum":NumberInt(666),"state":"1"}]);#由于多条数据插入失败不会回滚,可以加上try,catchtry{db.comment.insertMany([{"_id":"1","articleid":"1000001","content":"健康很重要s","userid":"1002","nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),"likenum":NumberInt(1000),"state":"1"},{"_id":"2","articleid":"1000001","content":"夏天喝凉水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date('2019-08-05'),"likenum":NumberInt(888),"state":"1"},{"_id":"3","articleid":"1000001","content":"不能空腹吃饭","userid":"1002","nickname":"相忘于江湖s","createdatetime":new Date('2019-08-05T22:08:15.522Z'),"likenum":NumberInt(666),"state":"1"}]);} catch(e){print(e);}try{db.comment.insertMany([{"_id":"4","articleid":"1000001","content":"2023上岸广东省定向选调生!","userid":"1003","nickname":"蒙特卡罗树正在找图灵的猫","createdatetime":new Date('2019-08-05T22:08:15.522Z'),"likenum":NumberInt(888),"state":"1"},{"_id":"5","articleid":"1000001","content":"刚烧开的水烫嘴","userid":"1004","nickname":"杰克船长","createdatetime":new Date('2019-08-05T22:08:15.522Z'),"likenum":NumberInt(666),"state":"1"}]);}catch(e){print(e);}
文档的更新
#覆盖更新:会删除原来的所有记录只保留新添加的#例如:修改_id:1的数据,修改点赞数量db.comment.update({_id:'1'},{likenum:NumberInt(1001)})#结果只剩下id和likenum

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

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

#列值增长的修改#对于某列原有的数值进行增增加、减少,使用$incdb.comment.update({_id:'3'},{$inc:{likenum:NumberInt(1)}})#减少db.comment.update({_id:'3'},{$inc:{likenum:NumberInt(-1)}})
删除文档

#删除_id:0的数据db.comment.remove({_id:'0'})#ObjectId("628d18d18829eb518937da40")
文档的统计查询

#记录所有db.comment.count()#按照条件,统计userid为1002的数量db.comment.count({userid:"1002"})
分页列表查询

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

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

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

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

#只显示nickname下,找到nickname中==‘凯撒’或者==’杰克船长‘的数据db.comment.find({nickname:{$in:["凯撒","杰克船长"]}},{nickname:1})#找到nickname!=‘凯撒的数据’db.comment.find({nickname:{$nin:['凯撒']}},{nickname:1})
条件连接查询
#如果查询需要满足两个条件以上,使用$and,或者使用$or$and:[{},{},{}]$or:[{},{},{}]#查询userid=1005的数据或者点赞数小于889并且大于800db.comment.find({$or:[{userid:'1005'},{$and:[{likenum:{$lt:NumberInt(889)} },{likenum:{$gt:NumberInt(800)} }]}]})
常用命令小结
4. 索引
4.1 概述
4.2 索引类型
单字段索引
复合索引

其他索引
4.3 索引的管理操作
索引查看

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


语法:db.collection_name.createIndex(keys,options)#单子段索引:对userid建立升序索引db.comment.createIndex({userid:1})#复合索引db.comment.createIndex({userid:1,nickname:-1})#查看索引db.comment.getIndexes()
索引的移除

#可以用条件去删除,也可以用索引名字删除#按照条件删除db.comment.dropIndex({userid:1})#按照名字删除,getIndex()可以看到索引名字db.comment.dropIndex("userid_1_nickname_-1")#删除所有索引db.comment.dropIndexes()
4.4 索引的使用
执行计划

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


#因此我们加上索引再看db.comment.createIndex({userid:1})#checkdb.comment.find({userid:'1003'}).explain()
涵盖的查询

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


5. 文章评论的开发
5.1 需求分析

评论中还有子评论
需要实现功能:
- 基本增删改查API
- 根据文章id查询评论
- 评论点赞
5.2 表结构分析
5.3 技术选型
5.4 文章微服务模块搭建
下载idea,建立新项目

<parent><groupId>org.springframework.boot</groupId><version>2.1.6.RELEASE</version><artifactId>spring-boot-starter-parent</artifactId><relativePath/></parent><groupId>cn.itcast</groupId><artifactId>article</artifactId><version>1.0-SNAPSHOT</version><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>

spring:#数据源配置data:mongodb:#主机地址host: 192.168.56.1#数据库database: articledb#默认端口27017port: 27017

cn.itcast.article.ArticleApplicationpackage cn.itcast.article;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ArticleApplication{public static void main(String[] args){SpringApplication.run(ArticleApplication.class, args);}}
5.5 文章评论实体类的编写

cn.itcast.article.po.Comment
package cn.itcast.article.po;import org.springframework.data.annotation.Id;import org.springframework.data.mongodb.core.index.Indexed;import org.springframework.data.mongodb.core.mapping.Document;import org.springframework.data.mongodb.core.mapping.Field;import java.io.Serializable;import java.time.LocalDateTime;import java.util.Date;/*** 文章评论实体类*///把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。//@Document(collection=’mongodb‘对应collection名字)//若未加@Document,该been 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”则可以省略@Idprivate String id;//主键@Field("content")private String content; //评论内容private Date publishtime; //发布日期//添加一个单子段索引@Indexedprivate String userid;//发布人idprivate String nickname; //昵称private LocalDateTime createdatetime; //评论日期private Integer likenum; //点赞数private Integer replynum; //回复数private String state; //状态private String parentid;//上级idprivate 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;}@Overridepublic 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 + "\'" +"}";}}
5.6文章评论的增删改查


#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在插入一个文档
//






