本文只是一个简单的学习,并不是在项目中的实际使用场景
实际使用可能有所不同
目录
MongoDB
-
教程
-
基本使用与语法--------√
-
整合SpringBoot的基本使用------√
-
索引深入相关--------×
-
相关高级使用--------×
-
底层实现原理--------×
MongoDB介绍
-
概述
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的
-
介绍
-
由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。
-
高负载的情况下,添加更多的节点,可以保证服务器性能
-
数据存储为一个文档,数据结构由键值(key=>value)对组成
-
MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组
-
-
使用场景
-
业务需要事务,使用mysql,因为mongodb不支持事务
-
数据量大,但是数据本身价值不大,使用mongodb
-
数据是非结构化的,且数据量大,使用mongodb
-
业务未来走向不明确,使用mongodb,方便扩展
-
-
MongoDB的Windows安装
-
下载
-
解压安装
-
在bin的同级创建data\db
- 在bin目录下进入cmd
-
输入指令启动
-
mongod --dbpath=..\data\db 最终端口号为27017
-
-
在bin目录同级创建conf\mongod.conf(配置文件)
-
配置配置文件
-
storage: dbPath: c:\data\db
-
-
根据配置文件指令启动
-
mongod -f ..\conf\mongod.conf mongod --config ..\conf\mongod.conf
-
-
MongoDB的字段
数据库 | MongoDB | 注释 |
---|---|---|
database | database | 数据库 |
table | collection | 表<->集合 |
row | document | 行<->文档 |
column | field | 列(字段)<->域 |
index | index | 索引<->索引 |
table joins | 表连接<->mongoDB不支持 | |
primary key | primary key | 主键<->_id为主键 |
MongoDB语法
创建数据库
-
use databaseName -- databaseName:创建的数据库名
删除数据库
-
db.dropDatabase()
创建集合
-
db.createCollection(name,options) -- name:集合名 options:可选->指定有关内存大小以及索引的选择 -- 在插入数据时,如果集合不存在会自动创建
删除集合
-
db.collectionName.drop() -- collectionName:集合名
插入文档
-
db.COLLECTION_NAME.insert(document) -- COLLECTION_NAME:集合名 -- document:文档 json数据 db.COLLECION_NAME.insertMany([document,document])
-
db.list2.insert( { "user_id":"3", "name":"zhangsan", "username":"admin", "password":"123456", "age":"18" }) db.c3.insertMany([ {"sex":1},{"sex":1},{"sex":1} ])
-
删除文档
-
db.collectionName.remove( <query>, { justOne: <boolean>, writeConcern: <document> } ) -- collectionName:集合名称 -- query :(可选)删除的文档的条件。 -- justOne : (可选)如果设为 true 或 1,则只删除一个文档。 -- writeConcern :(可选)抛出异常的级别。
-
db.list2.remove( { "user_id": "1" }, { justOne: true } )
-
修改文档
-
db.collectionName.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } ) -- collectionName:集合名 -- query : update的查询条件,类似sql update查询内where后面的。 -- update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的 -- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。 -- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。 -- writeConcern :可选,抛出异常的级别。
-
-- 只更新第一条记录: db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } ); -- 全部更新: db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true ); -- 只添加第一条: db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false ); -- 全部添加加进去: db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true ); -- 全部更新 增加: db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true ); -- 只更新第一条记录 减少: db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : -1} },false,false );
-
查询文档
-
db.collection.find(query, projection) -- query :可选,使用查询操作符指定查询条件 -- projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
-
db.list2.find({ "user_id": { $gt: "0" }, $or:[ { "username": "lisi" }, { "username": "wangba" }] }) -- user_id > 0 And (username= lisi or wangba)
-
排序
-
db.list2.find().sort({"username":-1},{"user_id":1}) -- 1是升序 -- -1是降序
分页
-
limit()+skip()
-
db.list2.find().limit(2).skip(2) -- limit:pageSize -- skip:(currentPage-1)*pageSize
聚合查询
-
db.collectionName.aggregate( [ {管道:{表达式}} ...... ] )
-
常用管道
-
$group:分组->将数据进行分组 $match:过滤->只输出符合条件的数据 $sort:排序->对指定数据进行排序 $limit:分页->显示指定数量的数据 $skip:跳过->跳过指定数量的数据
-
-
常用表达式
-
$sum->总和 $sum:1 同 count 表示统计 $avg->平均值 $max->最大值 $min->最小值 ...
-
分组统计
-
db.c1.aggregate([ { $group: { _id: "$sex", -- 根据 关键字 分组 count: { $sum: 1 -- 求每组总数 }, age_count: { $sum: "$age" -- 求age 之和 } } } ])
-
-
求总数和平均年龄
-
db.c1.aggregate([ { $group: { _id: null, -- 不指定分组->不分组 count: { $sum: 1 -- 求总数 }, age_ave: { $avg: "$age" -- 计算平均值 } } } ])
-
-
求男生女生人数,并排序
-
db.c1.aggregate([ { $group: { _id: "$sex", count: { $sum: 1 } } }, { $sort: { count: 1 } } ])
-
-
-
MongoDB的索引
-
创建简单索引
-
db.c2.createIndex({"sex":1,"age":-1})
-
-
默认索引:_id(唯一索引),自动创建
-
单键索引:不会自动创建。
-
多键索引:值具有多个记录,比如数组。
SpringBoot整合MongoDB
mongodb-driver(了解)
mongo官方推出的java链接mongoDB的驱动包,等同于JDBC驱动。
官方驱动说明和下载:http://mongodb.github.io/mongo-java-driver/
官方驱动示例文档:http://mongodb.github.io/mongo-java-driver/3.8/driver/getting-started/quick-start/
SpringDataMongoDB
SpringData之一,用于操作MongoDB的持久层框架,底层对mongodb-driver进行了封装。
-
依赖导入
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
-
-
配置文件
-
spring: data: mongodb: # ip地址 host: localhost # 数据库名称 database: user_test # 端口号 port: 27017 #使用uri链接 #uri: mongodb://127.0.0.1:27017/user_test
-
-
实体类
-
//指定集合名 @Document(collection = "c2") //指定复合索引 @CompoundIndex(def = "{'age':1,'sex':-1}") public class StudyMongoDBUser { @Id//指定_id字段 若变量名为id可以去除 private String id; @Indexed(direction = IndexDirection.DESCENDING/*降序*/)//建立索引 private String username; private String password; private Integer age; private Integer sex; @Field("create_time")//字段名称映射 private Date createTime; @DBRef//设置子集合关联 private List<StudyMongoDBStudent> studentList; @Transient//忽略字段,当作javaBean字段使用 private List<Integer> nums; }
-
-
注解说明
-
@Id:用于标记id字段,没有标记此字段的实体也会自动生成id字段,但是我们无法通过实体来获取id。id建议使用ObjectId类型来创建
-
通过时间戳+机器标识+进程ID+自增计数器(确保同一秒内产生的Id不会冲突)构成。
-
-
@Document:用于标记此实体类是mongodb集合映射类。可以使用collection参数指定集合名称。特别需要注意的是如果实体类没有为任何字段创建索引将不会自动创建集合。
-
@Indexed:用于标记为某一字段创建索引。direction参数可以指定排序方向,升或降序。默认升序(ASC)
-
@CompoundIndex:用于创建复合索引。def参数可以定义复合索引的字段及排序方向。
-
@Transient:被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性。
-
@PersistenceConstructor:用于声明构造函数,作用是把从数据库取出的数据实例化为对象。
-
@Field:用于指定某一个字段映射到数据库中的名称。字段名不同的相互映射
-
@DBRef:用于指定与其他集合的级联关系,但是需要注意的是并不会自动创建级联集合。
-
使用注解存储的是集合名+id。但是不会自动保存子集数据,需要手动存储
-
-
SpringDataMongoDB方法使用
操作文档:http://www.mydlq.club/article/85/
创建集合
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoDBCreateCollectionTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 创建集合
* 默认创建,不限制集合大小
*/
@Test
public void createCollection(){
MongoCollection<Document> user_list = mongoTemplate.createCollection("user_list");
//判断集合是否存在
boolean flag = mongoTemplate.collectionExists("user_list");
System.out.println(user_list);
System.out.println(flag);
}
/**
*创建固定大小的集合
*/
@Test
public void createCollectionFixedSize(){
CollectionOptions collectionOptions = CollectionOptions.empty()
//创建固定大小集合,超过范围会自动覆盖最早的文档
.capped()
//指定数据最大值(KB)
.size(1024L)
//指定最大文档数量
.maxDocuments(5L);
MongoCollection<Document> user_list_2 = mongoTemplate.createCollection("user_list_2", collectionOptions);
boolean flag = mongoTemplate.collectionExists("user_list_2");
System.out.println(flag);
}
/**
* 创建【验证文档数据】的集合
*
* 创建集合并在文档"插入"与"更新"时进行数据效验,
* 如果符合创建集合设置的条件就进允许更新与插入,否则则按照设置的设置的策略进行处理。
*
* * 效验级别:
* - off:关闭数据校验。
* - strict:(默认值) 对所有的文档"插入"与"更新"操作有效。
* - moderate:仅对"插入"和满足校验规则的"文档"做"更新"操作有效。对已存在的不符合校验规则的"文档"无效。
* * 执行策略:
* - error:(默认值) 文档必须满足校验规则,才能被写入。
* - warn:对于"文档"不符合校验规则的 MongoDB 允许写入,
* 但会记录一条告警到 mongod.log 中去。
* 日志内容记录报错信息以及该"文档"的完整记录。
*
*/
@Test
public void createCollectionValidation(){
//编写校验权限,年龄大于20的信息
CriteriaDefinition criteria = Criteria.where("age").gt(20);
//设置集合的选择验证对象
CollectionOptions collectionOptions = CollectionOptions
//创建新的空的CollectionOptions
.empty()
//设置校验权限
.validator(Validator.criteria(criteria))
//设置校验级别
.strictValidation()
//设置校验不通过的执行操作
.failOnValidationError();
//执行创建
mongoTemplate.createCollection("user_list_3",collectionOptions);
}
}
查询集合
@RunWith(SpringRunner.class)
@SpringBootTest
public class QueryCollectionTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 获取【集合名称】列表
*/
@Test
public void getCollectionNameList(){
//获取集合名称列表
Set<String> collectionNames = mongoTemplate.getCollectionNames();
collectionNames.forEach(System.out::println);
}
/**
*检测集合是否 存在
* 根据【集合名】
*/
@Test
public void collectionExist(){
//根据document
boolean flag1 = mongoTemplate.collectionExists(StudyMongoDBStudent.class);
//根据CollectionName
boolean flag2 = mongoTemplate.collectionExists("user_list");
//混合
boolean flag3 = mongoTemplate.collectionExists(mongoTemplate.getCollectionName(StudyMongoDBStudent.class));
System.out.println(flag1);
System.out.println(flag2);
System.out.println(flag3);
}
}
删除集合
@RunWith(SpringRunner.class)
@SpringBootTest
public class DropCollectionTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 删除集合
*/
@Test
public void DeleteCollection(){
//直接删除集合
mongoTemplate.dropCollection("user_list");
System.out.println(mongoTemplate.collectionExists("user_list"));
//获取集合调用drop方法
mongoTemplate.getCollection("user_list_2").drop();
System.out.println(mongoTemplate.collectionExists("user_list_2"));
}
}
文档插入
@RunWith(SpringRunner.class)
@SpringBootTest
public class InsertDocumentTest {
@Autowired
private MongoTemplate mongoTemplate;
User user = new User();
@Before
public void setUser() {
user.setUserId(1);
user.setName("zhangsan");
user.setUsername("admin");
user.setPassword("admin");
user.setAge(19);
user.setCreateTime(new Date());
user.setUserType(1);
user.setParentId(0);
}
/**
* 插入一条 文档信息
* <p>
* insert(T objectToSave)
* 将对象插入到要保存的对象实体类型的集合中。
* <p>
* insert(T objectToSave, String collectionName)
* 将对象插入到指定的集合中。
* <p>
* _id 如果相同则会报错
*/
@Test
public void insertDocument() {
//当数据,插入到指定的【集合】当中
User getUser = mongoTemplate.insert(user, "userList");
System.out.println(getUser);
}
/**
* 同时插入多条数据
*/
@Test
public void insertDocumentList() {
List<User> userList = new ArrayList<>();
userList.add(user);
userList.add(user);
userList.add(user);
mongoTemplate.insert(userList,"userList");
}
/**
* 文档存储
* 如果文档已经存在(_id存在)
* 则对文档进行【更新】
*/
@Test
public void saveDocument(){
mongoTemplate.save(user,"userList");
}
}
文档查询
@RunWith(SpringRunner.class)
@SpringBootTest
public class QueryDocumentTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 查询【全部】文档信息
*/
@Test
public void findAll(){
List<User> userList = mongoTemplate.findAll(User.class, "userList");
userList.forEach(System.out::println);
}
/**
* 根据【_id】查询
* _id自带默认索引
*/
@Test
public void findById(){
User user = mongoTemplate.findById("60b73e43b4f04011a925ee0b", User.class, "userList");
System.out.println(user);
}
/**
* 根据【条件】查询,只取【第一条数据】
*/
@Test
public void findOne(){
//创建条件对象
Criteria criteria = Criteria.where("name").is("zhangsan");
//创建查询对象
Query query = new Query(criteria);
//执行查询,如果有多条,取第一条
User user = mongoTemplate.findOne(query, User.class, "userList");
System.out.println(user);
}
/**
* 根据【条件】查询文档,获取满足条件的文档列表
* 并且【排序】
*/
@Test
public void findByCondition(){
//创建条件对象
Criteria criteria = Criteria.where("name").is("zhangsan");
//创建查询对象
Query query = new Query(criteria)
//进行排序设置
.with(Sort.by("userId").descending());
//执行查询
List<User> userList = mongoTemplate.find(query, User.class, "userList");
userList.forEach(System.out::println);
}
/**
*根据【条件】 查询文档
* 并且进行【排序】
* 并【限制】查询记录数
* 和【跳过】指定记录数
* 达到【分页】效果
*/
@Test
public void findPage(){
Integer currentPage = 3;
Integer pageSize = 10;
//创建条件对象
Criteria criteria = Criteria.where("name").is("zhangsan");
//创建查询对象
Query query = new Query(criteria)
//进行排序操作,并且指定降序
.with(Sort.by("user_id").descending())
//指定 显示的数据量
.limit(pageSize)
//指定 跳过的数据量
.skip((currentPage-1)*pageSize);
//执行查询
List<User> userList = mongoTemplate.find(query, User.class, "userList");
for (User user : userList) {
System.out.println(user);
}
}
/**
* 查询【存在】指定字段的文档信息
*/
@Test
public void findExist(){
//创建条件对象
Criteria criteria = Criteria.where("name")
//判断字段是否存在
.exists(true);
//创建查询对象
Query query = new Query(criteria);
//执行查询
List<User> userList = mongoTemplate.find(query, User.class, "userList");
userList.forEach(System.out::println);
}
/**
* 根据【AND】关键字进行多个条件查询
*/
@Test
public void findAnd(){
// //创建条件对象
// Criteria criteria = Criteria.where("user_id").is(1)
// //使用and关键词
// .and("username").is("root");
//创建 UserId 条件对象
Criteria criteriaUserId = Criteria.where("user_id").is(1);
//创建 Username 条件对象
Criteria criteriaUsername = Criteria.where("username").is("root");
//使用and 链接
Criteria criteria = new Criteria().andOperator(criteriaUserId,criteriaUsername);
//创建查询对象
Query query = new Query(criteria);
//执行查询
List<User> userList = mongoTemplate.find(query, User.class, "userList");
userList.forEach(System.out::println);
}
}
文档更新
@RunWith(SpringRunner.class)
@SpringBootTest
public class UpdateDocumentTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 更新集合中符合【条件】的[第一条]文档
* 如果没有文档则【创建】一条新的文档
*/
@Test
public void update(){
//创建条件对象
Criteria criteria = Criteria.where("user_id").gt(0)
.and("parent_id").is(12)
.and("age").is(18);
//创建查询对象
Query query = new Query(criteria);
//创建更新对象,并设置更新内容
Update update = new Update()
//设置新值 K_V
.set("name","sdqwe")
.set("password","root");
//执行修改,若不存在文档则会插入一条新的 文档
UpdateResult result = mongoTemplate.upsert(query, update, User.class, "userList");
System.out.println(result);
System.out.println("查询匹配了:"+result.getMatchedCount()+"条数据,"
+(result.getModifiedCount() == 0 ? "未":"并")+
"进行修改");
}
/**
* 更新【匹配】到的文档集合中的,[第一条]数据
*/
@Test
public void updateFirst(){
//创建条件对象
Criteria criteria = Criteria.where("user_id").is(1);
//创建查询对象
Query query = new Query(criteria);
//创建更新对象,并设置更新内容
Update update = new Update()
//设置新值 K_V
.set("name","sdqwe")
.set("password","root");
//执行更新
UpdateResult result = mongoTemplate.updateFirst(query, update, User.class, "userList");
System.out.println("查询匹配了:"+result.getMatchedCount()+"条数据,"
+(result.getModifiedCount() == 0 ? "未":"并")+
"进行修改");
}
/**
* 更新【匹配】到的文档集合中的[所有]文档数据
*/
@Test
public void updateMany(){
//创建条件对象
Criteria criteria = Criteria.where("user_id").is(1);
//创建查询对象
Query query = new Query(criteria);
//创建更新对象,并设置更新内容
Update update = new Update()
//设置新值 K_V
.set("name","sdqwe")
.set("password","root");
//执行更新
UpdateResult result = mongoTemplate.updateMulti(query, update, User.class, "userList");
System.out.println("查询匹配了:"+result.getMatchedCount()+"条数据,"
+(result.getModifiedCount() == 0 ? "未":"并")+
"进行修改");
}
}
-
当使用upsert和updataFirst时,在查询时默认只会查询到第一条
-
导致result.getMatchedCount()是结果一直为0或者1
文档删除
@RunWith(SpringRunner.class)
@SpringBootTest
public class DropDocumentTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 删除【匹配】的[所有]文档数据
*/
@Test
public void removeMany(){
//创建条件对象
Criteria criteria = Criteria.where("user_id").gt(80000);
//创建查询条件
Query query = new Query(criteria);
//执行删除所有满足条件的文档
DeleteResult result = mongoTemplate.remove(query, "userList");
System.out.println("共删除了:"+result.getDeletedCount()+"条文档数据");
}
/**
* 删除查询【匹配】的[第一条]文档信息
* 并返回[被删除的文档]
*/
@Test
public void findAndRemove(){
//创建条件对象
Criteria criteria = Criteria.where("user_id").gt(70000);
//创建查询条件
Query query = new Query(criteria)
//根据user_id 进行降序排序
.with(Sort.by("user_id").descending());
//执行删除所有满足条件的文档
User user = mongoTemplate.findAndRemove(query, User.class, "userList");
System.out.println("被删除的文档数据:"+user);
}
/**
* 删除查询【匹配】的[全部]文档信息
* 并返回[被删除的文档集合]
* [必须有 【@id】 来接收_id]
*/
@Test
public void findAllAndRemove(){
//创建条件对象
Criteria criteria = Criteria.where("user_id").gt(70000);
//创建查询条件
Query query = new Query(criteria)
//根据user_id 进行降序排序
.with(Sort.by("user_id").descending());
//执行删除所有满足条件的文档
List<User> userList = mongoTemplate.findAllAndRemove(query, User.class, "userList");
userList.forEach(user->
System.out.println("被删除的文档数据:"+user));
}
}
-
删除全部文档并接受文档集合的方法
-
findAllAndRemove()在我们的entity当中
-
必须有@Id注解的变量来接受_id
聚合表达式操作
/**
* 聚合表达式
* 【$group】
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AggregateDocumentTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 使用【$group】和【$count】管道
* 进行聚合统计
*/
@Test
public void aggregateGroupAndCount(){
//使用 group 管道符进行聚合,并且使用count进行统计
AggregationOperation aga = Aggregation
//根据字段进行分组
.group("age")
//进行统计
.count()
//对统计字段进行别名设置
.as("numCount");
//创建聚合对象,并将操作注入
Aggregation aggregation = Aggregation.newAggregation(aga);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
//result的key为String 为_id(age) value为Integer
for (Map result : results) {
System.out.println(result);
}
}
/**
* 使用【$group】管道
* 和【$max】
* 【$min】
* 【$sum】
* 【$avg】
* 表达式
* 进行聚合最大值
*/
@Test
public void aggregationGroupAnd(){
//使用 group 管道符进行聚合,并且使用max和min指定字段最大最小值
AggregationOperation aga = Aggregation
//根据user_id进行分组
.group("parent_id")
//获取age最大值,并且取别名
.max("age").as("age_max")
//获取age最小值,并且取别名
.min("age").as("age_min")
//获取age和值,并且取别名
.sum("age").as("age_sum")
//获取age平均值,并且取别名
.avg("age").as("age_avg");
//创建聚合对象,并且添加操作
Aggregation aggregation = Aggregation.newAggregation(aga);
//执行聚合操作
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
for (Map result : results) {
System.out.println(result);
}
}
/**
*使用管道操作符 【$group】 结合
* 表达式操作符 【$first】 获取每个组的
* [包含]某字段的文档的[第一条]数据
* 【$last】 获取每个组的
* [包含]某字段的文档的[最后一条]数据
*/
@Test
public void aggregationGroupFirst(){
//先对数据进行排序,
//然后使用管道操作符 $group 进行分组,
//最后统计各个组文档某字段值第一个值
AggregationOperation sort = Aggregation
//根据user_id进行降序
.sort(Sort.by("user_id").descending());
AggregationOperation group = Aggregation
.group("parent_id")
//获取user_id 第一条
.first("user_id").as("user_id_first")
//获取age 第一条
.first("age").as("age_first")
//获取user_id 最后一条
.last("user_id").as("user_id_last");
//创建聚合对象
Aggregation aggregation = Aggregation.newAggregation(sort,group);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
for (Map result : results) {
System.out.println(result);
}
}
/**
* 使用管道操作符 【$group】 结合
* 表达式操作符 【$push】 获取某字段列表
*/
@Test
public void aggregationGroupPush(){
// 先对数据进行排序,
AggregationOperation sort = Aggregation
//根据user_id进行降序排序
.sort(Sort.by("user_id").descending());
// 然后使用管道操作符 $group 进行分组,
// 然后以数组形式列出某字段的全部值
AggregationOperation group = Aggregation
.group("username")
//设置获取字段,最后可以得到该字段的集合
.push("age").as("age_push");
//创建聚合对象
Aggregation aggregation = Aggregation.newAggregation(sort,group);
//执行聚合操作
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
for (Map result : results) {
//获取age字段的列表
System.out.println(result.get("age_push"));
//ArrayList
System.out.println(result.get("age_push").getClass().toString());
}
}
/**
* 通过聚合操作进行去重
* 1.根据去重数据分组拿到所有数据,和每组数据总数
* 2.根据去重的数据和其对应值当作查询条件
* 3.根据查询条件去进行删除,但是要跳过第一条数据
* 4.对删除数目与总数进行校验,差值是否为1
*/
@Test
public void aggregationDedup(){
AggregationOperation group = Aggregation
//根据需要去重的数据进行分组
.group("age","parent_id")
//获得数据总数
.count().as("count");
//创建聚合对象
Aggregation aggregation = Aggregation.newAggregation(group);
//执行聚合查询,并获得结果
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
//遍历结果集合
for (Map result : results) {
//获取count,分组数据总数
Integer count = (Integer) result.get("count");
//将总数进行 -1 操作,与下面删除数据结果比较
//因为会跳过第一条,达到去重效果所以-1
Long countL = count.longValue()-1;
//获取_id列表
LinkedHashMap<String,Integer> id = (LinkedHashMap) result.get("_id");
//创建条件对象
Criteria criteria = Criteria
//获取age和parent_id,来比较条件,找到重复数据
.where("age").is(id.get("age"))
.and("parent_id").is(id.get("parent_id"));
//创建查询对象,并跳过第一条,删除剩余所有,达到去重效果
Query query = new Query(criteria).skip(1);
//执行删除操作
DeleteResult deleteResult = mongoTemplate.remove(query, "userList");
//比较预期的删除数据数和总数-1是否相符合
System.out.println(countL.equals(deleteResult.getDeletedCount()));
}
}
}
聚合管道操作符
-
$project: 可以从文档中选择想要的字段,和不想要的字段(指定的字段可以是来自输入文档或新计算字段的现有字段 ,也可以通过管道表达式进行一些复杂的操作,例如数学操作,日期操作,字符串操作,逻辑操作。
-
$match: 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
-
$limit: 用来限制MongoDB聚合管道返回的文档数。
-
$skip: 在聚合管道中跳过指定数量的文档,并返回余下的文档。
-
$unwind: 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
-
$group: 将集合中的文档分组,可用于统计结果。
-
$sort: 将输入文档排序后输出。
/**
* 聚合的管道符操作
* * **$project:** 可以从文档中选择想要的字段,和不想要的字段(指定的字段可以是来自输入文档或新计算字段的现有字段 ,也可以通过管道表达式进行一些复杂的操作,例如数学操作,日期操作,字符串操作,逻辑操作。
* * **$match:** 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
* * **$limit:** 用来限制MongoDB聚合管道返回的文档数。
* * **$skip:** 在聚合管道中跳过指定数量的文档,并返回余下的文档。
* * **$unwind:** 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
* * **$group:** 将集合中的文档分组,可用于统计结果。
* * **$sort:** 将输入文档排序后输出。
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AggregateDocumentTest2 {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 使用 【$group】 和 【$match】 聚合,
* 先使用 【$match】 过滤文档,
* 然后再使用 【$group】 进行分组
* 最后根据【$sort】排序
*/
@Test
public void aggregationGroupMatch() {
AggregationOperation match = Aggregation
//使用match进行过滤
.match(//创建条件对象
Criteria
//age大于18
.where("age").gt(18)
);
AggregationOperation group = Aggregation
//根据账户名分组
.group("username")
//获取年龄最大值
.max("age").as("age_max")
//获取年龄最小值
.min("age").as("age_min");
AggregationOperation sort = Aggregation
//sort排序,根据group的结果去设置字段
.sort(Sort.by("age_max").descending());
//创建聚合对象
//如果先分组,这match找不到age字段
//match会根据group的结果去做过滤
// Aggregation aggregation = Aggregation.newAggregation(group,match);
Aggregation aggregation = Aggregation.newAggregation(match, group, sort);
//执行聚合操作
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
for (Map result : results) {
System.out.println(result);
}
}
/**
* 使用【$group】进行分组操作
* 在使用【$limit】显示数据限制
* 和【$skip】跳过指定数目文档
* 达到分页效果
*/
@Test
public void aggregatePage() {
AggregationOperation group = Aggregation
//根据_id分组
.group("_id")
//获取最后一条user_id
.last("user_id").as("user_id")
//获取最后一条parent_id
.last("parent_id").as("parent_id")
//获取最后一条age
.last("age").as("age");
Long currentPage = 2L;
Long pageSize = 2L;
AggregationOperation skip = Aggregation
//设置跳过数据量
.skip((currentPage - 1) * pageSize);
AggregationOperation limit = Aggregation
//设置每页显示数据量
.limit(pageSize);
//创建聚合对象 先skip在limit
Aggregation aggregation = Aggregation.newAggregation(group, skip, limit);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
for (Map result : results) {
System.out.println(result);
}
}
/**
* 使用 $group 和 $project 聚合,
* 先使用 【$group】 进行分组,
* 然后再使用 【$project】 限制显示的字段
*/
@Test
public void aggregateGroupProject() {
AggregationOperation group = Aggregation
//根据_id分组
.group("_id")
//统计每组数据量
.count().as("count")
//获取最后一条user_id
.last("user_id").as("user_id")
//获取最后一条parent_id
.last("parent_id").as("parent_id")
//获取最后一条age
.last("age").as("age");
AggregationOperation project = Aggregation
//设置 所需要显示的字段
.project("count","user_id","age");
//创建聚合对象
Aggregation aggregation = Aggregation.newAggregation(group, project);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "userList", Map.class);
for (Map result : results) {
System.out.println(result);
}
}
/**
* 使用 $group 和 $unwind 聚合,先使用 $project 进行分组,然后再使用 $unwind 拆分文档中的数组为一条新文档记录
*
* @return 聚合结果
*/
public void aggregateProjectUnwind() {
// 设置聚合条件,设置显示`name`、`age`、`title`字段,然后将结果中的多条文档按 title 字段进行拆分
AggregationOperation project = Aggregation.project("name", "age", "title");
AggregationOperation unwind = Aggregation.unwind("title");
// 将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(project, unwind);
// 执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "collectionName", Map.class);
for (Map result : results) {
System.out.println(result);
}
}
}