MongoDB
# MongoDB
# 前言
之前学习的数据库大部分都是关系型数据库,尤其以Mysql
为主,非关系数据库接触过Redis
,但无论是非关系数据库还是关系数据库,我个人目前的认知在于他们是去满足特定的需求的,这次通过简单学习一下MongoDB
,加深一下对非关系数据库的认知,同时更加深刻去理解NoSql
。
# 简介
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系型数据库的。
# 安装
Windows下安装
这里我们直接选择msi下载
自定义安装方式
选择将MongoDB作为服务运行,同时设定好data目录和log目录。
取消安装MongoDB客户端工具
在安装目录下运行
mongo.exe
,就可以启动我们的MongoDB了。
Linux下安装
https://www.runoob.com/mongodb/mongodb-linux-install.html
客户端工具
- MongoDB Compass
- Navicat 15
- Robo 3T
- ...
# 相关概念
MongoDB是非关系型数据库当中最像关系型数据库的,所以我们通过它与关系型数据库的对比,来了解下它的概念。
SQL概念 | MongoDB概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
# 数据库操作
- 创建数据库,使用
use
命令去创建数据库,当插入第一条数据时会创建数据库,例如创建一个test
数据库,插入一条wxx的数据。
- 删除数据库,使用db对象中的
dropDatabase()
方法来删除。
# 集合操作
创建集合,其实在我们刚才创建数据库的时候,我们使用了db.article.insert,其实这个article就是我们所创建的集合。这里我们使用db对象中的
createCollection()
方法来创建集合。
集合在关系数据库中概念等同于表。
删除集合使用collection对象的
drop()
方法来删除集合。
# 文档操作
在开始之前,我们还是类比一下。之前数据库操作,和我们关系数据库操作类似,就是创建了一个数据库。而关系数据库在创建好数据库之后,我们就需要创建表了,而在MongoDB
中我们创建的为集合
。接着在关系数据库创建表之后,我们就要设定我们的字段,然后录入数据了,而这里录入的就是文档
。
插入文档
MongoDB通过collection对象的
insert()
方法向集合中插入文档,语法如下:db.collection.insert(document)
现在我们去创建一个
article
集合,并插入一个文档:db.article.insert({title:"test",author:"wxx"})
使用collection对象的
find()
方法可以获取文档:> db.article.find({}) { "_id" : ObjectId("620db323963dba507fc4e5fe"), "title" : "test", "author" : "wxx" }
更新文档
MongoDB通过collection对象的
update()
来更新集合中的文档,语法如下:db.collection.update( <query>, <update>, { multi: <boolean> } ) # query:修改的查询条件,类似于SQL中的WHERE部分 # update:更新属性的操作符,类似与SQL中的SET部分 # multi:设置为true时会更新所有符合条件的文档,默认为false只更新找到的第一条
现在我们利用上面的语法把刚才的文档的作者更换一下:
db.article.update({'author':'wxx'}, {'author':'wxx02'})
除了
update()
方法以外,save()
方法可以用来替换已有文档,语法如下:db.collection.save(document)
现在我们来把刚才已经修改过的
文档
,我们再来使用save()
方法来修改一下db.article.save({"_id":ObjectId("620db323963dba507fc4e5fe"),"author":"wxx03"})
删除文档
MongoDB通过collection对象的
remove()
方法来删除集合中的文档,语法如下:db.collection.remove( <query>, { justOne: <boolean> } ) # query:删除的查询条件,类似于SQL中的WHERE部分 # justOne:设置为true只删除一条记录,默认为false删除所有记录
现在我们来删除刚才作者为wxx03的文档
db.article.remove({"author":"wxx03"})
查询文档
在查询文档之前,我们去添加几个文档,便于后续普通查询和条件查询练习。
db.article.insert([{
"name": "wxx",
"sex": "男",
"age": "20"
},
{
"name": "wxx1",
"sex": "女",
"age": "22"
},
{
"name": "wxx2",
"sex": "男",
"age": "25"
}])
MongoDB通过collection对象的
find()
方法来查询文档,语法如下:db.collection.find(query, projection) # query:查询条件,类似于SQL中的WHERE部分 # projection:可选,使用投影操作符指定返回的键
查询
article
集合中的所有文档db.article.find()
在开始条件查询之前,我们还是来把一些条件操作符和我们熟悉的mysql进行对比:
操作 格式 SQL中的类似语句 等于 {<key>:<value>}
where title = 'MongoDB 教程'
小于 {<key>:{$lt:<value>}}
where likes < 50
小于或等于 {<key>:{$lte:<value>}}
where likes <= 50
大于 {<key>:{$gt:<value>}}
where likes > 50
大于或等于 {<key>:{$gte:<value>}}
where likes >= 50
不等于 {<key>:{$ne:<value>}}
where likes != 50
条件查询-查询名字为
wxx
的文档db.article.find({"name":"wxx"})
条件查询-查询年龄小于
22
的文档db.article.find({"age":{$lt:"22"}})
条件查询-查询年龄小于等于
22
的文档db.article.find({"age":{$lte:"22"}})
条件查询-查询年龄大于
22
的文档db.article.find({"age":{$gt:"22"}})
条件查询-查询年龄大于等于
22
的文档db.article.find({"age":{$gte:"22"}})
条件查询-查询年龄不等于
22
的文档db.article.find({"age":{$ne:"22"}})
条件查询-查询年龄大于
20
,且性别为男
db.article.find({"age":{$gt:"20"},"sex":"男"})
条件查询-查询年龄为
22
或25
db.article.find({$or:[{"age":"22"},{"age":"25"}]})
# 其他操作
限制和跳过
类似与分页操作,有时候我们要限制我们查询的数量,或者我们需要跳过前几个文档。
读取指定数量的文档,可以使用
limit()
方法,语法如下:db.collection.find().limit(NUMBER)
查询
article
集合中的前两条数据:db.article.find().limit(2)
跳过指定数量的文档来读取,可以使用
skip()
方法,语法如下:db.collection.find().limit(NUMBER).skip(NUMBER)
跳过
article
集合中的前两条,查询第一条db.article.find().limit(1).skip(2)
排序
在MongoDB中使用
sort()
方法对数据进行排序,sort()
方法通过参数来指定排序的字段,并使用1和-1来指定排序方式,1为升序,-1为降序db.collection.find().sort({KEY:1})
将
article
集合中的文档按照年龄
降序排列:db.article.find().sort({"age":-1})
索引
索引我们在mysql中应用是为了提高查询的效率,这里也类似。
MongoDB使用
createIndex()
方法来创建索引,语法如下:db.collection.createIndex(keys, options) # background:建索引过程会阻塞其它数据库操作,设置为true表示后台创建,默认为false # unique:设置为true表示创建唯一索引 # name:指定索引名称,如果没有指定会自动生成
给我们的
姓名
创建索引,1表示升序索引,-1表示降序索引,指定以后台方式创建db.article.createIndex({"name":1},{background: true})
查看
article
集合中已经创建的索引:db.article.getIndexes()
聚合
MongoDB中的聚合使用
aggregate()
方法,类似于SQL中的group by语句,语法如下:db.collection.aggregate(AGGREGATE_OPERATION)
聚合中常用操作符如下;
操作符 描述 $sum 计算总和 $avg 计算平均值 $min 计算最小值 $max 计算最大值 查询
男
和女
性别各有多少人:db.article.aggregate([{$group:{_id:"$sex", sum_count:{$sum:1}}}])
正则表达式
MongoDB使用
$regex
操作符来设置匹配字符串的正则表达式,可以用来模糊查询,类似于SQL中的like操作查询名字中包含
wxx
的文档:db.article.find({"name":{$regex:"wxx"}})
# 集成代码
下面我们简单的将springboot
和mongodb
进行集成。
相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.16</version>
</dependency>
实体
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Article {
/**
* 文章id
*/
@Id
private Long id;
/**
* 文章标题
*/
private String title;
/**
* 文章内容
*/
private String content;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 点赞数量
*/
private Long thumbUp;
/**
* 访客数量
*/
private Long visits;
}
Repository
public interface ArticleRepository extends MongoRepository<Article, Long> {
/**
* 根据标题模糊查询
*
* @param title 标题
* @return 满足条件的文章列表
*/
List<Article> findByTitleLike(String title);
}
这里可以看看MongoRepository
,感觉和jpa有点类似,基本的一些增删改查已经封装好了,直接使用即可。
测试
@SpringBootTest
@RunWith(SpringRunner.class)
public
class SpringBootMongodbApplicationTests {
@Test
void contextLoads() {
}
}
@Slf4j
public class ArticleRepositoryTest extends SpringBootMongodbApplicationTests {
@Autowired
private ArticleRepository articleRepo;
@Autowired
private MongoTemplate mongoTemplate;
}
新增
/**
* 测试新增
*/
@Test
public void testSave() {
Article article = new Article(IdUtil.getSnowflake(1, 1).nextId(), RandomUtil.randomString(20), RandomUtil.randomString(150), DateUtil.date(), DateUtil
.date(), 0L, 0L);
articleRepo.save(article);
log.info("【article】= {}", JSONUtil.toJsonStr(article));
}
批量新增
/**
* 测试批量新增
*/
@Test
public void testSaveList() {
List<Article> articles = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
articles.add(new Article(IdUtil.getSnowflake(1, 1).nextId(), RandomUtil.randomString(20), RandomUtil.randomString(150), DateUtil
.date(), DateUtil.date(), 0L, 0L));
}
articleRepo.saveAll(articles);
log.info("【articles】= {}", JSONUtil.toJsonStr(articles.stream()
.map(Article::getId)
.collect(Collectors.toList())));
}
更新
/**
* 测试更新
*/
@Test
public void testUpdate() {
articleRepo.findById(1L).ifPresent(article -> {
article.setTitle("更新之后的标题:" + article.getTitle());
article.setUpdateTime(DateUtil.date());
articleRepo.save(article);
log.info("【article】= {}", JSONUtil.toJsonStr(article));
});
}
删除
/**
* 测试删除
*/
@Test
public void testDelete() {
// 根据主键删除
articleRepo.deleteById(1L);
// 全部删除
//articleRepo.deleteAll();
}
字段更新
/**
* 测试点赞数、访客数,使用save方式更新点赞、访客
*/
@Test
public void testThumbUp() {
articleRepo.findById(1L).ifPresent(article -> {
article.setThumbUp(article.getThumbUp() + 1);
article.setVisits(article.getVisits() + 1);
articleRepo.save(article);
log.info("【标题】= {}【点赞数】= {}【访客数】= {}", article.getTitle(), article.getThumbUp(), article.getVisits());
});
}
/**
* 测试点赞数、访客数,使用更优雅/高效的方式更新点赞、访客
*/
@Test
public void testThumbUp2() {
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(1L));
Update update = new Update();
update.inc("thumbUp", 1L);
update.inc("visits", 1L);
mongoTemplate.updateFirst(query, update, "article");
articleRepo.findById(1L)
.ifPresent(article -> log.info("【标题】= {}【点赞数】= {}【访客数】= {}", article.getTitle(), article.getThumbUp(), article
.getVisits()));
}
这里的第一种方法和我们的更新操作类似,第二种方法是通过mongoTemplate的updateFirst方法。
分页查询
/**
* 测试分页排序查询
*/
@Test
public void testQuery() {
Sort sort = Sort.by("thumbUp", "updateTime").descending();
PageRequest pageRequest = PageRequest.of(0, 5, sort);
Page<Article> all = articleRepo.findAll(pageRequest);
log.info("【总页数】= {}", all.getTotalPages());
log.info("【总条数】= {}", all.getTotalElements());
log.info("【当前页数据】= {}", JSONUtil.toJsonStr(all.getContent()
.stream()
.map(article -> "文章标题:" + article.getTitle() + "点赞数:" + article.getThumbUp() + "更新时间:" + article.getUpdateTime())
.collect(Collectors.toList())));
}
模糊查询
/**
* 测试根据标题模糊查询
*/
@Test
public void testFindByTitleLike() {
List<Article> articles = articleRepo.findByTitleLike("更新");
log.info("【articles】= {}", JSONUtil.toJsonStr(articles));
}
# 参考
https://juejin.cn/post/6844904150635921422#heading-6
https://github.com/xkcoding/spring-boot-demo/tree/master/demo-mongodb